From 74ded8f0f96ede953c376047735922d005381730 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Wed, 11 Oct 2017 16:44:10 -0600 Subject: [PATCH 01/43] Added changes for faster cape and twb routines done by @supreethms1809 --- fortran/rip_cape.f90 | 710 ++++++++++++++++++++++-------- fortran/wrf_rip_phys_routines.f90 | 64 ++- fortran/wrffortran.pyf | 31 +- src/wrf/extension.py | 12 +- 4 files changed, 620 insertions(+), 197 deletions(-) diff --git a/fortran/rip_cape.f90 b/fortran/rip_cape.f90 index 08cf9a0..945b98e 100644 --- a/fortran/rip_cape.f90 +++ b/fortran/rip_cape.f90 @@ -37,7 +37,8 @@ END FUNCTION TVIRTUAL REAL(KIND=8) FUNCTION TONPSADIABAT(thte, prs, psadithte, psadiprs, psaditmk, gamma,& errstat, errmsg) USE wrf_constants, ONLY : ALGERR - +!$OMP DECLARE SIMD (TONPSADIABAT) +!!uniform(thte,prs,psadithte,psadiprs,psaditmk) !f2py threadsafe !f2py intent(in,out) :: cape, cin @@ -57,8 +58,9 @@ REAL(KIND=8) FUNCTION TONPSADIABAT(thte, prs, psadithte, psadiprs, psaditmk, gam REAL(KIND=8) :: fracjt2 REAL(KIND=8) :: fracip REAL(KIND=8) :: fracip2 - - INTEGER :: ip, ipch, jt, jtch + + INTEGER :: l1, h1, mid1, rang1, l2, h2, mid2, rang2 + INTEGER :: ip, jt ! This function gives the temperature (in K) on a moist adiabat ! (specified by thte in K) given pressure in hPa. It uses a @@ -78,22 +80,53 @@ REAL(KIND=8) FUNCTION TONPSADIABAT(thte, prs, psadithte, psadiprs, psaditmk, gam ! Otherwise, look for the given thte/prs point in the lookup table. jt = -1 - DO jtch = 1, 150-1 - IF (thte .GE. psadithte(jtch) .AND. thte .LT. psadithte(jtch+1)) THEN - jt = jtch - EXIT - !GO TO 213 - END IF + l1 = 1 + h1 = 149 + rang1 = h1 - l1 + mid1 = (h1 + l1) / 2 + DO WHILE(rang1 .GT. 1) + if(thte .GE. psadithte(mid1)) then + l1 = mid1 + else + h1 = mid1 + end if + rang1 = h1 - l1 + mid1 = (h1 + l1) / 2 END DO - - ip = -1 - DO ipch = 1, 150-1 - IF (prs .LE. psadiprs(ipch) .AND. prs .GT. psadiprs(ipch+1)) THEN - ip = ipch - EXIT - !GO TO 215 - END IF + jt = l1 + + ! DO jtch = 1, 150-1 + ! IF (thte .GE. psadithte(jtch) .AND. thte .LT. psadithte(jtch+1)) THEN + ! jt = jtch + ! EXIT + ! !GO TO 213 + ! END IF + ! END DO + + ip = -1 + l2 = 1 + h2 = 149 + rang2 = h2 - l2 + mid2 = (h2 + l2) / 2 + DO WHILE(rang2 .GT. 1) + if(prs .LE. psadiprs(mid2)) then + l2 = mid2 + else + h2 = mid2 + end if + rang2 = h2 - l2 + mid2 = (h2 + l2) / 2 END DO + ip = l2 + + ! ip = -1 + ! DO ipch = 1, 150-1 + ! IF (prs .LE. psadiprs(ipch) .AND. prs .GT. psadiprs(ipch+1)) THEN + ! ip = ipch + ! EXIT + ! !GO TO 215 + ! END IF + ! END DO IF (jt .EQ. -1 .OR. ip .EQ. -1) THEN ! Set the error and return @@ -187,28 +220,26 @@ END SUBROUTINE DLOOKUP_TABLE ! level is above. SUBROUTINE DPFCALC(prs, sfp, pf, miy, mjx, mkzh, ter_follow) - REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(IN) :: prs + REAL(KIND=8), DIMENSION(mkzh,miy,mjx), INTENT(IN) :: prs REAL(KIND=8), DIMENSION(miy,mjx), INTENT(IN) :: sfp - REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(OUT) :: pf + REAL(KIND=8), DIMENSION(mkzh,miy,mjx), INTENT(OUT) :: pf INTEGER, INTENT(IN) :: ter_follow,miy,mjx,mkzh INTEGER :: i,j,k - - ! do j=1,mjx-1 Artifact of MM5 + DO j = 1,mjx - ! do i=1,miy-1 staggered grid DO i = 1,miy DO k = 1,mkzh IF (k .EQ. mkzh) THEN ! terrain-following data IF (ter_follow .EQ. 1) THEN - pf(i,j,k) = sfp(i,j) + pf(k,i,j) = sfp(i,j) ! pressure-level data ELSE - pf(i,j,k) = .5D0 * (3.D0*prs(i,j,k) - prs(i,j,k-1)) + pf(k,i,j) = .5D0 * (3.D0*prs(k,i,j) - prs(k-1,i,j)) END IF ELSE - pf(i,j,k) = .5D0 * (prs(i,j,k+1) + prs(i,j,k)) + pf(k,i,j) = .5D0 * (prs(k+1,i,j) + prs(k,i,j)) END IF END DO END DO @@ -224,17 +255,338 @@ END SUBROUTINE DPFCALC ! ! !DESCRIPTION: ! -! If i3dflag=1, this routine calculates CAPE and CIN (in m**2/s**2, +! This routine calculates CAPE and CIN (in m**2/s**2, ! or J/kg) for every grid point in the entire 3D domain (treating -! each grid point as a parcel). If i3dflag=0, then it -! calculates CAPE and CIN only for the parcel with max theta-e in +! each grid point as a parcel). +! + + +! Important! The z-indexes must be arranged so that mkzh (max z-index) is the +! surface pressure. So, pressure must be ordered in ascending order before +! calling this routine. Other variables must be ordered the same (p,tk,q,z). + +! Also, be advised that missing data values are not checked during the computation. +! Also also, Pressure must be hPa + +! NCLFORTSTART +SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& + cmsg,miy,mjx,mkzh,ter_follow,& + psafile, errstat, errmsg) + USE wrf_constants, ONLY : ALGERR, CELKEL, G, EZERO, ESLCON1, ESLCON2, & + EPS, RD, CP, GAMMA, CPMD, RGASMD, GAMMAMD, TLCLC1, & + TLCLC2, TLCLC3, TLCLC4, THTECON1, THTECON2, THTECON3 + + !USE omp_lib + IMPLICIT NONE + + !f2py threadsafe + !f2py intent(in,out) :: cape, cin + + INTEGER, INTENT(IN) :: miy, mjx, mkzh, ter_follow + REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(IN) :: prs + REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(IN) :: tmk + REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(IN) :: qvp + REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(IN) :: ght + REAL(KIND=8), DIMENSION(miy,mjx), INTENT(IN) :: ter + REAL(KIND=8), DIMENSION(miy,mjx), INTENT(IN) ::sfp + REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(OUT) :: cape + REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(OUT) :: cin + REAL(KIND=8), INTENT(IN) :: cmsg + CHARACTER(LEN=*), INTENT(IN) :: psafile + INTEGER, INTENT(INOUT) :: errstat + CHARACTER(LEN=*), INTENT(INOUT) :: errmsg + +! NCLFORTEND + + ! local variables + INTEGER :: i, j, k, ilcl, kel, kk, klcl, klev, klfc, kmax, kpar + REAL(KIND=8) :: tlcl, zlcl + REAL(KIND=8) :: ethpari, qvppari, tmkpari + REAL(KIND=8) :: facden, qvplift, tmklift, tvenv, tvlift, ghtlift + REAL(KIND=8) :: eslift, tmkenv, qvpenv, tonpsadiabat + REAL(KIND=8) :: benamin, dz + REAL(KIND=8), DIMENSION(150) :: buoy, zrel, benaccum + REAL(KIND=8), DIMENSION(mkzh,miy,mjx) :: prsf + REAL(KIND=8), DIMENSION(150) :: psadithte, psadiprs + REAL(KIND=8), DIMENSION(150,150) :: psaditmk + LOGICAL :: elfound + REAL :: t1,t2 + + REAL(KIND=8), DIMENSION(mkzh,miy,mjx) :: prs_new + REAL(KIND=8), DIMENSION(mkzh,miy,mjx) :: tmk_new + REAL(KIND=8), DIMENSION(mkzh,miy,mjx) :: qvp_new + REAL(KIND=8), DIMENSION(mkzh,miy,mjx) :: ght_new + + ! To remove compiler warnings + tmkpari = 0 + qvppari = 0 + klev = 0 + klcl = 0 + kel = 0 + + + ! the comments were taken from a mark stoelinga email, 23 apr 2007, + ! in response to a user getting the "outside of lookup table bounds" + ! error message. + + ! tmkpari - initial temperature of parcel, k + ! values of 300 okay. (not sure how much from this you can stray.) + + ! prspari - initial pressure of parcel, hpa + ! values of 980 okay. (not sure how much from this you can stray.) + + ! thtecon1, thtecon2, thtecon3 + ! these are all constants, the first in k and the other two have + ! no units. values of 3376, 2.54, and 0.81 were stated as being + ! okay. + + ! tlcl - the temperature at the parcel's lifted condensation level, k + ! should be a reasonable atmospheric temperature around 250-300 k + ! (398 is "way too high") + + ! qvppari - the initial water vapor mixing ratio of the parcel, + ! kg/kg (should range from 0.000 to 0.025) + ! + + ! calculated the pressure at full sigma levels (a set of pressure + ! levels that bound the layers represented by the vertical grid points) + + !CALL cpu_time(t1) + !CALL OMP_SET_NUM_THREADS(16) +!$OMP PARALLEL DO + DO i = 1,mjx + DO j = 1,miy + DO k = 1,mkzh + prs_new(k,j,i) = prs(j,i,k) + tmk_new(k,j,i) = tmk(j,i,k) + qvp_new(k,j,i) = qvp(j,i,k) + ght_new(k,j,i) = ght(j,i,k) + END DO + END DO + END DO +!$OMP END PARALLEL DO + + CALL DPFCALC(prs_new, sfp, prsf, miy, mjx, mkzh, ter_follow) + + ! before looping, set lookup table for getting temperature on + ! a pseudoadiabat. + + CALL DLOOKUP_TABLE(psadithte, psadiprs, psaditmk, psafile, errstat, errmsg) + + IF (errstat .NE. 0) THEN + RETURN + END IF + +!$OMP PARALLEL DO COLLAPSE(2) PRIVATE(tlcl, ethpari, & +!$OMP zlcl, kk, ilcl, klcl, tmklift, tvenv, tvlift, ghtlift, & +!$OMP facden, tmkenv, qvpenv, eslift, qvplift, buoy, benamin, & +!$OMP benaccum, zrel, kmax, dz, elfound, & +!$OMP kel, klfc, & +!$OMP i,j,k,kpar) + DO j = 1,mjx + DO i = 1,miy + cape(i,j,1) = 0.d0 + cin(i,j,1) = 0.d0 + +!$OMP SIMD + DO kpar = 2, mkzh + + ! Calculate temperature and moisture properties of parcel + ! (note, qvppari and tmkpari already calculated above for 2d case.) + + tlcl = TLCLC1/(LOG(tmk_new(kpar,i,j)**TLCLC2/(MAX(1.D-20,qvp_new(kpar,i,j)*prs_new(kpar,i,j)/ & + (EPS + qvp_new(kpar,i,j))))) - TLCLC3) + TLCLC4 + + ethpari = tmk_new(kpar,i,j)*(1000.D0/prs_new(kpar,i,j))**(GAMMA*(1.D0 + GAMMAMD*qvp_new(kpar,i,j)))* & + EXP((THTECON1/tlcl - THTECON2)*qvp_new(kpar,i,j)*(1.D0 + THTECON3*qvp_new(kpar,i,j))) + + zlcl = ght_new(kpar,i,j) + (tmk_new(kpar,i,j) - tlcl)/(G/CP * (1.D0 + CPMD*qvp_new(kpar,i,j))) + + ! DO k = kpar,1,-1 + ! tmklift_new(k) = TONPSADIABAT(ethpari, prs_new(k,i,j), psadithte, psadiprs,& + ! psaditmk, GAMMA, errstat, errmsg) + ! END DO + ! Calculate buoyancy and relative height of lifted parcel at + ! all levels, and store in bottom up arrays. add a level at the lcl, + ! and at all points where buoyancy is zero. + ! + ! For arrays that go bottom to top + kk = 0 + ilcl = 0 + + IF (ght_new(kpar,i,j) .GE. zlcl) THEN + ! Initial parcel already saturated or supersaturated. + ilcl = 2 + klcl = 1 + END IF + +!$OMP SIMD lastprivate(qvplift,tmklift,ghtlift,tvlift,tmkenv,qvpenv,tvenv,eslift,facden) + DO k = kpar,1,-1 + ! For arrays that go bottom to top + kk = kk + 1 + + ! Model level is below lcl + IF (ght_new(k,i,j) .LT. zlcl) THEN + tmklift = tmk_new(kpar,i,j) - G/(CP * (1.D0 + CPMD*qvp_new(kpar,i,j))) * (ght_new(k,i,j) - ght_new(kpar,i,j)) + tvenv = tmk_new(k,i,j)*(EPS + qvp_new(k,i,j))/(EPS*(1.D0 + qvp_new(k,i,j))) + tvlift = tmklift*(EPS + qvp_new(kpar,i,j))/(EPS*(1.D0 + qvp_new(kpar,i,j))) + ghtlift = ght_new(k,i,j) + ELSE IF (ght(i,j,k) .GE. zlcl .AND. ilcl .EQ. 0) THEN + ! This model level and previous model level straddle the lcl, + ! so first create a new level in the bottom-up array, at the lcl. + facden = 1.0/(ght_new(k,i,j) - ght_new(k+1,i,j)) + tmkenv = tmk_new(k+1,i,j)*((ght_new(k,i,j)-zlcl)*facden) + tmk_new(k,i,j)*((zlcl-ght_new(k+1,i,j))*facden) + qvpenv = qvp_new(k+1,i,j)*((ght_new(k,i,j)-zlcl)*facden) + qvp_new(k,i,j)*((zlcl-ght_new(k+1,i,j))*facden) + tvenv = tmkenv* (EPS + qvpenv) / (EPS * (1.D0 + qvpenv)) + tvlift = tlcl* (EPS + qvp_new(kpar,i,j)) / (EPS *(1.D0 + qvp_new(kpar,i,j))) + ghtlift = zlcl + ilcl = 1 + ELSE + tmklift = TONPSADIABAT(ethpari, prs_new(k,i,j), psadithte, psadiprs,& + psaditmk, GAMMA, errstat, errmsg) + eslift = EZERO*EXP(ESLCON1*(tmklift - CELKEL)/(tmklift - ESLCON2)) + qvplift = EPS*eslift/(prs_new(k,i,j) - eslift) + tvenv = tmk_new(k,i,j) * (EPS + qvp_new(k,i,j)) / (EPS * (1.D0 + qvp_new(k,i,j))) + tvlift = tmklift*(EPS + qvplift) / (EPS * (1.D0 + qvplift)) + ghtlift = ght_new(k,i,j) + END IF + ! Buoyancy + buoy(kk) = G*(tvlift - tvenv)/tvenv + zrel(kk) = ghtlift - ght_new(kpar,i,j) + IF ((kk .GT. 1) .AND. (buoy(kk)*buoy(kk-1) .LT. 0.0D0)) THEN + ! Parcel ascent curve crosses sounding curve, so create a new level + ! in the bottom-up array at the crossing. + kk = kk + 1 + buoy(kk) = buoy(kk-1) + zrel(kk) = zrel(kk-1) + buoy(kk-1) = 0.D0 + zrel(kk-1) = zrel(kk-2) + buoy(kk-2)/& + (buoy(kk-2) - buoy(kk))*(zrel(kk) - zrel(kk-2)) + END IF + IF (ilcl .EQ. 1) THEN + klcl = kk + ilcl = 2 + CYCLE + END IF + + END DO + + kmax = kk + ! IF (kmax .GT. 150) THEN + ! print *,'kmax got too big' + ! errstat = ALGERR + ! WRITE(errmsg, *) 'capecalc3d: kmax got too big. kmax=',kmax + ! RETURN + ! END IF + + ! If no lcl was found, set klcl to kmax. it is probably not really + ! at kmax, but this will make the rest of the routine behave + ! properly. + IF (ilcl .EQ. 0) klcl=kmax + + ! Get the accumulated buoyant energy from the parcel's starting + ! point, at all levels up to the top level. + benaccum(1) = 0.0D0 + benamin = 9d9 + DO k = 2,kmax + dz = zrel(k) - zrel(k-1) + benaccum(k) = benaccum(k-1) + .5D0*dz*(buoy(k-1) + buoy(k)) + IF (benaccum(k) .LT. benamin) THEN + benamin = benaccum(k) + END IF + END DO + ! Determine equilibrium level (el), which we define as the highest + ! level of non-negative buoyancy above the lcl. note, this may be + ! the top level if the parcel is still buoyant there. + + elfound = .FALSE. + DO k = kmax,klcl,-1 + IF (buoy(k) .GE. 0.D0) THEN + ! k of equilibrium level + kel = k + elfound = .TRUE. + EXIT + END IF + END DO + + ! If we got through that loop, then there is no non-negative + ! buoyancy above the lcl in the sounding. in these situations, + ! both cape and cin will be set to -0.1 j/kg. (see below about + ! missing values in v6.1.0). also, where cape is + ! non-zero, cape and cin will be set to a minimum of +0.1 j/kg, so + ! that the zero contour in either the cin or cape fields will + ! circumscribe regions of non-zero cape. + + ! In v6.1.0 of ncl, we added a _fillvalue attribute to the return + ! value of this function. at that time we decided to change -0.1 + ! to a more appropriate missing value, which is passed into this + ! routine as cmsg. + + IF (.NOT. elfound) THEN + !print *,'el not found' + cape(i,j,kpar) = cmsg + cin(i,j,kpar) = cmsg + klfc = kmax + CYCLE + END IF + + ! If there is an equilibrium level, then cape is positive. we'll + ! define the level of free convection (lfc) as the point below the + ! el, but at or above the lcl, where accumulated buoyant energy is a + ! minimum. the net positive area (accumulated buoyant energy) from + ! the lfc up to the el will be defined as the cape, and the net + ! negative area (negative of accumulated buoyant energy) from the + ! parcel starting point to the lfc will be defined as the convective + ! inhibition (cin). + + ! First get the lfc according to the above definition. + benamin = 9D9 + klfc = kmax + DO k = klcl,kel + IF (benaccum(k) .LT. benamin) THEN + benamin = benaccum(k) + klfc = k + END IF + END DO + + ! Now we can assign values to cape and cin + + cape(i,j,kpar) = MAX(benaccum(kel)-benamin, 0.1D0) + cin(i,j,kpar) = MAX(-benamin, 0.1D0) + + ! cin is uninteresting when cape is small (< 100 j/kg), so set + ! cin to -0.1 (see note about missing values in v6.1.0) in + ! that case. + + ! In v6.1.0 of ncl, we added a _fillvalue attribute to the return + ! value of this function. at that time we decided to change -0.1 + ! to a more appropriate missing value, which is passed into this + ! routine as cmsg. + + IF (cape(i,j,kpar) .LT. 100.D0) cin(i,j,kpar) = cmsg + + END DO + END DO + END DO +!$OMP END PARALLEL DO + !CALL cpu_time(t2) + !print *,'Time taken in seconds ',(t2-t1) + RETURN +END SUBROUTINE DCAPECALC3D + +!====================================================================== +! +! !IROUTINE: capecalc2d -- Calculate CAPE and CIN +! +! !DESCRIPTION: +! +! Calculates CAPE and CIN only for the parcel with max theta-e in ! the column, (i.e. something akin to Colman's MCAPE). By "parcel", ! we mean a 500-m deep parcel, with actual temperature and moisture ! averaged over that depth. ! -! In the case of i3dflag=0, ! CAPE and CIN are 2D fields that are placed in the k=mkzh slabs of -! the cape and cin arrays. Also, if i3dflag=0, LCL and LFC heights +! the cape and cin arrays. Also, LCL and LFC heights ! are put in the k=mkzh-1 and k=mkzh-2 slabs of the cin array. ! @@ -243,23 +595,25 @@ END SUBROUTINE DPFCALC ! surface pressure. So, pressure must be ordered in ascending order before ! calling this routine. Other variables must be ordered the same (p,tk,q,z). -! Also, be advised that missing data values are not checked during the computation. +! Also, be advised that missing data values are not checked during the +! computation. ! Also also, Pressure must be hPa ! NCLFORTSTART -SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& - cmsg,miy,mjx,mkzh,i3dflag,ter_follow,& +SUBROUTINE DCAPECALC2D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& + cmsg,miy,mjx,mkzh,ter_follow,& psafile, errstat, errmsg) USE wrf_constants, ONLY : ALGERR, CELKEL, G, EZERO, ESLCON1, ESLCON2, & EPS, RD, CP, GAMMA, CPMD, RGASMD, GAMMAMD, TLCLC1, & TLCLC2, TLCLC3, TLCLC4, THTECON1, THTECON2, THTECON3 + !USE omp_lib IMPLICIT NONE !f2py threadsafe !f2py intent(in,out) :: cape, cin - INTEGER, INTENT(IN) :: miy, mjx, mkzh, i3dflag, ter_follow + INTEGER, INTENT(IN) :: miy, mjx, mkzh, ter_follow REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(IN) :: prs REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(IN) :: tmk REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(IN) :: qvp @@ -275,26 +629,35 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& ! NCLFORTEND + ! local variables INTEGER :: i, j, k, ilcl, kel, kk, klcl, klev, klfc, kmax, kpar, kpar1, kpar2 - REAL(KIND=8) :: davg, ethmax, q, t, p, e, eth, tlcl, zlcl + REAL(KIND=8) :: ethmax, q, p, e, tlcl, zlcl REAL(KIND=8) :: pavg, tvirtual, p1, p2, pp1, pp2, th, totthe, totqvp, totprs - REAL(KIND=8) :: cpm, deltap, ethpari, gammam, ghtpari, qvppari, prspari, tmkpari - REAL(KIND=8) :: facden, fac1, fac2, qvplift, tmklift, tvenv, tvlift, ghtlift + REAL(KIND=8) :: cpm, deltap, ethpari, gammam, qvppari, tmkpari + REAL(KIND=8) :: facden, qvplift, tmklift, tvenv, tvlift, ghtlift, fac1, fac2 REAL(KIND=8) :: eslift, tmkenv, qvpenv, tonpsadiabat REAL(KIND=8) :: benamin, dz, pup, pdn REAL(KIND=8), DIMENSION(150) :: buoy, zrel, benaccum - REAL(KIND=8), DIMENSION(miy,mjx,mkzh) :: prsf + REAL(KIND=8), DIMENSION(mkzh,miy,mjx) :: prsf REAL(KIND=8), DIMENSION(150) :: psadithte, psadiprs REAL(KIND=8), DIMENSION(150,150) :: psaditmk LOGICAL :: elfound + INTEGER :: nthreads + REAL(KIND=8), DIMENSION(mkzh) :: eth_temp + REAL(KIND=8), DIMENSION(mkzh,miy,mjx) :: prs_new + REAL(KIND=8), DIMENSION(mkzh,miy,mjx) :: tmk_new + REAL(KIND=8), DIMENSION(mkzh,miy,mjx) :: qvp_new + REAL(KIND=8), DIMENSION(mkzh,miy,mjx) :: ght_new ! To remove compiler warnings + errstat = 0 tmkpari = 0 qvppari = 0 klev = 0 klcl = 0 kel = 0 + deltap = 0 ! the comments were taken from a mark stoelinga email, 23 apr 2007, @@ -320,10 +683,23 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& ! kg/kg (should range from 0.000 to 0.025) ! +!$OMP PARALLEL DO + DO i = 1,mjx + DO j = 1,miy + DO k = 1,mkzh + prs_new(k,j,i) = prs(j,i,k) + tmk_new(k,j,i) = tmk(j,i,k) + qvp_new(k,j,i) = qvp(j,i,k) + ght_new(k,j,i) = ght(j,i,k) + END DO + END DO + END DO +!$OMP END PARALLEL DO + + ! calculated the pressure at full sigma levels (a set of pressure ! levels that bound the layers represented by the vertical grid points) - - CALL DPFCALC(prs, sfp, prsf, miy, mjx, mkzh, ter_follow) + CALL DPFCALC(prs_new, sfp, prsf, miy, mjx, mkzh, ter_follow) ! before looping, set lookup table for getting temperature on ! a pseudoadiabat. @@ -334,148 +710,144 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& RETURN END IF - ! do j=1,mjx-1 + !CALL OMP_SET_NUM_THREADS(16) + !nthreads = omp_get_num_threads() + +!$OMP PARALLEL DO COLLAPSE(2) PRIVATE(tlcl, ethpari, & +!$OMP zlcl, kk, ilcl, klcl, tmklift, tvenv, tvlift, ghtlift, & +!$OMP facden, tmkenv, qvpenv, eslift, qvplift, buoy, benamin, & +!$OMP benaccum, zrel, kmax, dz, elfound, & +!$OMP kel, klfc, pavg, p2, p1, totthe, totqvp, totprs, & +!$OMP i,j,k,kpar, qvppari, tmkpari,p, pup, pdn, q, th, & +!$OMP pp1, pp2) DO j = 1,mjx - ! do i=1,miy-1 DO i = 1,miy cape(i,j,1) = 0.d0 cin(i,j,1) = 0.d0 + ! find parcel with max theta-e in lowest 3 km agl. + ethmax = -1.d0 + eth_temp = -1.d0 + DO k = 1, mkzh + IF (ght_new(k,i,j)-ter(i,j) .LT. 3000.d0) THEN + tlcl = TLCLC1 / (LOG(tmk_new(k,i,j)**TLCLC2/& + (MAX(qvp_new(k,i,j), 1.d-15)*prs_new(k,i,j)/(EPS+MAX(qvp_new(k,i,j), 1.d-15))))-TLCLC3)+TLCLC4 + eth_temp(k) = tmk_new(k,i,j) * (1000.d0/prs_new(k,i,j))**& + (GAMMA*(1.d0 + GAMMAMD*(MAX(qvp_new(k,i,j), 1.d-15))))*& + EXP((THTECON1/tlcl - THTECON2)*(MAX(qvp_new(k,i,j), 1.d-15))*& + (1.d0 + THTECON3*(MAX(qvp_new(k,i,j), 1.d-15)))) + END IF + END DO + klev = mkzh + DO k = 1,mkzh + IF (eth_temp(k) .GT. ethmax) THEN + klev = k + ethmax = eth_temp(k) + END IF + END DO - IF (i3dflag .EQ. 1) THEN - kpar1 = 2 - kpar2 = mkzh - ELSE - ! find parcel with max theta-e in lowest 3 km agl. - ethmax = -1.d0 - DO k = mkzh,1,-1 - IF (ght(i,j,k)-ter(i,j) .LT. 3000.d0) THEN - q = MAX(qvp(i,j,k), 1.d-15) - t = tmk(i,j,k) - p = prs(i,j,k) - e = q*p/(EPS + q) - tlcl = TLCLC1 / (LOG(t**TLCLC2/e)-TLCLC3) + TLCLC4 - eth = t * (1000.d0/p)**(GAMMA*(1.d0 + GAMMAMD*q))*& - EXP((THTECON1/tlcl - THTECON2)*q*(1.d0 + THTECON3*q)) - IF (eth .GT. ethmax) THEN - klev = k - ethmax = eth - END IF - END IF - END DO - kpar1 = klev - kpar2 = klev - - ! Establish average properties of that parcel - ! (over depth of approximately davg meters) - - ! davg=.1 - davg = 500.d0 - pavg = davg*prs(i,j,kpar1)*& - G/(RD*tvirtual(tmk(i,j,kpar1), qvp(i,j,kpar1))) - p2 = MIN(prs(i,j,kpar1)+.5d0*pavg, prsf(i,j,mkzh)) - p1 = p2 - pavg - totthe = 0.D0 - totqvp = 0.D0 - totprs = 0.D0 - DO k = mkzh,2,-1 - IF (prsf(i,j,k) .LE. p1) EXIT !GOTO 35 - IF (prsf(i,j,k-1) .GE. p2) CYCLE !GOTO 34 - p = prs(i,j,k) - pup = prsf(i,j,k) - pdn = prsf(i,j,k-1) - q = MAX(qvp(i,j,k),1.D-15) - th = tmk(i,j,k)*(1000.D0/p)**(GAMMA*(1.D0 + GAMMAMD*q)) - pp1 = MAX(p1,pdn) - pp2 = MIN(p2,pup) - IF (pp2 .GT. pp1) THEN - deltap = pp2 - pp1 - totqvp = totqvp + q*deltap - totthe = totthe + th*deltap - totprs = totprs + deltap - END IF -! 34 CONTINUE - END DO -! 35 CONTINUE - qvppari = totqvp/totprs - tmkpari = (totthe/totprs)*& - (prs(i,j,kpar1)/1000.D0)**(GAMMA*(1.D0+GAMMAMD*qvp(i,j,kpar1))) - END IF - + kpar1 = klev + kpar2 = klev + + + ! Establish average properties of that parcel + ! (over depth of approximately davg meters) + + !davg = 500.d0 + pavg = 500.d0 * prs_new(kpar1,i,j)*& + G/(RD*tvirtual(tmk_new(kpar1,i,j), qvp_new(kpar1,i,j))) + p2 = MIN(prs_new(kpar1,i,j)+.5d0*pavg, prsf(mkzh,i,j)) + p1 = p2 - pavg + totthe = 0.D0 + totqvp = 0.D0 + totprs = 0.D0 + DO k = mkzh,2,-1 + IF (prsf(k,i,j) .LE. p1) EXIT !GOTO 35 + IF (prsf(k-1,i,j) .GE. p2) CYCLE !GOTO 34 + p = prs_new(k,i,j) + pup = prsf(k,i,j) + pdn = prsf(k-1,i,j) + !q = MAX(qvp_new(k,i,j),1.D-15) + th = tmk_new(k,i,j)*(1000.D0/prs_new(k,i,j))**(GAMMA*(1.D0 + GAMMAMD*MAX(qvp_new(k,i,j),1.D-15))) + pp1 = MAX(p1,pdn) + pp2 = MIN(p2,pup) + IF (pp2 .GT. pp1) THEN + ! deltap = pp2 - pp1 + totqvp = totqvp + MAX(qvp_new(k,i,j),1.D-15)*(pp2 - pp1) + totthe = totthe + th*(pp2 - pp1) + totprs = totprs + (pp2 - pp1) + END IF + END DO + qvppari = totqvp/totprs + tmkpari = (totthe/totprs)*& + (prs_new(kpar1,i,j)/1000.D0)**(GAMMA*(1.D0+GAMMAMD*qvp_new(kpar1,i,j))) + +!CALL CPU_TIME(t3) DO kpar = kpar1, kpar2 ! Calculate temperature and moisture properties of parcel - ! (note, qvppari and tmkpari already calculated above for 2d case.) + ! (note, qvppari and tmkpari already calculated above for 2d + ! case.) - IF (i3dflag .EQ. 1) THEN - qvppari = qvp(i,j,kpar) - tmkpari = tmk(i,j,kpar) - END IF - prspari = prs(i,j,kpar) - ghtpari = ght(i,j,kpar) + !prspari = prs_new(kpar,i,j) + !ghtpari = ght_new(kpar,i,j) gammam = GAMMA * (1.D0 + GAMMAMD*qvppari) cpm = CP * (1.D0 + CPMD*qvppari) - e = MAX(1.D-20,qvppari*prspari/(EPS + qvppari)) + e = MAX(1.D-20,qvppari*prs_new(kpar,i,j)/(EPS + qvppari)) tlcl = TLCLC1/(LOG(tmkpari**TLCLC2/e) - TLCLC3) + TLCLC4 - ethpari = tmkpari*(1000.D0/prspari)**(GAMMA*(1.D0 + GAMMAMD*qvppari))*& + ethpari = tmkpari*(1000.D0/prs_new(kpar,i,j))**(GAMMA*(1.D0 + GAMMAMD*qvppari))*& EXP((THTECON1/tlcl - THTECON2)*qvppari*(1.D0 + THTECON3*qvppari)) - zlcl = ghtpari + (tmkpari - tlcl)/(G/cpm) + zlcl = ght_new(kpar,i,j) + (tmkpari - tlcl)/(G/cpm) ! Calculate buoyancy and relative height of lifted parcel at - ! all levels, and store in bottom up arrays. add a level at the lcl, + ! all levels, and store in bottom up arrays. add a level at the + ! lcl, ! and at all points where buoyancy is zero. ! + ! ! For arrays that go bottom to top kk = 0 ilcl = 0 - IF (ghtpari .GE. zlcl) THEN + IF (ght_new(kpar,i,j) .GE. zlcl) THEN ! Initial parcel already saturated or supersaturated. ilcl = 2 klcl = 1 END IF k = kpar - DO WHILE (k .GE. 1)!k = kpar, 1, -1 - !DO k = kpar, 1, -1 - ! For arrays that go bottom to top -! 33 kk = kk + 1 + DO k = kpar,1,-1 + ! For arrays that go bottom to top kk = kk + 1 ! Model level is below lcl - IF (ght(i,j,k) .LT. zlcl) THEN - qvplift = qvppari - tmklift = tmkpari - G/cpm*(ght(i,j,k) - ghtpari) - tvenv = tvirtual(tmk(i,j,k), qvp(i,j,k)) - tvlift = tvirtual(tmklift, qvplift) - ghtlift = ght(i,j,k) + IF (ght_new(k,i,j) .LT. zlcl) THEN + tmklift = tmk_new(kpar,i,j) - G/(CP * (1.D0 + CPMD*qvp_new(kpar,i,j))) * (ght_new(k,i,j) - ght_new(kpar,i,j)) + tvenv = tmk_new(k,i,j)*(EPS + qvp_new(k,i,j))/(EPS*(1.D0 + qvp_new(k,i,j))) + tvlift = tmklift*(EPS + qvp_new(kpar,i,j))/(EPS*(1.D0 + qvp_new(kpar,i,j))) + ghtlift = ght_new(k,i,j) ELSE IF (ght(i,j,k) .GE. zlcl .AND. ilcl .EQ. 0) THEN ! This model level and previous model level straddle the lcl, ! so first create a new level in the bottom-up array, at the lcl. - tmklift = tlcl - qvplift = qvppari - facden = ght(i,j,k) - ght(i,j,k+1) - fac1 = (zlcl-ght(i,j,k+1))/facden - fac2 = (ght(i,j,k)-zlcl)/facden - tmkenv = tmk(i,j,k+1)*fac2 + tmk(i,j,k)*fac1 - qvpenv = qvp(i,j,k+1)*fac2 + qvp(i,j,k)*fac1 - tvenv = tvirtual(tmkenv, qvpenv) - tvlift = tvirtual(tmklift, qvplift) + facden = 1/(ght_new(k,i,j) - ght_new(k+1,i,j)) + tmkenv = tmk_new(k+1,i,j)*((ght_new(k,i,j)-zlcl)*facden) + tmk_new(k,i,j)*((zlcl-ght_new(k+1,i,j))*facden) + qvpenv = qvp_new(k+1,i,j)*((ght_new(k,i,j)-zlcl)*facden) + qvp_new(k,i,j)*((zlcl-ght_new(k+1,i,j))*facden) + tvenv = tmkenv* (EPS + qvpenv) / (EPS * (1.D0 + qvpenv)) + tvlift = tlcl* (EPS + qvp_new(kpar,i,j)) / (EPS *(1.D0 + qvp_new(kpar,i,j))) ghtlift = zlcl ilcl = 1 ELSE - tmklift = TONPSADIABAT(ethpari, prs(i,j,k), psadithte, psadiprs,& + tmklift = TONPSADIABAT(ethpari, prs_new(k,i,j), psadithte, psadiprs,& psaditmk, GAMMA, errstat, errmsg) eslift = EZERO*EXP(ESLCON1*(tmklift - CELKEL)/(tmklift - ESLCON2)) - qvplift = EPS*eslift/(prs(i,j,k) - eslift) - tvenv = tvirtual(tmk(i,j,k), qvp(i,j,k)) - tvlift = tvirtual(tmklift, qvplift) - ghtlift = ght(i,j,k) + qvplift = EPS*eslift/(prs_new(k,i,j) - eslift) + tvenv = tmk_new(k,i,j) * (EPS + qvp_new(k,i,j)) / (EPS * (1.D0 + qvp_new(k,i,j))) + tvlift = tmklift*(EPS + qvplift) / (EPS * (1.D0 + qvplift)) + ghtlift = ght_new(k,i,j) END IF ! Buoyancy buoy(kk) = G*(tvlift - tvenv)/tvenv - zrel(kk) = ghtlift - ghtpari - + zrel(kk) = ghtlift - ght_new(kpar,i,j) IF ((kk .GT. 1) .AND. (buoy(kk)*buoy(kk-1) .LT. 0.0D0)) THEN ! Parcel ascent curve crosses sounding curve, so create a new level ! in the bottom-up array at the crossing. @@ -486,25 +858,23 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& zrel(kk-1) = zrel(kk-2) + buoy(kk-2)/& (buoy(kk-2) - buoy(kk))*(zrel(kk) - zrel(kk-2)) END IF - IF (ilcl .EQ. 1) THEN klcl = kk ilcl = 2 - !GOTO 33 CYCLE END IF - k = k - 1 END DO kmax = kk - IF (kmax .GT. 150) THEN - errstat = ALGERR - WRITE(errmsg, *) 'capecalc3d: kmax got too big. kmax=',kmax - RETURN - END IF - - ! If no lcl was found, set klcl to kmax. it is probably not really + ! IF (kmax .GT. 150) THEN + ! errstat = ALGERR + ! WRITE(errmsg, *) 'capecalc3d: kmax got too big. kmax=',kmax + ! RETURN + ! END IF + + ! If no lcl was found, set klcl to kmax. it is probably not + ! really ! at kmax, but this will make the rest of the routine behave ! properly. IF (ilcl .EQ. 0) klcl=kmax @@ -520,7 +890,6 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& benamin = benaccum(k) END IF END DO - ! Determine equilibrium level (el), which we define as the highest ! level of non-negative buoyancy above the lcl. note, this may be ! the top level if the parcel is still buoyant there. @@ -532,7 +901,6 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& kel = k elfound = .TRUE. EXIT - !GOTO 50 END IF END DO @@ -543,14 +911,11 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& ! non-zero, cape and cin will be set to a minimum of +0.1 j/kg, so ! that the zero contour in either the cin or cape fields will ! circumscribe regions of non-zero cape. - - ! In v6.1.0 of ncl, we added a _fillvalue attribute to the return + ! In v6.1.0 of ncl, we added a _fillvalue attribute to the return ! value of this function. at that time we decided to change -0.1 ! to a more appropriate missing value, which is passed into this ! routine as cmsg. - ! cape(i,j,kpar) = -0.1D0 - ! cin(i,j,kpar) = -0.1D0 IF (.NOT. elfound) THEN cape(i,j,kpar) = cmsg cin(i,j,kpar) = cmsg @@ -558,17 +923,20 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& CYCLE END IF -! GOTO 102 -! 50 CONTINUE - - ! If there is an equilibrium level, then cape is positive. we'll - ! define the level of free convection (lfc) as the point below the - ! el, but at or above the lcl, where accumulated buoyant energy is a - ! minimum. the net positive area (accumulated buoyant energy) from + ! If there is an equilibrium level, then cape is positive. + ! we'll + ! define the level of free convection (lfc) as the point below + ! the + ! el, but at or above the lcl, where accumulated buoyant energy + ! is a + ! minimum. the net positive area (accumulated buoyant energy) + ! from ! the lfc up to the el will be defined as the cape, and the net - ! negative area (negative of accumulated buoyant energy) from the - ! parcel starting point to the lfc will be defined as the convective + ! negative area (negative of accumulated buoyant energy) from + ! the + ! parcel starting point to the lfc will be defined as the + ! convective ! inhibition (cin). ! First get the lfc according to the above definition. @@ -595,23 +963,19 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& ! to a more appropriate missing value, which is passed into this ! routine as cmsg. - ! IF (cape(i,j,kpar).lt.100.D0) cin(i,j,kpar) = -0.1D0 IF (cape(i,j,kpar) .LT. 100.D0) cin(i,j,kpar) = cmsg -! 102 CONTINUE END DO - IF (i3dflag .EQ. 0) THEN cape(i,j,mkzh) = cape(i,j,kpar1) cin(i,j,mkzh) = cin(i,j,kpar1) ! meters agl - cin(i,j,mkzh-1) = zrel(klcl) + ghtpari - ter(i,j) + cin(i,j,mkzh-1) = zrel(klcl) + ght_new(kpar,i,j) - ter(i,j) ! meters agl - cin(i,j,mkzh-2) = zrel(klfc) + ghtpari - ter(i,j) + cin(i,j,mkzh-2) = zrel(klfc) + ght_new(kpar,i,j) - ter(i,j) - ENDIF END DO END DO - +!$OMP END PARALLEL DO RETURN -END SUBROUTINE DCAPECALC3D +END SUBROUTINE DCAPECALC2D diff --git a/fortran/wrf_rip_phys_routines.f90 b/fortran/wrf_rip_phys_routines.f90 index 350fb75..0b307f2 100644 --- a/fortran/wrf_rip_phys_routines.f90 +++ b/fortran/wrf_rip_phys_routines.f90 @@ -50,13 +50,15 @@ SUBROUTINE WETBULBCALC(prs, tmk, qvp, twb, nx, ny, nz, psafile, errstat, errmsg) !NCLEND INTEGER :: i, j, k - INTEGER :: jtch, jt, ipch, ip + INTEGER :: jt, ip REAL(KIND=8) :: q, t, p, e, tlcl, eth REAL(KIND=8) :: fracip, fracip2, fracjt, fracjt2 REAL(KIND=8), DIMENSION(150) :: PSADITHTE, PSADIPRS REAL(KIND=8), DIMENSION(150,150) :: PSADITMK REAL(KIND=8) :: tonpsadiabat + INTEGER :: l1, h1, mid1, rang1, l2, h2, mid2, rang2 + !INTEGER :: ip, ipch, jt, jtch ! Before looping, set lookup table for getting temperature on ! a pseudoadiabat. @@ -92,21 +94,53 @@ SUBROUTINE WETBULBCALC(prs, tmk, qvp, twb, nx, ny, nz, psafile, errstat, errmsg) tonpsadiabat = eth*(p/1000.)**GAMMA ELSE ! Otherwise, look for the given thte/prs point in the lookup table. - jt=-1 - DO jtch=1,150-1 - IF (eth .GE. PSADITHTE(jtch) .AND. eth .LT. PSADITHTE(jtch+1)) THEN - jt = jtch - EXIT - ENDIF - END DO - - ip=-1 - DO ipch=1,150-1 - IF (p .LE. PSADIPRS(ipch) .AND. p .GT. PSADIPRS(ipch+1)) THEN - ip = ipch - EXIT - ENDIF + jt = -1 + l1 = 1 + h1 = 149 + rang1 = h1 - l1 + mid1 = (h1 + l1) / 2 + DO WHILE(rang1 .GT. 1) + if(eth .GE. psadithte(mid1)) then + l1 = mid1 + else + h1 = mid1 + end if + rang1 = h1 - l1 + mid1 = (h1 + l1) / 2 END DO + jt = l1 + +! jt=-1 +! DO jtch=1,150-1 +! IF (eth .GE. PSADITHTE(jtch) .AND. eth .LT. PSADITHTE(jtch+1)) THEN +! jt = jtch +! EXIT +! ENDIF +! END DO + + ip = -1 + l2 = 1 + h2 = 149 + rang2 = h2 - l2 + mid2 = (h2 + l2) / 2 + DO WHILE(rang2 .GT. 1) + if(p .LE. psadiprs(mid2)) then + l2 = mid2 + else + h2 = mid2 + end if + rang2 = h2 - l2 + mid2 = (h2 + l2) / 2 + END DO + ip = l2 + +! ip=-1 +! DO ipch=1,150-1 +! IF (p .LE. PSADIPRS(ipch) .AND. p .GT. PSADIPRS(ipch+1)) THEN +! ip = ipch +! EXIT +! ENDIF +! END DO IF (jt .EQ. -1 .OR. ip .EQ. -1) THEN errstat = ALGERR diff --git a/fortran/wrffortran.pyf b/fortran/wrffortran.pyf index bf99a1e..eed6320 100644 --- a/fortran/wrffortran.pyf +++ b/fortran/wrffortran.pyf @@ -64,16 +64,38 @@ python module _wrffortran ! in character*(*) intent(inout) :: errmsg end subroutine dlookup_table subroutine dpfcalc(prs,sfp,pf,miy,mjx,mkzh,ter_follow) ! in :_wrffortran:rip_cape.f90 + real(kind=8) dimension(mkzh,miy,mjx),intent(in) :: prs + real(kind=8) dimension(miy,mjx),intent(in),depend(miy,mjx) :: sfp + real(kind=8) dimension(mkzh,miy,mjx),intent(out),depend(mkzh,miy,mjx) :: pf + integer, optional,intent(in),check(shape(prs,1)==miy),depend(prs) :: miy=shape(prs,1) + integer, optional,intent(in),check(shape(prs,2)==mjx),depend(prs) :: mjx=shape(prs,2) + integer, optional,intent(in),check(shape(prs,0)==mkzh),depend(prs) :: mkzh=shape(prs,0) + integer intent(in) :: ter_follow + end subroutine dpfcalc + subroutine dcapecalc3d(prs,tmk,qvp,ght,ter,sfp,cape,cin,cmsg,miy,mjx,mkzh,ter_follow,psafile,errstat,errmsg) ! in :_wrffortran:rip_cape.f90 + threadsafe + use omp_lib + use wrf_constants, only: tlclc2,gamma,tlclc1,rgasmd,tlclc4,g,tlclc3,thtecon3,eps,rd,cpmd,celkel,gammamd,eslcon2,eslcon1,cp,thtecon1,algerr,ezero,thtecon2 real(kind=8) dimension(miy,mjx,mkzh),intent(in) :: prs + real(kind=8) dimension(miy,mjx,mkzh),intent(in),depend(miy,mjx,mkzh) :: tmk + real(kind=8) dimension(miy,mjx,mkzh),intent(in),depend(miy,mjx,mkzh) :: qvp + real(kind=8) dimension(miy,mjx,mkzh),intent(in),depend(miy,mjx,mkzh) :: ght + real(kind=8) dimension(miy,mjx),intent(in),depend(miy,mjx) :: ter real(kind=8) dimension(miy,mjx),intent(in),depend(miy,mjx) :: sfp - real(kind=8) dimension(miy,mjx,mkzh),intent(out),depend(miy,mjx,mkzh) :: pf + real(kind=8) dimension(miy,mjx,mkzh),intent(out,in),depend(miy,mjx,mkzh) :: cape + real(kind=8) dimension(miy,mjx,mkzh),intent(out,in),depend(miy,mjx,mkzh) :: cin + real(kind=8) intent(in) :: cmsg integer, optional,intent(in),check(shape(prs,0)==miy),depend(prs) :: miy=shape(prs,0) integer, optional,intent(in),check(shape(prs,1)==mjx),depend(prs) :: mjx=shape(prs,1) integer, optional,intent(in),check(shape(prs,2)==mkzh),depend(prs) :: mkzh=shape(prs,2) integer intent(in) :: ter_follow - end subroutine dpfcalc - subroutine dcapecalc3d(prs,tmk,qvp,ght,ter,sfp,cape,cin,cmsg,miy,mjx,mkzh,i3dflag,ter_follow,psafile,errstat,errmsg) ! in :_wrffortran:rip_cape.f90 + character*(*) intent(in) :: psafile + integer intent(inout) :: errstat + character*(*) intent(inout) :: errmsg + end subroutine dcapecalc3d + subroutine dcapecalc2d(prs,tmk,qvp,ght,ter,sfp,cape,cin,cmsg,miy,mjx,mkzh,ter_follow,psafile,errstat,errmsg) ! in :_wrffortran:rip_cape.f90 threadsafe + use omp_lib use wrf_constants, only: tlclc2,gamma,tlclc1,rgasmd,tlclc4,g,tlclc3,thtecon3,eps,rd,cpmd,celkel,gammamd,eslcon2,eslcon1,cp,thtecon1,algerr,ezero,thtecon2 real(kind=8) dimension(miy,mjx,mkzh),intent(in) :: prs real(kind=8) dimension(miy,mjx,mkzh),intent(in),depend(miy,mjx,mkzh) :: tmk @@ -87,12 +109,11 @@ python module _wrffortran ! in integer, optional,intent(in),check(shape(prs,0)==miy),depend(prs) :: miy=shape(prs,0) integer, optional,intent(in),check(shape(prs,1)==mjx),depend(prs) :: mjx=shape(prs,1) integer, optional,intent(in),check(shape(prs,2)==mkzh),depend(prs) :: mkzh=shape(prs,2) - integer intent(in) :: i3dflag integer intent(in) :: ter_follow character*(*) intent(in) :: psafile integer intent(inout) :: errstat character*(*) intent(inout) :: errmsg - end subroutine dcapecalc3d + end subroutine dcapecalc2d subroutine dcloudfrac(pres,rh,lowc,midc,highc,nz,ns,ew) ! in :_wrffortran:wrf_cloud_fracf.f90 threadsafe real(kind=8) dimension(ew,ns,nz),intent(in) :: pres diff --git a/src/wrf/extension.py b/src/wrf/extension.py index 5f3fcea..90bb8cc 100755 --- a/src/wrf/extension.py +++ b/src/wrf/extension.py @@ -7,8 +7,8 @@ from .constants import Constants from ._wrffortran import (dcomputetk, dinterp3dz, dinterp2dxy, dinterp1d, dcomputeseaprs, dfilter2d, dcomputerh, dcomputeuvmet, - dcomputetd, dcapecalc3d, dcloudfrac, wrfcttcalc, - calcdbz, dcalrelhl, dcalcuh, dcomputepv, + dcomputetd, dcapecalc2d, dcapecalc3d, dcloudfrac, + wrfcttcalc, calcdbz, dcalrelhl, dcalcuh, dcomputepv, dcomputeabsvort, dlltoij, dijtoll, deqthecalc, omgcalc, virtual_temp, wetbulbcalc, dcomputepw, wrf_monotonic, wrf_vintrp, dcomputewspd, @@ -597,8 +597,13 @@ def _cape(p_hpa, tk, qv, ht, ter, sfp, missing, i3dflag, ter_follow, errstat = np.array(0) errmsg = np.zeros(Constants.ERRLEN, "c") + if i3dflag: + cape_routine = dcapecalc3d + else: + cape_routine = dcapecalc2d + # note that p_hpa, tk, qv, and ht have the vertical flipped - result = dcapecalc3d(p_hpa, + result = cape_routine(p_hpa, tk, qv, ht, @@ -607,7 +612,6 @@ def _cape(p_hpa, tk, qv, ht, ter, sfp, missing, i3dflag, ter_follow, capeview, cinview, missing, - i3dflag, ter_follow, psafile, errstat, From f4eeee927bafe46e07d15ffd198382691d8bba16 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Fri, 13 Oct 2017 16:09:51 -0600 Subject: [PATCH 02/43] Modified the indexing labels so that they make sense. --- fortran/rip_cape.f90 | 121 +++++++++++++++++++++-------------------- fortran/wrffortran.pyf | 58 ++++++++++---------- 2 files changed, 89 insertions(+), 90 deletions(-) diff --git a/fortran/rip_cape.f90 b/fortran/rip_cape.f90 index 945b98e..84fdb30 100644 --- a/fortran/rip_cape.f90 +++ b/fortran/rip_cape.f90 @@ -37,7 +37,7 @@ END FUNCTION TVIRTUAL REAL(KIND=8) FUNCTION TONPSADIABAT(thte, prs, psadithte, psadiprs, psaditmk, gamma,& errstat, errmsg) USE wrf_constants, ONLY : ALGERR -!$OMP DECLARE SIMD (TONPSADIABAT) +!!$OMP DECLARE SIMD (TONPSADIABAT) !!uniform(thte,prs,psadithte,psadiprs,psaditmk) !f2py threadsafe !f2py intent(in,out) :: cape, cin @@ -218,17 +218,17 @@ END SUBROUTINE DLOOKUP_TABLE ! which case it assumes the lower bounding pressure level is as far ! below the lowest vertical level as the upper bounding pressure ! level is above. -SUBROUTINE DPFCALC(prs, sfp, pf, miy, mjx, mkzh, ter_follow) +SUBROUTINE DPFCALC(prs, sfp, pf, mix, mjy, mkzh, ter_follow) - REAL(KIND=8), DIMENSION(mkzh,miy,mjx), INTENT(IN) :: prs - REAL(KIND=8), DIMENSION(miy,mjx), INTENT(IN) :: sfp - REAL(KIND=8), DIMENSION(mkzh,miy,mjx), INTENT(OUT) :: pf - INTEGER, INTENT(IN) :: ter_follow,miy,mjx,mkzh + REAL(KIND=8), DIMENSION(mkzh,mix,mjy), INTENT(IN) :: prs + REAL(KIND=8), DIMENSION(mix,mjy), INTENT(IN) :: sfp + REAL(KIND=8), DIMENSION(mkzh,mix,mjy), INTENT(OUT) :: pf + INTEGER, INTENT(IN) :: ter_follow,mix,mjy,mkzh INTEGER :: i,j,k - DO j = 1,mjx - DO i = 1,miy + DO j = 1,mjy + DO i = 1,mix DO k = 1,mkzh IF (k .EQ. mkzh) THEN ! terrain-following data @@ -270,7 +270,7 @@ END SUBROUTINE DPFCALC ! NCLFORTSTART SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& - cmsg,miy,mjx,mkzh,ter_follow,& + cmsg,mix,mjy,mkzh,ter_follow,& psafile, errstat, errmsg) USE wrf_constants, ONLY : ALGERR, CELKEL, G, EZERO, ESLCON1, ESLCON2, & EPS, RD, CP, GAMMA, CPMD, RGASMD, GAMMAMD, TLCLC1, & @@ -282,15 +282,15 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& !f2py threadsafe !f2py intent(in,out) :: cape, cin - INTEGER, INTENT(IN) :: miy, mjx, mkzh, ter_follow - REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(IN) :: prs - REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(IN) :: tmk - REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(IN) :: qvp - REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(IN) :: ght - REAL(KIND=8), DIMENSION(miy,mjx), INTENT(IN) :: ter - REAL(KIND=8), DIMENSION(miy,mjx), INTENT(IN) ::sfp - REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(OUT) :: cape - REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(OUT) :: cin + INTEGER, INTENT(IN) :: mix, mjy, mkzh, ter_follow + REAL(KIND=8), DIMENSION(mix,mjy,mkzh), INTENT(IN) :: prs + REAL(KIND=8), DIMENSION(mix,mjy,mkzh), INTENT(IN) :: tmk + REAL(KIND=8), DIMENSION(mix,mjy,mkzh), INTENT(IN) :: qvp + REAL(KIND=8), DIMENSION(mix,mjy,mkzh), INTENT(IN) :: ght + REAL(KIND=8), DIMENSION(mix,mjy), INTENT(IN) :: ter + REAL(KIND=8), DIMENSION(mix,mjy), INTENT(IN) ::sfp + REAL(KIND=8), DIMENSION(mix,mjy,mkzh), INTENT(OUT) :: cape + REAL(KIND=8), DIMENSION(mix,mjy,mkzh), INTENT(OUT) :: cin REAL(KIND=8), INTENT(IN) :: cmsg CHARACTER(LEN=*), INTENT(IN) :: psafile INTEGER, INTENT(INOUT) :: errstat @@ -306,16 +306,16 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& REAL(KIND=8) :: eslift, tmkenv, qvpenv, tonpsadiabat REAL(KIND=8) :: benamin, dz REAL(KIND=8), DIMENSION(150) :: buoy, zrel, benaccum - REAL(KIND=8), DIMENSION(mkzh,miy,mjx) :: prsf + REAL(KIND=8), DIMENSION(mkzh,mix,mjy) :: prsf REAL(KIND=8), DIMENSION(150) :: psadithte, psadiprs REAL(KIND=8), DIMENSION(150,150) :: psaditmk LOGICAL :: elfound REAL :: t1,t2 - REAL(KIND=8), DIMENSION(mkzh,miy,mjx) :: prs_new - REAL(KIND=8), DIMENSION(mkzh,miy,mjx) :: tmk_new - REAL(KIND=8), DIMENSION(mkzh,miy,mjx) :: qvp_new - REAL(KIND=8), DIMENSION(mkzh,miy,mjx) :: ght_new + REAL(KIND=8), DIMENSION(mkzh,mix,mjy) :: prs_new + REAL(KIND=8), DIMENSION(mkzh,mix,mjy) :: tmk_new + REAL(KIND=8), DIMENSION(mkzh,mix,mjy) :: qvp_new + REAL(KIND=8), DIMENSION(mkzh,mix,mjy) :: ght_new ! To remove compiler warnings tmkpari = 0 @@ -354,19 +354,19 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& !CALL cpu_time(t1) !CALL OMP_SET_NUM_THREADS(16) !$OMP PARALLEL DO - DO i = 1,mjx - DO j = 1,miy + DO j = 1,mjy + DO i = 1,mix DO k = 1,mkzh - prs_new(k,j,i) = prs(j,i,k) - tmk_new(k,j,i) = tmk(j,i,k) - qvp_new(k,j,i) = qvp(j,i,k) - ght_new(k,j,i) = ght(j,i,k) + prs_new(k,i,j) = prs(i,j,k) + tmk_new(k,i,j) = tmk(i,j,k) + qvp_new(k,i,j) = qvp(i,j,k) + ght_new(k,i,j) = ght(i,j,k) END DO END DO END DO !$OMP END PARALLEL DO - CALL DPFCALC(prs_new, sfp, prsf, miy, mjx, mkzh, ter_follow) + CALL DPFCALC(prs_new, sfp, prsf, mix, mjy, mkzh, ter_follow) ! before looping, set lookup table for getting temperature on ! a pseudoadiabat. @@ -383,12 +383,12 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& !$OMP benaccum, zrel, kmax, dz, elfound, & !$OMP kel, klfc, & !$OMP i,j,k,kpar) - DO j = 1,mjx - DO i = 1,miy + DO j = 1,mjy + DO i = 1,mix cape(i,j,1) = 0.d0 cin(i,j,1) = 0.d0 -!$OMP SIMD +!!$OMP SIMD DO kpar = 2, mkzh ! Calculate temperature and moisture properties of parcel @@ -420,7 +420,7 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& klcl = 1 END IF -!$OMP SIMD lastprivate(qvplift,tmklift,ghtlift,tvlift,tmkenv,qvpenv,tvenv,eslift,facden) +!!$OMP SIMD lastprivate(qvplift,tmklift,ghtlift,tvlift,tmkenv,qvpenv,tvenv,eslift,facden) DO k = kpar,1,-1 ! For arrays that go bottom to top kk = kk + 1 @@ -601,7 +601,7 @@ END SUBROUTINE DCAPECALC3D ! NCLFORTSTART SUBROUTINE DCAPECALC2D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& - cmsg,miy,mjx,mkzh,ter_follow,& + cmsg,mix,mjy,mkzh,ter_follow,& psafile, errstat, errmsg) USE wrf_constants, ONLY : ALGERR, CELKEL, G, EZERO, ESLCON1, ESLCON2, & EPS, RD, CP, GAMMA, CPMD, RGASMD, GAMMAMD, TLCLC1, & @@ -613,15 +613,15 @@ SUBROUTINE DCAPECALC2D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& !f2py threadsafe !f2py intent(in,out) :: cape, cin - INTEGER, INTENT(IN) :: miy, mjx, mkzh, ter_follow - REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(IN) :: prs - REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(IN) :: tmk - REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(IN) :: qvp - REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(IN) :: ght - REAL(KIND=8), DIMENSION(miy,mjx), INTENT(IN) :: ter - REAL(KIND=8), DIMENSION(miy,mjx), INTENT(IN) ::sfp - REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(OUT) :: cape - REAL(KIND=8), DIMENSION(miy,mjx,mkzh), INTENT(OUT) :: cin + INTEGER, INTENT(IN) :: mix, mjy, mkzh, ter_follow + REAL(KIND=8), DIMENSION(mix,mjy,mkzh), INTENT(IN) :: prs + REAL(KIND=8), DIMENSION(mix,mjy,mkzh), INTENT(IN) :: tmk + REAL(KIND=8), DIMENSION(mix,mjy,mkzh), INTENT(IN) :: qvp + REAL(KIND=8), DIMENSION(mix,mjy,mkzh), INTENT(IN) :: ght + REAL(KIND=8), DIMENSION(mix,mjy), INTENT(IN) :: ter + REAL(KIND=8), DIMENSION(mix,mjy), INTENT(IN) ::sfp + REAL(KIND=8), DIMENSION(mix,mjy,mkzh), INTENT(OUT) :: cape + REAL(KIND=8), DIMENSION(mix,mjy,mkzh), INTENT(OUT) :: cin REAL(KIND=8), INTENT(IN) :: cmsg CHARACTER(LEN=*), INTENT(IN) :: psafile INTEGER, INTENT(INOUT) :: errstat @@ -639,16 +639,16 @@ SUBROUTINE DCAPECALC2D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& REAL(KIND=8) :: eslift, tmkenv, qvpenv, tonpsadiabat REAL(KIND=8) :: benamin, dz, pup, pdn REAL(KIND=8), DIMENSION(150) :: buoy, zrel, benaccum - REAL(KIND=8), DIMENSION(mkzh,miy,mjx) :: prsf + REAL(KIND=8), DIMENSION(mkzh,mix,mjy) :: prsf REAL(KIND=8), DIMENSION(150) :: psadithte, psadiprs REAL(KIND=8), DIMENSION(150,150) :: psaditmk LOGICAL :: elfound INTEGER :: nthreads REAL(KIND=8), DIMENSION(mkzh) :: eth_temp - REAL(KIND=8), DIMENSION(mkzh,miy,mjx) :: prs_new - REAL(KIND=8), DIMENSION(mkzh,miy,mjx) :: tmk_new - REAL(KIND=8), DIMENSION(mkzh,miy,mjx) :: qvp_new - REAL(KIND=8), DIMENSION(mkzh,miy,mjx) :: ght_new + REAL(KIND=8), DIMENSION(mkzh,mix,mjy) :: prs_new + REAL(KIND=8), DIMENSION(mkzh,mix,mjy) :: tmk_new + REAL(KIND=8), DIMENSION(mkzh,mix,mjy) :: qvp_new + REAL(KIND=8), DIMENSION(mkzh,mix,mjy) :: ght_new ! To remove compiler warnings errstat = 0 @@ -684,13 +684,13 @@ SUBROUTINE DCAPECALC2D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& ! !$OMP PARALLEL DO - DO i = 1,mjx - DO j = 1,miy + DO j = 1,mjy + DO i = 1,mix DO k = 1,mkzh - prs_new(k,j,i) = prs(j,i,k) - tmk_new(k,j,i) = tmk(j,i,k) - qvp_new(k,j,i) = qvp(j,i,k) - ght_new(k,j,i) = ght(j,i,k) + prs_new(k,i,j) = prs(i,j,k) + tmk_new(k,i,j) = tmk(i,j,k) + qvp_new(k,i,j) = qvp(i,j,k) + ght_new(k,i,j) = ght(i,j,k) END DO END DO END DO @@ -699,7 +699,7 @@ SUBROUTINE DCAPECALC2D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& ! calculated the pressure at full sigma levels (a set of pressure ! levels that bound the layers represented by the vertical grid points) - CALL DPFCALC(prs_new, sfp, prsf, miy, mjx, mkzh, ter_follow) + CALL DPFCALC(prs_new, sfp, prsf, mix, mjy, mkzh, ter_follow) ! before looping, set lookup table for getting temperature on ! a pseudoadiabat. @@ -713,15 +713,16 @@ SUBROUTINE DCAPECALC2D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& !CALL OMP_SET_NUM_THREADS(16) !nthreads = omp_get_num_threads() + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(tlcl, ethpari, & !$OMP zlcl, kk, ilcl, klcl, tmklift, tvenv, tvlift, ghtlift, & !$OMP facden, tmkenv, qvpenv, eslift, qvplift, buoy, benamin, & !$OMP benaccum, zrel, kmax, dz, elfound, & !$OMP kel, klfc, pavg, p2, p1, totthe, totqvp, totprs, & -!$OMP i,j,k,kpar, qvppari, tmkpari,p, pup, pdn, q, th, & -!$OMP pp1, pp2) - DO j = 1,mjx - DO i = 1,miy +!$OMP i,j,k,kpar, kpar1, kpar2, qvppari, tmkpari,p, pup, pdn, q, th, & +!$OMP pp1, pp2, ethmax, eth_temp, klev) + DO j = 1,mjy + DO i = 1,mix cape(i,j,1) = 0.d0 cin(i,j,1) = 0.d0 ! find parcel with max theta-e in lowest 3 km agl. diff --git a/fortran/wrffortran.pyf b/fortran/wrffortran.pyf index eed6320..5a01ba5 100644 --- a/fortran/wrffortran.pyf +++ b/fortran/wrffortran.pyf @@ -63,51 +63,49 @@ python module _wrffortran ! in integer intent(inout) :: errstat character*(*) intent(inout) :: errmsg end subroutine dlookup_table - subroutine dpfcalc(prs,sfp,pf,miy,mjx,mkzh,ter_follow) ! in :_wrffortran:rip_cape.f90 - real(kind=8) dimension(mkzh,miy,mjx),intent(in) :: prs - real(kind=8) dimension(miy,mjx),intent(in),depend(miy,mjx) :: sfp - real(kind=8) dimension(mkzh,miy,mjx),intent(out),depend(mkzh,miy,mjx) :: pf - integer, optional,intent(in),check(shape(prs,1)==miy),depend(prs) :: miy=shape(prs,1) - integer, optional,intent(in),check(shape(prs,2)==mjx),depend(prs) :: mjx=shape(prs,2) + subroutine dpfcalc(prs,sfp,pf,mix,mjy,mkzh,ter_follow) ! in :_wrffortran:rip_cape.f90 + real(kind=8) dimension(mkzh,mix,mjy),intent(in) :: prs + real(kind=8) dimension(mix,mjy),intent(in),depend(mix,mjy) :: sfp + real(kind=8) dimension(mkzh,mix,mjy),intent(out),depend(mkzh,mix,mjy) :: pf + integer, optional,intent(in),check(shape(prs,1)==mix),depend(prs) :: mix=shape(prs,1) + integer, optional,intent(in),check(shape(prs,2)==mjy),depend(prs) :: mjy=shape(prs,2) integer, optional,intent(in),check(shape(prs,0)==mkzh),depend(prs) :: mkzh=shape(prs,0) integer intent(in) :: ter_follow end subroutine dpfcalc - subroutine dcapecalc3d(prs,tmk,qvp,ght,ter,sfp,cape,cin,cmsg,miy,mjx,mkzh,ter_follow,psafile,errstat,errmsg) ! in :_wrffortran:rip_cape.f90 + subroutine dcapecalc3d(prs,tmk,qvp,ght,ter,sfp,cape,cin,cmsg,mix,mjy,mkzh,ter_follow,psafile,errstat,errmsg) ! in :_wrffortran:rip_cape.f90 threadsafe - use omp_lib use wrf_constants, only: tlclc2,gamma,tlclc1,rgasmd,tlclc4,g,tlclc3,thtecon3,eps,rd,cpmd,celkel,gammamd,eslcon2,eslcon1,cp,thtecon1,algerr,ezero,thtecon2 - real(kind=8) dimension(miy,mjx,mkzh),intent(in) :: prs - real(kind=8) dimension(miy,mjx,mkzh),intent(in),depend(miy,mjx,mkzh) :: tmk - real(kind=8) dimension(miy,mjx,mkzh),intent(in),depend(miy,mjx,mkzh) :: qvp - real(kind=8) dimension(miy,mjx,mkzh),intent(in),depend(miy,mjx,mkzh) :: ght - real(kind=8) dimension(miy,mjx),intent(in),depend(miy,mjx) :: ter - real(kind=8) dimension(miy,mjx),intent(in),depend(miy,mjx) :: sfp - real(kind=8) dimension(miy,mjx,mkzh),intent(out,in),depend(miy,mjx,mkzh) :: cape - real(kind=8) dimension(miy,mjx,mkzh),intent(out,in),depend(miy,mjx,mkzh) :: cin + real(kind=8) dimension(mix,mjy,mkzh),intent(in) :: prs + real(kind=8) dimension(mix,mjy,mkzh),intent(in),depend(mix,mjy,mkzh) :: tmk + real(kind=8) dimension(mix,mjy,mkzh),intent(in),depend(mix,mjy,mkzh) :: qvp + real(kind=8) dimension(mix,mjy,mkzh),intent(in),depend(mix,mjy,mkzh) :: ght + real(kind=8) dimension(mix,mjy),intent(in),depend(mix,mjy) :: ter + real(kind=8) dimension(mix,mjy),intent(in),depend(mix,mjy) :: sfp + real(kind=8) dimension(mix,mjy,mkzh),intent(out,in),depend(mix,mjy,mkzh) :: cape + real(kind=8) dimension(mix,mjy,mkzh),intent(out,in),depend(mix,mjy,mkzh) :: cin real(kind=8) intent(in) :: cmsg - integer, optional,intent(in),check(shape(prs,0)==miy),depend(prs) :: miy=shape(prs,0) - integer, optional,intent(in),check(shape(prs,1)==mjx),depend(prs) :: mjx=shape(prs,1) + integer, optional,intent(in),check(shape(prs,0)==mix),depend(prs) :: mix=shape(prs,0) + integer, optional,intent(in),check(shape(prs,1)==mjy),depend(prs) :: mjy=shape(prs,1) integer, optional,intent(in),check(shape(prs,2)==mkzh),depend(prs) :: mkzh=shape(prs,2) integer intent(in) :: ter_follow character*(*) intent(in) :: psafile integer intent(inout) :: errstat character*(*) intent(inout) :: errmsg end subroutine dcapecalc3d - subroutine dcapecalc2d(prs,tmk,qvp,ght,ter,sfp,cape,cin,cmsg,miy,mjx,mkzh,ter_follow,psafile,errstat,errmsg) ! in :_wrffortran:rip_cape.f90 + subroutine dcapecalc2d(prs,tmk,qvp,ght,ter,sfp,cape,cin,cmsg,mix,mjy,mkzh,ter_follow,psafile,errstat,errmsg) ! in :_wrffortran:rip_cape.f90 threadsafe - use omp_lib use wrf_constants, only: tlclc2,gamma,tlclc1,rgasmd,tlclc4,g,tlclc3,thtecon3,eps,rd,cpmd,celkel,gammamd,eslcon2,eslcon1,cp,thtecon1,algerr,ezero,thtecon2 - real(kind=8) dimension(miy,mjx,mkzh),intent(in) :: prs - real(kind=8) dimension(miy,mjx,mkzh),intent(in),depend(miy,mjx,mkzh) :: tmk - real(kind=8) dimension(miy,mjx,mkzh),intent(in),depend(miy,mjx,mkzh) :: qvp - real(kind=8) dimension(miy,mjx,mkzh),intent(in),depend(miy,mjx,mkzh) :: ght - real(kind=8) dimension(miy,mjx),intent(in),depend(miy,mjx) :: ter - real(kind=8) dimension(miy,mjx),intent(in),depend(miy,mjx) :: sfp - real(kind=8) dimension(miy,mjx,mkzh),intent(out,in),depend(miy,mjx,mkzh) :: cape - real(kind=8) dimension(miy,mjx,mkzh),intent(out,in),depend(miy,mjx,mkzh) :: cin + real(kind=8) dimension(mix,mjy,mkzh),intent(in) :: prs + real(kind=8) dimension(mix,mjy,mkzh),intent(in),depend(mix,mjy,mkzh) :: tmk + real(kind=8) dimension(mix,mjy,mkzh),intent(in),depend(mix,mjy,mkzh) :: qvp + real(kind=8) dimension(mix,mjy,mkzh),intent(in),depend(mix,mjy,mkzh) :: ght + real(kind=8) dimension(mix,mjy),intent(in),depend(mix,mjy) :: ter + real(kind=8) dimension(mix,mjy),intent(in),depend(mix,mjy) :: sfp + real(kind=8) dimension(mix,mjy,mkzh),intent(out,in),depend(mix,mjy,mkzh) :: cape + real(kind=8) dimension(mix,mjy,mkzh),intent(out,in),depend(mix,mjy,mkzh) :: cin real(kind=8) intent(in) :: cmsg - integer, optional,intent(in),check(shape(prs,0)==miy),depend(prs) :: miy=shape(prs,0) - integer, optional,intent(in),check(shape(prs,1)==mjx),depend(prs) :: mjx=shape(prs,1) + integer, optional,intent(in),check(shape(prs,0)==mix),depend(prs) :: mix=shape(prs,0) + integer, optional,intent(in),check(shape(prs,1)==mjy),depend(prs) :: mjy=shape(prs,1) integer, optional,intent(in),check(shape(prs,2)==mkzh),depend(prs) :: mkzh=shape(prs,2) integer intent(in) :: ter_follow character*(*) intent(in) :: psafile From 64838f1841729bdded5b7f4c7c50c13b3e4c2969 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Wed, 18 Oct 2017 16:26:49 -0600 Subject: [PATCH 03/43] Added OpenMP directives for wrf_vintrp --- fortran/wrf_vinterp.f90 | 113 +++++++++++++++++++--------------------- 1 file changed, 53 insertions(+), 60 deletions(-) diff --git a/fortran/wrf_vinterp.f90 b/fortran/wrf_vinterp.f90 index c9bebb0..b7a5f64 100644 --- a/fortran/wrf_vinterp.f90 +++ b/fortran/wrf_vinterp.f90 @@ -12,10 +12,11 @@ SUBROUTINE wrf_monotonic(out, in, lvprs, cor, idir, delta, ew, ns, nz, icorsw) INTEGER, INTENT(IN) :: idir, ew, ns, nz, icorsw REAL(KIND=8), INTENT(IN) :: delta - REAL(KIND=8), DIMENSION(ew,ns,nz), INTENT(INOUT) :: in + REAL(KIND=8), DIMENSION(ew,ns,nz), INTENT(IN) :: in REAL(KIND=8), DIMENSION(ew,ns,nz), INTENT(OUT) :: out - REAL(KIND=8), DIMENSION(ew,ns,nz) :: lvprs - REAL(KIND=8), DIMENSION(ew,ns) :: cor + REAL(KIND=8), DIMENSION(ew,ns,nz), INTENT(IN) :: lvprs + REAL(KIND=8), DIMENSION(ew,ns), INTENT(IN) :: cor + !NCLEND @@ -65,7 +66,7 @@ END SUBROUTINE wrf_monotonic !NCLFORTSTART -FUNCTION wrf_intrp_value(wvalp0, wvalp1, vlev, vcp0, vcp1, icase, errstat, errmsg) +FUNCTION wrf_intrp_value(wvalp0, wvalp1, vlev, vcp0, vcp1, icase, errstat) USE wrf_constants, ONLY : ALGERR, SCLHT IMPLICIT NONE @@ -75,7 +76,6 @@ FUNCTION wrf_intrp_value(wvalp0, wvalp1, vlev, vcp0, vcp1, icase, errstat, errms INTEGER, INTENT(IN) :: icase REAL(KIND=8), INTENT(IN) :: wvalp0, wvalp1, vlev, vcp0, vcp1 INTEGER, INTENT(INOUT) :: errstat - CHARACTER(LEN=*), INTENT(INOUT) :: errmsg REAL(KIND=8) :: wrf_intrp_value !NCLEND @@ -83,10 +83,6 @@ FUNCTION wrf_intrp_value(wvalp0, wvalp1, vlev, vcp0, vcp1, icase, errstat, errms REAL(KIND=8) :: valp0, valp1, rvalue REAL(KIND=8) :: chkdiff - !REAL(KIND=8), PARAMETER :: RGAS=287.04d0 - !REAL(KIND=8), PARAMETER :: USSALR=0.0065d0 - !REAL(KIND=8), PARAMETER :: SCLHT=RGAS*256.d0/9.81d0 - errstat = 0 valp0 = wvalp0 @@ -99,7 +95,7 @@ FUNCTION wrf_intrp_value(wvalp0, wvalp1, vlev, vcp0, vcp1, icase, errstat, errms chkdiff = vcp1 - vcp0 IF(chkdiff .EQ. 0) THEN errstat = ALGERR - errmsg = "bad difference in vcp's" + !errmsg = "bad difference in vcp's" wrf_intrp_value = 0 RETURN !PRINT *,"bad difference in vcp's" @@ -129,6 +125,7 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& USE wrf_constants, ONLY : ALGERR, SCLHT, EXPON, EXPONI, GAMMA, GAMMAMD, TLCLC1, & TLCLC2, TLCLC3, TLCLC4, THTECON1, THTECON2, THTECON3, & CELKEL, EPS, USSALR + USE omp_lib IMPLICIT NONE @@ -152,6 +149,7 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& INTEGER :: nreqlvs, ripk !njx,niy INTEGER :: i, j, k, kupper !itriv INTEGER :: ifound, isign !miy,mjx + INTEGER :: log_errcnt, interp_errcnt, interp_errstat REAL(KIND=8), DIMENSION(ew,ns) :: tempout REAL(KIND=8) :: rlevel, vlev, diff REAL(KIND=8) :: tmpvlev @@ -165,27 +163,7 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& REAL(KIND=8) :: pbot, zbot, tbotextrap, e REAL(KIND=8) :: tlcl, gammam CHARACTER(LEN=1) :: cvcord - - !REAL(KIND=8), PARAMETER :: RGAS = 287.04d0 !J/K/kg - !REAL(KIND=8), PARAMETER :: RGASMD = .608d0 - !REAL(KIND=8), PARAMETER :: USSALR = .0065d0 ! deg C per m - !REAL(KIND=8), PARAMETER :: SCLHT = RGAS*256.d0/9.81d0 - !REAL(KIND=8), PARAMETER :: EPS = 0.622d0 - !REAL(KIND=8), PARAMETER :: RCONST = -9.81d0/(RGAS * USSALR) - !REAL(KIND=8), PARAMETER :: EXPON = RGAS*USSALR/9.81d0 - !REAL(KIND=8), PARAMETER :: EXPONI = 1./EXPON - !REAL(KIND=8), PARAMETER :: TLCLC1 = 2840.d0 - !REAL(KIND=8), PARAMETER :: TLCLC2 = 3.5d0 - !REAL(KIND=8), PARAMETER :: TLCLC3 = 4.805d0 - !REAL(KIND=8), PARAMETER :: TLCLC4 = 55.d0 - !REAL(KIND=8), PARAMETER :: THTECON1 = 3376.d0 ! K - !REAL(KIND=8), PARAMETER :: THTECON2 = 2.54d0 - !REAL(KIND=8), PARAMETER :: THTECON3 = 0.81d0 - !REAL(KIND=8), PARAMETER :: CP = 1004.d0 - !REAL(KIND=8), PARAMETER :: CPMD = 0.887d0 - !REAL(KIND=8), PARAMETER :: GAMMA = RGAS/CP - !REAL(KIND=8), PARAMETER :: GAMMAMD = RGASMD-CPMD - !REAL(KIND=8), PARAMETER :: CELKEL = 273.16d0 + INTEGER :: thd ! Removes the warnings for uninitialized variables cvcord = '' @@ -193,6 +171,9 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& zlev = 0 vlev = 0 errstat = 0 + interp_errcnt = 0 + interp_errstat = 0 + log_errcnt = 0 IF (vcor .EQ. 1) THEN cvcord = 'p' @@ -207,11 +188,13 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& !njx = ew !niy = ns +!$OMP PARALLEL DO DO j = 1,ns DO i = 1,ew tempout(i,j) = rmsg END DO END DO +!$OMP END PARALLEL DO DO nreqlvs = 1,numlevels IF (cvcord .EQ. 'z') THEN @@ -224,9 +207,16 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& vlev = interp_levels(nreqlvs) END IF +!$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i, j, k, ifound, & +!$OMP ripk, vcp1, vcp0, valp0, valp1, tmpvlev, interp_errstat, & +!$OMP vclhsl, vctophsl, diff, isign, plhsl, zlhsl, ezlhsl, tlhsl, & +!$OMP zsurf, qvapor, psurf, psurfsm, ezsurf, plev, ezlev, zlev, & +!$OMP ptarget, dpmin, kupper, pbot, zbot, pratio, tbotextrap, & +!$OMP vt, tlev, gammam, e, tlcl) REDUCTION (+:log_errcnt, interp_errcnt) DO j=1,ns DO i=1,ew ! Get the interpolated value that is within the model domain + thd = omp_get_thread_num() ifound = 0 DO k = 1,nz-1 ripk = nz-k+1 @@ -245,35 +235,33 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& IF (logp .EQ. 1) THEN vcp1 = LOG(vcp1) vcp0 = LOG(vcp0) - IF (vlev .EQ. 0.0D0) THEN - errstat = ALGERR - WRITE(errmsg, *) "Pres=0. Unable to take log of 0." - RETURN - !PRINT *,"Pressure value = 0" - !PRINT *,"Unable to take log of 0" - !STOP + IF (vlev .NE. 0.0D0) THEN + tmpvlev = LOG(vlev) + ELSE + log_errcnt = log_errcnt + 1 + tmpvlev = rmsg END IF - tmpvlev = LOG(vlev) ELSE tmpvlev = vlev END IF - tempout(i,j) = wrf_intrp_value(valp0, valp1, tmpvlev, vcp0, & - vcp1, icase, errstat, errmsg) - IF (errstat .NE. 0) THEN - RETURN - END IF - ! print *,"one ",i,j,tempout(i,j) - ifound = 1 + IF (tmpvlev .NE. rmsg) THEN + tempout(i,j) = wrf_intrp_value(valp0, valp1, tmpvlev, vcp0, & + vcp1, icase, interp_errstat) + + IF (interp_errstat .NE. 0) THEN + tempout(i,j) = rmsg + interp_errcnt = interp_errcnt + 1 + END IF + + ifound = 1 + END IF END IF - !GOTO 115 ! EXIT EXIT END IF END DO !end for the k loop - !115 CONTINUE IF (ifound .EQ. 1) THEN !Grid point is in the model domain - !GOTO 333 ! CYCLE CYCLE END IF @@ -281,7 +269,6 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& !all values above or below the model level to rmsg. IF (extrap .EQ. 0) THEN tempout(i,j) = rmsg - !GOTO 333 ! CYCLE CYCLE END IF @@ -296,8 +283,6 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& IF (isign*vlev .GE. isign*vctophsl) THEN ! Assign the highest model level to the out array tempout(i,j) = datain(i,j,nz) - ! print *,"at warn",i,j,tempout(i,j) - !GOTO 333 ! CYCLE CYCLE END IF @@ -307,7 +292,6 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& IF (datain(i,j,1) .EQ. rmsg) THEN tempout(i,j) = rmsg - !GOTO 333 ! CYCLE CYCLE END IF @@ -351,7 +335,6 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& zlev = -SCLHT*LOG(ezlev) IF (icase .EQ. 2) THEN tempout(i,j) = zlev - !GOTO 333 ! CYCLE CYCLE END IF @@ -362,7 +345,6 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& psurf + (ezsurf - ezlev)*plhsl)/(ezsurf - ezlhsl) IF (icase .EQ. 1) THEN tempout(i,j) = plev - !GOTO 333 ! CYCLE CYCLE END IF END IF @@ -374,12 +356,11 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& ripk = nz-k+1 dp = ABS((pres(i,j,ripk) * 0.01D0) - ptarget) IF (dp .GT. dpmin) THEN - !GOTO 334 ! EXIT EXIT END IF dpmin = MIN(dpmin, dp) END DO - !334 + kupper = k-1 ripk = nz - kupper + 1 @@ -394,7 +375,6 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& zlev = zbot + vt/USSALR*(1. - (vlev/pbot)**EXPON) IF (icase .EQ. 2) THEN tempout(i,j) = zlev - !GOTO 333 ! CYCLE CYCLE END IF ELSE IF (cvcord .EQ. 'z') THEN @@ -402,7 +382,6 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& plev = pbot*(1. + USSALR/vt*(zbot - zlev))**EXPONI IF (icase .EQ. 1) THEN tempout(i,j) = plev - !GOTO 333 ! CYCLE CYCLE END IF END IF @@ -434,13 +413,27 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& END DO END DO +!$OMP END PARALLEL DO + + IF (log_errcnt > 0) THEN + errstat = ALGERR + WRITE(errmsg, *) "Pres=0. Unable to take log of 0." + RETURN + END IF + + IF (interp_errcnt > 0) THEN + errstat = ALGERR + WRITE(errmsg, *) "bad difference in vcp's" + RETURN + END IF - ! print *,"----done----",interp_levels(nreqlvs) +!$OMP PARALLEL DO DO j = 1,ns DO i = 1,ew dataout(i,j,nreqlvs) = tempout(i,j) END DO END DO +!$OMP END PARALLEL DO END DO !end for the nreqlvs From 8c66408920f89211383ed583f00b7f7570f0dfda Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Wed, 18 Oct 2017 16:27:53 -0600 Subject: [PATCH 04/43] Fix computational problem with new fortran DCAPECALC2D routine --- fortran/rip_cape.f90 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fortran/rip_cape.f90 b/fortran/rip_cape.f90 index 84fdb30..51c3916 100644 --- a/fortran/rip_cape.f90 +++ b/fortran/rip_cape.f90 @@ -683,7 +683,7 @@ SUBROUTINE DCAPECALC2D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& ! kg/kg (should range from 0.000 to 0.025) ! -!$OMP PARALLEL DO +!$OMP PARALLEL DO COLLAPSE(3) DO j = 1,mjy DO i = 1,mix DO k = 1,mkzh @@ -731,7 +731,8 @@ SUBROUTINE DCAPECALC2D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& DO k = 1, mkzh IF (ght_new(k,i,j)-ter(i,j) .LT. 3000.d0) THEN tlcl = TLCLC1 / (LOG(tmk_new(k,i,j)**TLCLC2/& - (MAX(qvp_new(k,i,j), 1.d-15)*prs_new(k,i,j)/(EPS+MAX(qvp_new(k,i,j), 1.d-15))))-TLCLC3)+TLCLC4 + (MAX(qvp_new(k,i,j), 1.d-15)*prs_new(k,i,j)/(EPS+MAX(qvp_new(k,i,j), 1.d-15))))-TLCLC3)+& + TLCLC4 eth_temp(k) = tmk_new(k,i,j) * (1000.d0/prs_new(k,i,j))**& (GAMMA*(1.d0 + GAMMAMD*(MAX(qvp_new(k,i,j), 1.d-15))))*& EXP((THTECON1/tlcl - THTECON2)*(MAX(qvp_new(k,i,j), 1.d-15))*& From 0ee9ec6c89562ea7711a64e1d2cb00e367bd6e90 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Fri, 20 Oct 2017 16:25:00 -0600 Subject: [PATCH 05/43] vectorization improvements and openmp added --- fortran/wrf_user.f90 | 325 +++++++++++++++++++++++++--------------- fortran/wrf_vinterp.f90 | 24 +-- 2 files changed, 216 insertions(+), 133 deletions(-) diff --git a/fortran/wrf_user.f90 b/fortran/wrf_user.f90 index 4bb7297..28adadd 100644 --- a/fortran/wrf_user.f90 +++ b/fortran/wrf_user.f90 @@ -16,13 +16,15 @@ SUBROUTINE DCOMPUTEPI(pi, pressure, nx, ny, nz) !REAL(KIND=8), PARAMETER :: P1000MB=100000.D0, R_D=287.D0, CP=7.D0*R_D/2.D0 + !$OMP PARALLEL DO COLLAPSE(3) DO k = 1,nz - DO j = 1,ny - DO i = 1,nx - pi(i,j,k) = (pressure(i,j,k)/P1000MB)**(RD/CP) - END DO - END DO + DO j = 1,ny + DO i = 1,nx + pi(i,j,k) = (pressure(i,j,k)/P1000MB)**(RD/CP) + END DO + END DO END DO + !$OMP END PARALLEL DO END SUBROUTINE DCOMPUTEPI @@ -37,7 +39,7 @@ SUBROUTINE DCOMPUTETK(tk, pressure, theta, nx) !f2py intent(in,out) :: tk INTEGER, INTENT(IN) :: nx - REAL(KIND=8) :: pi + !REAL(KIND=8) :: pi REAL(KIND=8), DIMENSION(nx), INTENT(IN) :: pressure REAL(KIND=8), DIMENSION(nx), INTENT(IN) :: theta REAL(KIND=8), DIMENSION(nx), INTENT(OUT) :: tk @@ -48,10 +50,13 @@ SUBROUTINE DCOMPUTETK(tk, pressure, theta, nx) !REAL(KIND=8), PARAMETER :: P1000MB=100000.D0, RD=287.D0, CP=7.D0*RD/2.D0 + !$OMP PARALLEL DO DO i = 1,nx - pi = (pressure(i)/P1000MB)**(RD/CP) - tk(i) = pi*theta(i) + !pi = (pressure(i)/P1000MB)**(RD/CP) + !tk(i) = pi * theta(i) + tk(i) = (pressure(i)/P1000MB)**(RD/CP) * theta(i) END DO + !$OMP END PARALLEL DO RETURN @@ -76,9 +81,7 @@ SUBROUTINE DINTERP3DZ(data3d, out2d, zdata, desiredloc, nx, ny, nz, missingval) INTEGER :: i,j,kp,ip,im LOGICAL :: dointerp - REAL(KIND=8) :: height,w1,w2 - - height = desiredloc + REAL(KIND=8) :: w1,w2 ! does vertical coordinate increase or decrease with increasing k? ! set offset appropriately @@ -90,6 +93,8 @@ SUBROUTINE DINTERP3DZ(data3d, out2d, zdata, desiredloc, nx, ny, nz, missingval) im = 0 END IF + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i,j,kp,dointerp,w1,w2) & + !$OMP FIRSTPRIVATE(ip,im) DO i = 1,nx DO j = 1,ny ! Initialize to missing. Was initially hard-coded to -999999. @@ -98,17 +103,17 @@ SUBROUTINE DINTERP3DZ(data3d, out2d, zdata, desiredloc, nx, ny, nz, missingval) kp = nz DO WHILE ((.NOT. dointerp) .AND. (kp >= 2)) - IF (((zdata(i,j,kp-im) < height) .AND. (zdata(i,j,kp-ip) > height))) THEN - w2 = (height - zdata(i,j,kp-im))/(zdata(i,j,kp-ip) - zdata(i,j,kp-im)) + IF (((zdata(i,j,kp-im) < desiredloc) .AND. (zdata(i,j,kp-ip) > desiredloc))) THEN + w2 = (desiredloc - zdata(i,j,kp-im))/(zdata(i,j,kp-ip) - zdata(i,j,kp-im)) w1 = 1.D0 - w2 out2d(i,j) = w1*data3d(i,j,kp-im) + w2*data3d(i,j,kp-ip) dointerp = .TRUE. END IF kp = kp - 1 END DO - END DO END DO + !$OMP END PARALLEL DO RETURN @@ -195,20 +200,22 @@ SUBROUTINE DINTERP2DXY(v3d, v2d, xy, nx, ny, nz, nxy) INTEGER :: i, j, k, ij REAL(KIND=8) :: w11, w12, w21, w22, wx, wy + !$OMP PARALLEL DO PRIVATE(i,j,k,ij,w11,w12,w21,w22,wx,wy) DO ij = 1,nxy - i = MAX(1,MIN(nx-1,INT(xy(1,ij)+1))) - j = MAX(1,MIN(ny-1,INT(xy(2,ij)+1))) - wx = DBLE(i+1) - (xy(1,ij)+1) - wy = DBLE(j+1) - (xy(2,ij)+1) - w11 = wx*wy - w21 = (1.D0-wx)*wy - w12 = wx*(1.D0-wy) - w22 = (1.D0-wx)* (1.D0-wy) - DO k = 1,nz - v2d(ij,k) = w11*v3d(i,j,k) + w21*v3d(i+1,j,k) + & - w12*v3d(i,j+1,k) + w22*v3d(i+1,j+1,k) - END DO + i = MAX(1,MIN(nx-1,INT(xy(1,ij)+1))) + j = MAX(1,MIN(ny-1,INT(xy(2,ij)+1))) + wx = DBLE(i+1) - (xy(1,ij)+1) + wy = DBLE(j+1) - (xy(2,ij)+1) + w11 = wx*wy + w21 = (1.D0-wx)*wy + w12 = wx*(1.D0-wy) + w22 = (1.D0-wx)* (1.D0-wy) + DO k = 1,nz + v2d(ij,k) = w11*v3d(i,j,k) + w21*v3d(i+1,j,k) + & + w12*v3d(i,j+1,k) + w22*v3d(i+1,j+1,k) + END DO END DO + !$OMP END PARALLEL DO RETURN @@ -241,27 +248,29 @@ SUBROUTINE DINTERP1D(v_in, v_out, z_in, z_out, vmsg, nz_in, nz_out) ip = 0 im = 1 IF (z_in(1) .GT. z_in(nz_in)) THEN - ip = 1 - im = 0 + ip = 1 + im = 0 END IF + !$OMP PARALLEL DO PRIVATE(kp, k, interp, height, w1, w2) FIRSTPRIVATE(ip, im) DO k = 1,nz_out - v_out(k) = vmsg - - interp = .FALSE. - kp = nz_in - height = z_out(k) - - DO WHILE ((.NOT. interp) .AND. (kp .GE. 2)) - IF (((z_in(kp-im) .LE. height) .AND. (z_in(kp-ip) .GT. height))) THEN - w2 = (height - z_in(kp-im))/(z_in(kp-ip) - z_in(kp-im)) - w1 = 1.D0 - w2 - v_out(k) = w1*v_in(kp-im) + w2*v_in(kp-ip) - interp = .TRUE. - END IF - kp = kp - 1 - END DO + v_out(k) = vmsg + + interp = .FALSE. + kp = nz_in + height = z_out(k) + + DO WHILE ((.NOT. interp) .AND. (kp .GE. 2)) + IF (((z_in(kp-im) .LE. height) .AND. (z_in(kp-ip) .GT. height))) THEN + w2 = (height - z_in(kp-im))/(z_in(kp-ip) - z_in(kp-im)) + w1 = 1.D0 - w2 + v_out(k) = w1*v_in(kp-im) + w2*v_in(kp-ip) + interp = .TRUE. + END IF + kp = kp - 1 + END DO END DO + !$OMP END PARALLEL DO RETURN @@ -316,10 +325,11 @@ SUBROUTINE DCOMPUTESEAPRS(nx, ny, nz, z, t, p, q, sea_level_pressure, & INTEGER :: i, j, k INTEGER :: klo, khi + INTEGER :: errcnt - REAL(KIND=8) :: plo, phi, tlo, thi, zlo, zhi - REAL(KIND=8) :: p_at_pconst, t_at_pconst, z_at_pconst - REAL(KIND=8) :: z_half_lowest + !REAL(KIND=8) :: plo, phi, tlo, thi, zlo, zhi + !REAL(KIND=8) :: p_at_pconst, t_at_pconst, z_at_pconst + !REAL(KIND=8) :: z_half_lowest LOGICAL :: l1, l2, l3, found @@ -329,7 +339,9 @@ SUBROUTINE DCOMPUTESEAPRS(nx, ny, nz, z, t, p, q, sea_level_pressure, & ! heating cycle in the pressure field. errstat = 0 + errcnt = 0 + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i,j,k,found) REDUCTION(+:errcnt) DO j = 1,ny DO i = 1,nx level(i,j) = -1 @@ -345,22 +357,21 @@ SUBROUTINE DCOMPUTESEAPRS(nx, ny, nz, z, t, p, q, sea_level_pressure, & END DO IF (level(i,j) == -1) THEN - errstat = ALGERR - errmsg = "Error in finding 100 hPa up" - RETURN - - !PRINT '(A,I4,A)','Troubles finding level ', NINT(PCONST)/100,' above ground.' - !PRINT '(A,I4,A,I4,A)','Problems first occur at (',I,',',J,')' - !PRINT '(A,F6.1,A)','Surface pressure = ',p(i,j,1)/100,' hPa.' - !STOP 'Error in finding 100 hPa up' - + errcnt = errcnt + 1 END IF END DO END DO + !$OMP END PARALLEL DO + + IF (errcnt > 0) THEN + errstat = ALGERR + errmsg = "Error in finding 100 hPa up" + RETURN + END IF ! Get temperature PCONST Pa above surface. Use this to extrapolate ! the temperature at the surface and down to sea level. - + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i,j,klo,khi) REDUCTION(+:errcnt) DO j = 1,ny DO i = 1,nx @@ -368,39 +379,51 @@ SUBROUTINE DCOMPUTESEAPRS(nx, ny, nz, z, t, p, q, sea_level_pressure, & khi = MIN(klo+1, nz-1) IF (klo == khi) THEN - errstat = ALGERR - errmsg = "Error trapping levels" - RETURN - - !PRINT '(A)','Trapping levels are weird.' - !PRINT '(A,I3,A,I3,A)','klo = ',klo,', khi = ',khi,': and they should not be equal.' - !STOP 'Error trapping levels' + errcnt = errcnt + 1 END IF - plo = p(i,j,klo) - phi = p(i,j,khi) - tlo = t(i,j,klo)*(1.D0 + 0.608D0*q(i,j,klo)) - thi = t(i,j,khi)*(1.D0 + 0.608D0*q(i,j,khi)) - ! zlo = zetahalf(klo)/ztop*(ztop-terrain(i,j))+terrain(i,j) - ! zhi = zetahalf(khi)/ztop*(ztop-terrain(i,j))+terrain(i,j) - zlo = z(i,j,klo) - zhi = z(i,j,khi) - - p_at_pconst = p(i,j,1) - PCONST - t_at_pconst = thi - (thi-tlo)*LOG(p_at_pconst/phi)*LOG(plo/phi) - z_at_pconst = zhi - (zhi-zlo)*LOG(p_at_pconst/phi)*LOG(plo/phi) - - t_surf(i,j) = t_at_pconst * (p(i,j,1)/p_at_pconst)**(USSALR*RD/G) - t_sea_level(i,j) = t_at_pconst + USSALR*z_at_pconst - + ! This is the readable version of the code below. Don't delete this! + !plo = p(i,j,klo) + !phi = p(i,j,khi) + !tlo = t(i,j,klo)*(1.D0 + 0.608D0*q(i,j,klo)) + !thi = t(i,j,khi)*(1.D0 + 0.608D0*q(i,j,khi)) + !zlo = z(i,j,klo) + !zhi = z(i,j,khi) + !p_at_pconst = p(i,j,1) - PCONST + !t_at_pconst = thi - (thi-tlo)*LOG(p_at_pconst/phi)*LOG(plo/phi) + !z_at_pconst = zhi - (zhi-zlo)*LOG(p_at_pconst/phi)*LOG(plo/phi) + ! + !t_surf(i,j) = t_at_pconst * (p(i,j,1)/p_at_pconst)**(USSALR*RD/G) + !t_sea_level(i,j) = t_at_pconst + USSALR*z_at_pconst + + ! The same code as above with temporaries removed to improve vectorization + t_surf(i,j) = ((t(i,j,khi)*(1.D0 + 0.608D0*q(i,j,khi))) - & + ((t(i,j,khi)*(1.D0 + 0.608D0*q(i,j,khi)))-(t(i,j,klo)*& + (1.D0 + 0.608D0*q(i,j,klo))))*LOG((p(i,j,1) - PCONST)/p(i,j,khi))& + *LOG(p(i,j,klo)/p(i,j,khi))) * (p(i,j,1)/(p(i,j,1) - PCONST))**(USSALR*RD/G) + + t_sea_level(i,j) = t(i,j,khi)*(1.D0 + 0.608D0*q(i,j,khi)) - & + ((t(i,j,khi)*(1.D0 + 0.608D0*q(i,j,khi)))-(t(i,j,klo)*& + (1.D0 + 0.608D0*q(i,j,klo))))*LOG((p(i,j,1) - PCONST)/& + p(i,j,khi))*LOG(p(i,j,klo)/p(i,j,khi)) + & + USSALR*(z(i,j,khi) - (z(i,j,khi)-z(i,j,klo))*& + LOG((p(i,j,1) - PCONST)/p(i,j,khi))*LOG(p(i,j,klo)/p(i,j,khi))) END DO END DO + !$OMP END PARALLEL DO + + IF (errcnt > 0) THEN + errstat = ALGERR + errmsg = "Error trapping levels" + RETURN + END IF ! If we follow a traditional computation, there is a correction to the ! sea level temperature if both the surface and sea level ! temperatures are *too* hot. IF (ridiculous_mm5_test) THEN + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(l1,l2,l3) DO j = 1,ny DO i = 1,nx l1 = t_sea_level(i,j) < TC @@ -413,21 +436,23 @@ SUBROUTINE DCOMPUTESEAPRS(nx, ny, nz, z, t, p, q, sea_level_pressure, & END IF END DO END DO + !$OMP END PARALLEL DO END IF ! The grand finale: ta da! + !$OMP PARALLEL DO COLLAPSE(2) DO j = 1,ny DO i = 1,nx - ! z_half_lowest=zetahalf(1)/ztop*(ztop-terrain(i,j))+terrain(i,j) - z_half_lowest = z(i,j,1) + !z_half_lowest = z(i,j,1) ! Convert to hPa in this step, by multiplying by 0.01. The original ! Fortran routine didn't do this, but the NCL script that called it ! did, so we moved it here. - sea_level_pressure(i,j) = 0.01*(p(i,j,1)*EXP((2.D0*G*z_half_lowest)/& + sea_level_pressure(i,j) = 0.01*(p(i,j,1)*EXP((2.D0*G*z(i,j,1))/& (RD*(t_sea_level(i,j) + t_surf(i,j))))) END DO END DO + !$OMP END PARALLEL DO ! PRINT *,'sea pres input at weird location i=20,j=1,k=1' ! PRINT *,'t=',t(20,1,1),t(20,2,1),t(20,3,1) @@ -464,12 +489,15 @@ SUBROUTINE DFILTER2D(a, b, nx, ny, it, missing) INTEGER :: i, j, iter DO iter=1,it + !$OMP PARALLEL DO COLLAPSE(2) DO j=1,ny DO i = 1,nx b(i,j) = a(i,j) END DO END DO + !$OMP END PARALLEL DO + !$OMP PARALLEL DO COLLAPSE(2) DO j=2,ny-1 DO i=1,nx IF (b(i,j-1) .EQ. missing .OR. b(i,j) .EQ. missing .OR. & @@ -480,7 +508,9 @@ SUBROUTINE DFILTER2D(a, b, nx, ny, it, missing) END IF END DO END DO + !$OMP END PARALLEL DO + !$OMP PARALLEL DO COLLAPSE(2) DO j=1,ny DO i=2,nx-1 IF (b(i-1,j) .EQ. missing .OR. b(i,j) .EQ. missing .OR. & @@ -491,6 +521,7 @@ SUBROUTINE DFILTER2D(a, b, nx, ny, it, missing) END IF END DO END DO + !$OMP END PARALLEL DO ! do j=1,ny ! do i=1,nx ! b(i,j) = a(i,j) @@ -534,12 +565,15 @@ SUBROUTINE FILTER2D(a, b, nx, ny, it, missing) INTEGER :: i, j, iter DO iter=1,it + !$OMP PARALLEL DO COLLAPSE(2) DO j=1,ny DO i = 1,nx b(i,j) = a(i,j) END DO END DO + !$OMP END PARALLEL DO + !$OMP PARALLEL DO COLLAPSE(2) DO j=2,ny-1 DO i=1,nx IF (b(i,j-1) .EQ. missing .OR. b(i,j) .EQ. missing .OR. & @@ -550,7 +584,9 @@ SUBROUTINE FILTER2D(a, b, nx, ny, it, missing) END IF END DO END DO + !$OMP END PARALLEL DO + !$OMP PARALLEL DO COLLAPSE(2) DO j=1,ny DO i=2,nx-1 IF (b(i-1,j) .EQ. missing .OR. b(i,j) .EQ. missing .OR. & @@ -561,6 +597,7 @@ SUBROUTINE FILTER2D(a, b, nx, ny, it, missing) END IF END DO END DO + !$OMP END PARALLEL DO ! do j=1,ny ! do i=1,nx ! b(i,j) = a(i,j) @@ -601,6 +638,7 @@ SUBROUTINE DCOMPUTERH(qv, p, t, rh, nx) INTEGER :: i REAL(KIND=8) :: qvs,es,pressure,temperature + !$OMP PARALLEL DO PRIVATE(qvs, es, pressure, temperature) DO i = 1,nx pressure = p(i) temperature = t(i) @@ -612,6 +650,7 @@ SUBROUTINE DCOMPUTERH(qv, p, t, rh, nx) ! rh(i) = 100.*qv(i)/qvs rh(i) = 100.D0*MAX(MIN(qv(i)/qvs, 1.0D0), 0.0D0) END DO + !$OMP END PARALLEL DO RETURN @@ -707,11 +746,12 @@ SUBROUTINE DCOMPUTEUVMET(u, v, uvmet, longca,longcb,flong,flat, & ! NCLEND INTEGER :: i,j - REAL(KIND=8) :: uk, vk + !REAL(KIND=8) :: uk, vk ! msg stands for missing value in this code ! WRITE (6,FMT=*) ' in compute_uvmet ',NX,NY,NXP1,NYP1,ISTAG + !$OMP PARALLEL DO COLLAPSE(2) DO j = 1,ny DO i = 1,nx @@ -733,34 +773,79 @@ SUBROUTINE DCOMPUTEUVMET(u, v, uvmet, longca,longcb,flong,flat, & END DO END DO + !$OMP END PARALLEL DO - ! WRITE (6,FMT=*) " computing velocities " - DO j = 1,ny - DO i = 1,nx - IF (istag.EQ.1) THEN - IF (is_msg_val .AND. (u(i,j) .EQ. umsg .OR. v(i,j) .EQ. vmsg & - .OR. u(i+1,j) .EQ. umsg .OR. v(i,j+1) .EQ. vmsg)) THEN - uvmet(i,j,1) = uvmetmsg - uvmet(i,j,2) = uvmetmsg - ELSE - uk = 0.5D0*(u(i,j) + u(i+1,j)) - vk = 0.5D0*(v(i,j) + v(i,j+1)) - uvmet(i,j,1) = vk*longcb(i,j) + uk*longca(i,j) - uvmet(i,j,2) = vk*longca(i,j) - uk*longcb(i,j) - END IF - ELSE - IF (is_msg_val .AND. (u(i,j) .EQ. umsg .OR. v(i,j) .EQ. vmsg)) THEN - uvmet(i,j,1) = uvmetmsg - uvmet(i,j,2) = uvmetmsg - ELSE - uk = u(i,j) - vk = v(i,j) - uvmet(i,j,1) = vk*longcb(i,j) + uk*longca(i,j) - uvmet(i,j,2) = vk*longca(i,j) - uk*longcb(i,j) - END IF - END IF - END DO - END DO + ! Note: Intentionally removed as many IF statements as possible from loops + ! to improve vectorization. + + IF (istag .EQ. 0) THEN ! Not staggered + IF (.NOT. is_msg_val) THEN ! No missing values used + !$OMP PARALLEL DO COLLAPSE(2) + DO j = 1,ny + DO i = 1,nx + uvmet(i,j,1) = v(i,j)*longcb(i,j) + u(i,j)*longca(i,j) + uvmet(i,j,2) = v(i,j)*longca(i,j) - u(i,j)*longcb(i,j) + END DO + END DO + !$OMP END PARALLEL DO + ELSE ! Missing values used + !$OMP PARALLEL DO COLLAPSE(2) + DO j = 1,ny + DO i = 1,nx + IF ((u(i,j) .NE. umsg .AND. v(i,j) .NE. vmsg)) THEN + uvmet(i,j,1) = v(i,j)*longcb(i,j) + u(i,j)*longca(i,j) + uvmet(i,j,2) = v(i,j)*longca(i,j) - u(i,j)*longcb(i,j) + ELSE + uvmet(i,j,1) = uvmetmsg + uvmet(i,j,2) = uvmetmsg + END IF + END DO + END DO + !$OMP END PARALLEL DO + END IF + ELSE ! Staggered + IF (.NOT. is_msg_val) THEN ! No missing values used + !$OMP PARALLEL DO COLLAPSE(2) + DO j = 1,ny + DO i = 1,nx + ! This is the more readable version. Do not delete. + !uk = 0.5D0*(u(i,j) + u(i+1,j)) + !vk = 0.5D0*(v(i,j) + v(i,j+1)) + !uvmet(i,j,1) = vk*longcb(i,j) + uk*longca(i,j) + !uvmet(i,j,2) = vk*longca(i,j) - uk*longcb(i,j) + + uvmet(i,j,1) = (0.5D0*(v(i,j) + v(i,j+1)))*longcb(i,j) + & + (0.5D0*(u(i,j) + u(i+1,j)))*longca(i,j) + uvmet(i,j,2) = (0.5D0*(v(i,j) + v(i,j+1)))*longca(i,j) - & + (0.5D0*(u(i,j) + u(i+1,j)))*longcb(i,j) + + END DO + END DO + !$OMP END PARALLEL DO + ELSE ! Missing values used + !$OMP PARALLEL DO COLLAPSE(2) + DO j = 1,ny + DO i = 1,nx + IF (u(i,j) .NE. umsg .AND. v(i,j) .NE. vmsg .AND. u(i+1,j) .NE. umsg .AND. v(i,j+1) .NE. vmsg) THEN + ! This is the more readable version. Do not delete. + !uk = 0.5D0*(u(i,j) + u(i+1,j)) + !vk = 0.5D0*(v(i,j) + v(i,j+1)) + !uvmet(i,j,1) = vk*longcb(i,j) + uk*longca(i,j) + !uvmet(i,j,2) = vk*longca(i,j) - uk*longcb(i,j) + + uvmet(i,j,1) = (0.5D0*(v(i,j) + v(i,j+1)))*longcb(i,j) + & + (0.5D0*(u(i,j) + u(i+1,j)))*longca(i,j) + uvmet(i,j,2) = (0.5D0*(v(i,j) + v(i,j+1)))*longca(i,j) - & + (0.5D0*(u(i,j) + u(i+1,j)))*longcb(i,j) + ELSE + uvmet(i,j,1) = uvmetmsg + uvmet(i,j,2) = uvmetmsg + END IF + END DO + END DO + !$OMP END PARALLEL DO + END IF + END IF RETURN @@ -791,15 +876,17 @@ SUBROUTINE DCOMPUTETD(td, pressure, qv_in, nx) INTEGER :: i + !$OMP PARALLEL DO PRIVATE(i,qv,tdc) DO i = 1,nx - qv = MAX(qv_in(i), 0.D0) - ! vapor pressure - tdc = qv*pressure(i)/(.622D0 + qv) + qv = MAX(qv_in(i), 0.D0) + ! vapor pressure + tdc = qv*pressure(i)/(.622D0 + qv) - ! avoid problems near zero - tdc = MAX(tdc, 0.001D0) - td(i) = (243.5D0*LOG(tdc) - 440.8D0)/(19.48D0 - LOG(tdc)) + ! avoid problems near zero + tdc = MAX(tdc, 0.001D0) + td(i) = (243.5D0*LOG(tdc) - 440.8D0)/(19.48D0 - LOG(tdc)) END DO + !$OMP END PARALLEL DO RETURN @@ -825,11 +912,7 @@ SUBROUTINE DCOMPUTEICLW(iclw, pressure, qc_in, nx, ny, nz) REAL(KIND=8), PARAMETER :: GG = 1000.D0/G INTEGER i,j,k - DO j = 1,ny - DO i = 1,nx - iclw(i,j) = 0.D0 - END DO - END DO + iclw = 0 DO j = 3,ny - 2 DO i = 3,nx - 2 diff --git a/fortran/wrf_vinterp.f90 b/fortran/wrf_vinterp.f90 index b7a5f64..ee1835f 100644 --- a/fortran/wrf_vinterp.f90 +++ b/fortran/wrf_vinterp.f90 @@ -12,7 +12,7 @@ SUBROUTINE wrf_monotonic(out, in, lvprs, cor, idir, delta, ew, ns, nz, icorsw) INTEGER, INTENT(IN) :: idir, ew, ns, nz, icorsw REAL(KIND=8), INTENT(IN) :: delta - REAL(KIND=8), DIMENSION(ew,ns,nz), INTENT(IN) :: in + REAL(KIND=8), DIMENSION(ew,ns,nz), INTENT(INOUT) :: in REAL(KIND=8), DIMENSION(ew,ns,nz), INTENT(OUT) :: out REAL(KIND=8), DIMENSION(ew,ns,nz), INTENT(IN) :: lvprs REAL(KIND=8), DIMENSION(ew,ns), INTENT(IN) :: cor @@ -24,6 +24,7 @@ SUBROUTINE wrf_monotonic(out, in, lvprs, cor, idir, delta, ew, ns, nz, icorsw) k300 = 1 ! removes the warning + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i, j, k, ripk) FIRSTPRIVATE(k300) DO j=1,ns DO i=1,ew IF (icorsw .EQ. 1 .AND. cor(i,j) .LT. 0.) THEN @@ -59,6 +60,7 @@ SUBROUTINE wrf_monotonic(out, in, lvprs, cor, idir, delta, ew, ns, nz, icorsw) END DO END DO END DO + !$OMP END PARALLEL DO RETURN @@ -125,7 +127,6 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& USE wrf_constants, ONLY : ALGERR, SCLHT, EXPON, EXPONI, GAMMA, GAMMAMD, TLCLC1, & TLCLC2, TLCLC3, TLCLC4, THTECON1, THTECON2, THTECON3, & CELKEL, EPS, USSALR - USE omp_lib IMPLICIT NONE @@ -207,16 +208,15 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& vlev = interp_levels(nreqlvs) END IF -!$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i, j, k, ifound, & -!$OMP ripk, vcp1, vcp0, valp0, valp1, tmpvlev, interp_errstat, & -!$OMP vclhsl, vctophsl, diff, isign, plhsl, zlhsl, ezlhsl, tlhsl, & -!$OMP zsurf, qvapor, psurf, psurfsm, ezsurf, plev, ezlev, zlev, & -!$OMP ptarget, dpmin, kupper, pbot, zbot, pratio, tbotextrap, & -!$OMP vt, tlev, gammam, e, tlcl) REDUCTION (+:log_errcnt, interp_errcnt) + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i, j, k, ifound, & + !$OMP ripk, vcp1, vcp0, valp0, valp1, tmpvlev, interp_errstat, & + !$OMP vclhsl, vctophsl, diff, isign, plhsl, zlhsl, ezlhsl, tlhsl, & + !$OMP zsurf, qvapor, psurf, psurfsm, ezsurf, plev, ezlev, zlev, & + !$OMP ptarget, dpmin, kupper, pbot, zbot, pratio, tbotextrap, & + !$OMP vt, tlev, gammam, e, tlcl) REDUCTION (+:log_errcnt, interp_errcnt) DO j=1,ns DO i=1,ew ! Get the interpolated value that is within the model domain - thd = omp_get_thread_num() ifound = 0 DO k = 1,nz-1 ripk = nz-k+1 @@ -413,7 +413,7 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& END DO END DO -!$OMP END PARALLEL DO + !$OMP END PARALLEL DO IF (log_errcnt > 0) THEN errstat = ALGERR @@ -427,13 +427,13 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& RETURN END IF -!$OMP PARALLEL DO + !$OMP PARALLEL DO DO j = 1,ns DO i = 1,ew dataout(i,j,nreqlvs) = tempout(i,j) END DO END DO -!$OMP END PARALLEL DO + !$OMP END PARALLEL DO END DO !end for the nreqlvs From 0c270c7e028b5475d8ec0bea901fb3e9867371c1 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Mon, 30 Oct 2017 16:52:05 -0600 Subject: [PATCH 06/43] Added more OpenMP Directives. Fixed serious bug with cloud fraction and implemented new behavior allowing users to select the vertical coordinate type and select their own cloud thresholds. Fixes #25 . --- fortran/calc_uh.f90 | 12 +++- fortran/eqthecalc.f90 | 5 +- fortran/rip_cape.f90 | 21 +++--- fortran/wrf_cloud_fracf.f90 | 134 +++++++++++++++++++++++++++++++----- fortran/wrffortran.pyf | 23 +++++-- src/wrf/cloudfrac.py | 84 ++++++++++++++++++++-- src/wrf/computation.py | 58 ++++++++++++++-- src/wrf/extension.py | 36 ++++++---- src/wrf/metadecorators.py | 70 +++++++++++++++---- src/wrf/routines.py | 3 +- src/wrf/specialdec.py | 46 ++++++------- test/comp_utest.py | 4 +- test/listBug.ncl | 12 ++++ test/utests.py | 6 +- 14 files changed, 412 insertions(+), 102 deletions(-) diff --git a/fortran/calc_uh.f90 b/fortran/calc_uh.f90 index a893189..b99a48e 100644 --- a/fortran/calc_uh.f90 +++ b/fortran/calc_uh.f90 @@ -61,21 +61,26 @@ SUBROUTINE DCALCUH(nx, ny, nz, nzp1, zp, mapfct, dx, dy, uhmnhgt, uhmxhgt, us, & twodx = 2.0*dx twody = 2.0*dy + !$OMP PARALLEL DO COLLAPSE(3) DO k=2,nz-2 DO j=2,ny-1 DO i=2,nx-1 - wavg = 0.5*(w(i,j,k)+w(i,j,k+1)) - tem1(i,j,k) = wavg*((vs(i+1,j,k) - vs(i-1,j,k))/(twodx*mapfct(i,j)) - & - (us(i,j+1,k) - us(i,j-1,k))/(twody*mapfct(i,j))) + !wavg = 0.5*(w(i,j,k)+w(i,j,k+1)) + tem1(i,j,k) = (0.5*(w(i,j,k)+w(i,j,k+1)))*((vs(i+1,j,k) - & + vs(i-1,j,k))/(twodx*mapfct(i,j)) - & + (us(i,j+1,k) - us(i,j-1,k))/(twody*mapfct(i,j))) tem2(i,j,k) = 0.5*(zp(i,j,k) + zp(i,j,k+1)) END DO END DO END DO + !$OMP END PARALLEL DO ! Integrate over depth uhminhgt to uhmxhgt AGL ! ! WRITE(6,'(a,f12.1,a,f12.1,a)') & ! 'Calculating UH from ',uhmnhgt,' to ',uhmxhgt,' m AGL' + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i, j, k, zbot, ztop, kbot, ktop, & + !$OMP wgtlw, wbot, wtop, wsum, wmean, sum, helbot, heltop) DO j=2,ny-2 DO i=2,nx-2 zbot = zp(i,j,2) + uhmnhgt @@ -142,6 +147,7 @@ SUBROUTINE DCALCUH(nx, ny, nz, nzp1, zp, mapfct, dx, dy, uhmnhgt, uhmxhgt, us, & END IF END DO END DO + !$OMP END PARALLEL DO uh = uh*1000. ! Scale according to Kain et al. (2008) diff --git a/fortran/eqthecalc.f90 b/fortran/eqthecalc.f90 index a4cb6ba..d805be9 100644 --- a/fortran/eqthecalc.f90 +++ b/fortran/eqthecalc.f90 @@ -32,6 +32,8 @@ SUBROUTINE DEQTHECALC(qvp, tmk, prs, eth, miy, mjx, mkzh) REAL(KIND=8) :: tlcl INTEGER :: i, j, k + ! Note: removing temporaries did not improve performance for this algorithm. + !$OMP PARALLEL DO COLLAPSE(3) PRIVATE(i,j,k,q,t,p,e,tlcl) DO k = 1,mkzh DO j = 1,mjx DO i = 1,miy @@ -40,11 +42,12 @@ SUBROUTINE DEQTHECALC(qvp, tmk, prs, eth, miy, mjx, mkzh) p = prs(i,j,k)/100. e = q*p/(EPS + q) tlcl = TLCLC1/(LOG(t**TLCLC2/e) - TLCLC3) + TLCLC4 - eth(i,j,k) = t*(1000.D0/p)**(GAMMA*(1.D0 + GAMMAMD*q))* & + eth(i,j,k) = tmk(i,j,k)*(1000.D0/p)**(GAMMA*(1.D0 + GAMMAMD*q))* & EXP((THTECON1/tlcl - THTECON2)*q*(1.D0 + THTECON3*q)) END DO END DO END DO + !$OMP END PARALLEL DO RETURN diff --git a/fortran/rip_cape.f90 b/fortran/rip_cape.f90 index 51c3916..c4429b4 100644 --- a/fortran/rip_cape.f90 +++ b/fortran/rip_cape.f90 @@ -353,7 +353,8 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& !CALL cpu_time(t1) !CALL OMP_SET_NUM_THREADS(16) -!$OMP PARALLEL DO + + !$OMP PARALLEL DO DO j = 1,mjy DO i = 1,mix DO k = 1,mkzh @@ -364,7 +365,7 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& END DO END DO END DO -!$OMP END PARALLEL DO + !$OMP END PARALLEL DO CALL DPFCALC(prs_new, sfp, prsf, mix, mjy, mkzh, ter_follow) @@ -377,12 +378,12 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& RETURN END IF -!$OMP PARALLEL DO COLLAPSE(2) PRIVATE(tlcl, ethpari, & -!$OMP zlcl, kk, ilcl, klcl, tmklift, tvenv, tvlift, ghtlift, & -!$OMP facden, tmkenv, qvpenv, eslift, qvplift, buoy, benamin, & -!$OMP benaccum, zrel, kmax, dz, elfound, & -!$OMP kel, klfc, & -!$OMP i,j,k,kpar) + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(tlcl, ethpari, & + !$OMP zlcl, kk, ilcl, klcl, tmklift, tvenv, tvlift, ghtlift, & + !$OMP facden, tmkenv, qvpenv, eslift, qvplift, buoy, benamin, & + !$OMP benaccum, zrel, kmax, dz, elfound, & + !$OMP kel, klfc, & + !$OMP i,j,k,kpar) DO j = 1,mjy DO i = 1,mix cape(i,j,1) = 0.d0 @@ -395,10 +396,10 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& ! (note, qvppari and tmkpari already calculated above for 2d case.) tlcl = TLCLC1/(LOG(tmk_new(kpar,i,j)**TLCLC2/(MAX(1.D-20,qvp_new(kpar,i,j)*prs_new(kpar,i,j)/ & - (EPS + qvp_new(kpar,i,j))))) - TLCLC3) + TLCLC4 + (EPS + qvp_new(kpar,i,j))))) - TLCLC3) + TLCLC4 ethpari = tmk_new(kpar,i,j)*(1000.D0/prs_new(kpar,i,j))**(GAMMA*(1.D0 + GAMMAMD*qvp_new(kpar,i,j)))* & - EXP((THTECON1/tlcl - THTECON2)*qvp_new(kpar,i,j)*(1.D0 + THTECON3*qvp_new(kpar,i,j))) + EXP((THTECON1/tlcl - THTECON2)*qvp_new(kpar,i,j)*(1.D0 + THTECON3*qvp_new(kpar,i,j))) zlcl = ght_new(kpar,i,j) + (tmk_new(kpar,i,j) - tlcl)/(G/CP * (1.D0 + CPMD*qvp_new(kpar,i,j))) diff --git a/fortran/wrf_cloud_fracf.f90 b/fortran/wrf_cloud_fracf.f90 index 61cbe4c..c925704 100644 --- a/fortran/wrf_cloud_fracf.f90 +++ b/fortran/wrf_cloud_fracf.f90 @@ -18,7 +18,11 @@ SUBROUTINE DCLOUDFRAC(pres, rh, lowc, midc, highc, nz, ns, ew) kchi = 0 kcmi = 0 kclo = 0 + lowc = 0 + midc = 0 + highc = 0 + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i, j, k, kchi, kcmi, kclo) DO j = 1,ns DO i = 1,ew DO k = 1,nz-1 @@ -27,30 +31,124 @@ SUBROUTINE DCLOUDFRAC(pres, rh, lowc, midc, highc, nz, ns, ew) IF ( pres(i,j,k) .GT. 45000. ) kchi=k END DO - DO k = 1,nz-1 - IF (k .GE. kclo .AND. k .LT. kcmi) THEN - lowc(i,j) = MAX(rh(i,j,k), lowc(i,j)) - ELSE IF (k .GE. kcmi .AND. k .LT. kchi) THEN ! mid cloud - midc(i,j) = MAX(rh(i,j,k), midc(i,j)) - ELSE if (k .GE. kchi) THEN ! high cloud - highc(i,j) = MAX(rh(i,j,k), highc(i,j)) - END IF - END DO + DO k = 1,nz-1 + IF (k .GE. kclo .AND. k .LT. kcmi) THEN + lowc(i,j) = MAX(rh(i,j,k), lowc(i,j)) + ELSE IF (k .GE. kcmi .AND. k .LT. kchi) THEN ! mid cloud + midc(i,j) = MAX(rh(i,j,k), midc(i,j)) + ELSE if (k .GE. kchi) THEN ! high cloud + highc(i,j) = MAX(rh(i,j,k), highc(i,j)) + END IF + END DO - lowc(i,j) = 4.0*lowc(i,j)/100. - 3.0 - midc(i,j) = 4.0*midc(i,j)/100. - 3.0 - highc(i,j) = 2.5*highc(i,j)/100. - 1.5 + lowc(i,j) = 4.0*lowc(i,j)/100. - 3.0 + midc(i,j) = 4.0*midc(i,j)/100. - 3.0 + highc(i,j) = 2.5*highc(i,j)/100. - 1.5 - lowc(i,j) = MIN(lowc(i,j), 1.0) - lowc(i,j) = MAX(lowc(i,j), 0.0) - midc(i,j) = MIN(midc(i,j), 1.0) - midc(i,j) = MAX(midc(i,j), 0.0) - highc(i,j) = MIN(highc(i,j), 1.0) - highc(i,j) = MAX(highc(i,j), 0.0) + lowc(i,j) = MIN(lowc(i,j), 1.0) + lowc(i,j) = MAX(lowc(i,j), 0.0) + midc(i,j) = MIN(midc(i,j), 1.0) + midc(i,j) = MAX(midc(i,j), 0.0) + highc(i,j) = MIN(highc(i,j), 1.0) + highc(i,j) = MAX(highc(i,j), 0.0) END DO END DO + !$OMP END PARALLEL DO RETURN END SUBROUTINE DCLOUDFRAC + + +! NCLFORTSTART +SUBROUTINE DCLOUDFRAC2(vert, rh, vert_inc_w_height, low_thresh, mid_thresh, & + high_thresh, msg, lowc, midc, highc, nz, ns, ew) + IMPLICIT NONE + + !f2py threadsafe + !f2py intent(in,out) :: lowc, midc, highc + + INTEGER nz, ns, ew + REAL(KIND=8), DIMENSION(ew, ns, nz), INTENT(IN) :: rh, vert + REAL(KIND=8), INTENT(IN) :: low_thresh, mid_thresh, high_thresh, msg + INTEGER, INTENT(IN) :: vert_inc_w_height + REAL(KIND=8), DIMENSION(ew, ns), INTENT(OUT) :: lowc, midc, highc + +! NCLEND + + INTEGER i, j, k, kstart, kend + INTEGER kchi, kcmi, kclo + + ! Initialize the output + lowc = 0 + midc = 0 + highc = 0 + + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i, j, k, kchi, kcmi, kclo) + DO j = 1,ns + DO i = 1,ew + ! A value of -1 means 'not found'. This is needed to handle + ! the mountains, where the level thresholds are below the lowest + ! model level. + kchi = -1 + kcmi = -1 + kclo = -1 + + IF (vert_inc_w_height .NE. 0) THEN ! Vert coord increase with height + DO k = 1,nz + IF (vert(i,j,k) .LT. low_thresh) kclo=k + IF (vert(i,j,k) .LT. mid_thresh) kcmi=k + IF (vert(i,j,k) .LT. high_thresh) kchi=k + END DO + ELSE ! Vert coord decrease with height + DO k = 1,nz + IF (vert(i,j,k) .GT. low_thresh) kclo=k + IF (vert(i,j,k) .GT. mid_thresh) kcmi=k + IF (vert(i,j,k) .GT. high_thresh) kchi=k + END DO + ENDIF + + DO k = 1,nz + IF (k .GE. kclo .AND. k .LT. kcmi) THEN + lowc(i,j) = MAX(rh(i,j,k), lowc(i,j)) + ELSE IF (k .GE. kcmi .AND. k .LT. kchi) THEN ! mid cloud + midc(i,j) = MAX(rh(i,j,k), midc(i,j)) + ELSE if (k .GE. kchi) THEN ! high cloud + highc(i,j) = MAX(rh(i,j,k), highc(i,j)) + END IF + END DO + + ! Only do this when a cloud threshold is in the model vertical + ! domain, otherwise it will be set to missing + IF (kclo .GE. 1) THEN + lowc(i,j) = 4.0*lowc(i,j)/100. - 3.0 + lowc(i,j) = MIN(lowc(i,j), 1.0) + lowc(i,j) = MAX(lowc(i,j), 0.0) + ELSE + lowc(i,j) = msg + END IF + + IF (kcmi .GE. 1) THEN + midc(i,j) = 4.0*midc(i,j)/100. - 3.0 + midc(i,j) = MIN(midc(i,j), 1.0) + midc(i,j) = MAX(midc(i,j), 0.0) + ELSE + midc(i,j) = msg + END IF + + IF (kchi .GE. 1) THEN + highc(i,j) = 2.5*highc(i,j)/100. - 1.5 + highc(i,j) = MIN(highc(i,j), 1.0) + highc(i,j) = MAX(highc(i,j), 0.0) + ELSE + highc(i,j) = msg + END IF + + END DO + END DO + !$OMP END PARALLEL DO + + RETURN + +END SUBROUTINE DCLOUDFRAC2 diff --git a/fortran/wrffortran.pyf b/fortran/wrffortran.pyf index 5a01ba5..41eecbb 100644 --- a/fortran/wrffortran.pyf +++ b/fortran/wrffortran.pyf @@ -123,6 +123,22 @@ python module _wrffortran ! in integer, optional,check(shape(pres,1)==ns),depend(pres) :: ns=shape(pres,1) integer, optional,check(shape(pres,0)==ew),depend(pres) :: ew=shape(pres,0) end subroutine dcloudfrac + subroutine dcloudfrac2(vert,rh,vert_inc_w_height,low_thresh,mid_thresh,high_thresh,msg,lowc,midc,highc,nz,ns,ew) ! in :_wrffortran:wrf_cloud_fracf.f90 + threadsafe + real(kind=8) dimension(ew,ns,nz),intent(in) :: vert + real(kind=8) dimension(ew,ns,nz),intent(in),depend(ew,ns,nz) :: rh + integer intent(in) :: vert_inc_w_height + real(kind=8) intent(in) :: low_thresh + real(kind=8) intent(in) :: mid_thresh + real(kind=8) intent(in) :: high_thresh + real(kind=8) intent(in) :: msg + real(kind=8) dimension(ew,ns),intent(out,in),depend(ew,ns) :: lowc + real(kind=8) dimension(ew,ns),intent(out,in),depend(ew,ns) :: midc + real(kind=8) dimension(ew,ns),intent(out,in),depend(ew,ns) :: highc + integer, optional,check(shape(vert,2)==nz),depend(vert) :: nz=shape(vert,2) + integer, optional,check(shape(vert,1)==ns),depend(vert) :: ns=shape(vert,1) + integer, optional,check(shape(vert,0)==ew),depend(vert) :: ew=shape(vert,0) + end subroutine dcloudfrac2 module wrf_constants ! in :_wrffortran:wrf_constants.f90 real(kind=8), parameter,optional :: wrf_earth_radius=6370000.d0 real(kind=8), parameter,optional :: rhowat=1000.d0 @@ -527,8 +543,8 @@ python module _wrffortran ! in threadsafe real(kind=8) dimension(ew,ns,nz),intent(out,in) :: out real(kind=8) dimension(ew,ns,nz),intent(inout),depend(ew,ns,nz) :: in - real(kind=8) dimension(ew,ns,nz),depend(ew,ns,nz) :: lvprs - real(kind=8) dimension(ew,ns),depend(ew,ns) :: cor + real(kind=8) dimension(ew,ns,nz),intent(in),depend(ew,ns,nz) :: lvprs + real(kind=8) dimension(ew,ns),intent(in),depend(ew,ns) :: cor integer intent(in) :: idir real(kind=8) intent(in) :: delta integer, optional,intent(in),check(shape(out,0)==ew),depend(out) :: ew=shape(out,0) @@ -536,7 +552,7 @@ python module _wrffortran ! in integer, optional,intent(in),check(shape(out,2)==nz),depend(out) :: nz=shape(out,2) integer intent(in) :: icorsw end subroutine wrf_monotonic - function wrf_intrp_value(wvalp0,wvalp1,vlev,vcp0,vcp1,icase,errstat,errmsg) ! in :_wrffortran:wrf_vinterp.f90 + function wrf_intrp_value(wvalp0,wvalp1,vlev,vcp0,vcp1,icase,errstat) ! in :_wrffortran:wrf_vinterp.f90 threadsafe use wrf_constants, only: sclht,algerr real(kind=8) intent(in) :: wvalp0 @@ -546,7 +562,6 @@ python module _wrffortran ! in real(kind=8) intent(in) :: vcp1 integer intent(in) :: icase integer intent(inout) :: errstat - character*(*) intent(inout) :: errmsg real(kind=8) :: wrf_intrp_value end function wrf_intrp_value subroutine wrf_vintrp(datain,dataout,pres,tk,qvp,ght,terrain,sfp,smsfp,vcarray,interp_levels,numlevels,icase,ew,ns,nz,extrap,vcor,logp,rmsg,errstat,errmsg) ! in :_wrffortran:wrf_vinterp.f90 diff --git a/src/wrf/cloudfrac.py b/src/wrf/cloudfrac.py index 75b500f..38805c6 100644 --- a/src/wrf/cloudfrac.py +++ b/src/wrf/cloudfrac.py @@ -5,11 +5,16 @@ from .constants import Constants from .extension import _tk, _rh, _cloudfrac from .metadecorators import set_cloudfrac_metadata from .util import extract_vars +from .geoht import _get_geoht + +import numpy.ma as ma @set_cloudfrac_metadata() def get_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True, - cache=None, meta=True, _key=None): - """Return the cloud fraction. + cache=None, meta=True, _key=None, + vert_type="pres", low_thresh=None, mid_thresh=None, + high_thresh=None, missing=Constants.DEFAULT_FILL): + """Return the cloud fraction for low, mid, and high level clouds. The leftmost dimension of the returned array represents three different quantities: @@ -18,6 +23,33 @@ def get_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True, - return_val[1,...] will contain MID level cloud fraction - return_val[2,...] will contain HIGH level cloud fraction + For backwards compatibility, the default vertical coordinate type is + pressure, with default cloud levels defined as: + + 97000 Pa <= low_cloud < 80000 Pa + 80000 Pa <= mid_cloud < 45000 Pa + 45000 Pa <= high_cloud + + If the vertical coordinate type is 'height_agl' or 'height_msl', the + default cloud levels are defined as: + + 300 m <= low_cloud < 2000 m + 2000 m <= mid_cloud < 6000 m + 6000 m <= high_cloud + + Note that the default low cloud levels are chosen to + exclude clouds near the surface (fog). If you want fog included, set + *low_thresh* to ~99500 Pa if *vert_type* is set to 'pres', or 15 m if using + 'height_msl' or 'height_agl'. Keep in mind that the lowest mass grid points + are slightly above the ground, and in order to find clouds, the + *low_thresh* needs to be set to values that are slightly greater than + (less than) the lowest height (pressure) values. + + When using 'pres' or 'height_agl' for *vert_type*, there is a possibility + that the lowest WRF level will be higher than the low_cloud or mid_cloud + threshold, particularly for mountainous regions. When this happens, a + fill value will be used in the output. + This functions extracts the necessary variables from the NetCDF file object in order to perform the calculation. @@ -59,7 +91,27 @@ def get_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True, _key (:obj:`int`, optional): A caching key. This is used for internal purposes only. Default is None. - + + vert_type (:obj:`str`, optional): The type of vertical coordinate used + to determine cloud type thresholds. Must be 'pres', 'height_msl', + or 'height_agl'. For backwards compatibility, the default + is 'pres'. + + low_thresh (:obj:`float`, optional): The lower bound for what is + considered a low cloud. If *vert_type* is 'pres', the default is + 97000 Pa. If *vert_type* is 'height_agl' or 'height_msl', then the + default is 300 m. + + mid_thresh (:obj:`float`, optional): The lower bound for what is + considered a mid level cloud. If *vert_type* is 'pres', the + default is 80000 Pa. If *vert_type* is 'height_agl' or + 'height_msl', then the default is 2000 m. + + high_thresh (:obj:`float`, optional): The lower bound for what is + considered a high level cloud. If *vert_type* is 'pres', the + default is 45000 Pa. If *vert_type* is 'height_agl' or + 'height_msl', then the default is 6000 m. + Returns: :class:`xarray.DataArray` or :class:`numpy.ndarray`: The cloud fraction array whose leftmost dimension is 3 (LOW=0, MID=1, @@ -84,5 +136,29 @@ def get_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True, tk = _tk(full_p, full_t) rh = _rh(qv, full_p, tk) + + if vert_type.lower() == "pres" or vert_type.lower() == "pressure": + v_coord = full_p + _low_thresh = 97000. if low_thresh is None else low_thresh + _mid_thresh = 80000. if mid_thresh is None else mid_thresh + _high_thresh = 45000. if high_thresh is None else high_thresh + vert_inc_w_height = 0 + elif (vert_type.lower() == "height_msl" + or vert_type.lower() == "height_agl"): + is_msl = vert_type.lower() == "height_msl" + v_coord = _get_geoht(wrfin, timeidx, method, squeeze, + cache, meta=False, _key=_key, height=True, + msl=is_msl) + _low_thresh = 300. if low_thresh is None else low_thresh + _mid_thresh = 2000. if mid_thresh is None else mid_thresh + _high_thresh = 6000. if high_thresh is None else high_thresh + vert_inc_w_height = 1 + else: + raise ValueError("'vert_type' must be 'pres', 'height_msl', " + "or 'height_agl'") + + cfrac = _cloudfrac(v_coord, rh, vert_inc_w_height, + _low_thresh, _mid_thresh, _high_thresh, missing) - return _cloudfrac(full_p, rh) + return ma.masked_values(cfrac, missing) + diff --git a/src/wrf/computation.py b/src/wrf/computation.py index 81e37dd..1a0e1c3 100644 --- a/src/wrf/computation.py +++ b/src/wrf/computation.py @@ -962,8 +962,9 @@ def cape_3d(pres_hpa, tkel, qv, height, terrain, psfc_hpa, ter_follow, return ma.masked_values(cape_cin, missing) -@set_cloudfrac_alg_metadata(copyarg="pres") -def cloudfrac(pres, relh, meta=True): +@set_cloudfrac_alg_metadata(copyarg="vert") +def cloudfrac(vert, relh, vert_inc_w_height, low_thresh, mid_thresh, + high_thresh, missing=Constants.DEFAULT_FILL, meta=True): """Return the cloud fraction. The leftmost dimension of the returned array represents three different @@ -972,6 +973,33 @@ def cloudfrac(pres, relh, meta=True): - return_val[0,...] will contain LOW level cloud fraction - return_val[1,...] will contain MID level cloud fraction - return_val[2,...] will contain HIGH level cloud fraction + + For backwards compatibility, the default vertical coordinate type is + pressure, with default cloud levels defined as: + + 97000 Pa <= low_cloud < 80000 Pa + 80000 Pa <= mid_cloud < 45000 Pa + 45000 Pa <= high_cloud + + If the vertical coordinate type is 'height_agl' or 'height_msl', the + default cloud levels are defined as: + + 300 m <= low_cloud < 2000 m + 2000 m <= mid_cloud < 6000 m + 6000 m <= high_cloud + + Note that the default low cloud levels are chosen to + exclude clouds near the surface (fog). If you want fog included, set + *low_thresh* to ~99500 Pa if *vert_type* is set to 'pres', or 15 m if using + 'height_msl' or 'height_agl'. Keep in mind that the lowest mass grid points + are slightly above the ground, and in order to find clouds, the + *low_thresh* needs to be set to values that are slightly greater than + (less than) the lowest height (pressure) values. + + When using 'pres' or 'height_agl' for *vert_type*, there is a possibility + that the lowest WRF level will be higher than the low_cloud or mid_cloud + threshold, particularly for mountainous regions. When this happens, a + fill value will be used in the output. This is the raw computational algorithm and does not extract any variables from WRF output files. Use :meth:`wrf.getvar` to both extract and compute @@ -979,8 +1007,8 @@ def cloudfrac(pres, relh, meta=True): Args: - pres (:class:`xarray.DataArray` or :class:`numpy.ndarray`): Full - pressure (perturbation + base state pressure) in [Pa], with the + vert (:class:`xarray.DataArray` or :class:`numpy.ndarray`): The + vertical coordinate variable (usually pressure or height) with the rightmost dimensions as bottom_top x south_north x west_east Note: @@ -990,8 +1018,21 @@ def cloudfrac(pres, relh, meta=True): dimension names to the output. Otherwise, default names will be used. - relh (:class:`xarray.DataArray` or :class:`numpy.ndarray`) Relative - humidity with the same dimensionality as *pres* + relh (:class:`xarray.DataArray` or :class:`numpy.ndarray`): Relative + humidity with the same dimensionality as *vert* + + vert_inc_w_height (:obj:`int`): Set to 1 if the vertical coordinate + values increase with height (height values). Set to 0 if the + vertical coordinate values decrease with height (pressure values). + + low_thresh (:obj:`float`): The bottom vertical threshold for what is + considered a low cloud. + + mid_thresh (:obj:`float`): The bottom vertical threshold for what is + considered a mid level cloud. + + high_thresh (:obj:`float`): The bottom vertical threshold for what is + considered a high cloud. meta (:obj:`bool`): Set to False to disable metadata and return :class:`numpy.ndarray` instead of @@ -1016,7 +1057,10 @@ def cloudfrac(pres, relh, meta=True): :meth:`wrf.getvar`, :meth:`wrf.rh` """ - return _cloudfrac(pres, relh) + cfrac = _cloudfrac(vert, relh, vert_inc_w_height, low_thresh, mid_thresh, + high_thresh, missing) + + return ma.masked_values(cfrac, missing) @set_alg_metadata(2, "pres_hpa", refvarndims=3, diff --git a/src/wrf/extension.py b/src/wrf/extension.py index 90bb8cc..777e29d 100755 --- a/src/wrf/extension.py +++ b/src/wrf/extension.py @@ -7,7 +7,7 @@ from .constants import Constants from ._wrffortran import (dcomputetk, dinterp3dz, dinterp2dxy, dinterp1d, dcomputeseaprs, dfilter2d, dcomputerh, dcomputeuvmet, - dcomputetd, dcapecalc2d, dcapecalc3d, dcloudfrac, + dcomputetd, dcapecalc2d, dcapecalc3d, dcloudfrac2, wrfcttcalc, calcdbz, dcalrelhl, dcalcuh, dcomputepv, dcomputeabsvort, dlltoij, dijtoll, deqthecalc, omgcalc, virtual_temp, wetbulbcalc, dcomputepw, @@ -624,28 +624,34 @@ def _cape(p_hpa, tk, qv, ht, ter, sfp, missing, i3dflag, ter_follow, @check_args(0, 3, (3,3)) @cloudfrac_left_iter() -@cast_type(arg_idxs=(0, 1), outviews=("lowview", "medview", "highview")) -@extract_and_transpose(outviews=("lowview", "medview", "hightview")) -def _cloudfrac(p, rh, lowview=None, medview=None, highview=None): - """Wrapper for dcloudfrace. +@cast_type(arg_idxs=(0, 1), outviews=("lowview", "midview", "highview")) +@extract_and_transpose(outviews=("lowview", "midview", "highview")) +def _cloudfrac(vert, rh, vert_inc_w_height, low_thresh, mid_thresh, + high_thresh, missing, lowview=None, midview=None, highview=None): + """Wrapper for dcloudfrac2. Located in wrf_cloud_fracf.f90. """ if lowview is None: - lowview = np.zeros(p.shape[0:2], p.dtype, order="F") + lowview = np.zeros(vert.shape[0:2], vert.dtype, order="F") - if medview is None: - medview = np.zeros(p.shape[0:2], p.dtype, order="F") + if midview is None: + midview = np.zeros(vert.shape[0:2], vert.dtype, order="F") if highview is None: - highview = np.zeros(p.shape[0:2], p.dtype, order="F") - - result = dcloudfrac(p, - rh, - lowview, - medview, - highview) + highview = np.zeros(vert.shape[0:2], vert.dtype, order="F") + + result = dcloudfrac2(vert, + rh, + vert_inc_w_height, + low_thresh, + mid_thresh, + high_thresh, + missing, + lowview, + midview, + highview) return result diff --git a/src/wrf/metadecorators.py b/src/wrf/metadecorators.py index e34b71d..0fe6b35 100644 --- a/src/wrf/metadecorators.py +++ b/src/wrf/metadecorators.py @@ -322,6 +322,7 @@ def set_wind_metadata(copy_varname, name, description, return func_wrapper + def set_cape_metadata(is2d): """A decorator that sets the metadata for a wrapped CAPE function's output. @@ -468,14 +469,22 @@ def set_cloudfrac_metadata(): return wrapped(*args, **kwargs) argvars = from_args(wrapped, ("wrfin", "timeidx", "method", "squeeze", - "cache", "_key"), - *args, **kwargs) + "cache", "_key", "vert_type", + "low_thresh", "mid_thresh", + "high_thresh", "missing"), + *args, **kwargs) wrfin = argvars["wrfin"] timeidx = argvars["timeidx"] method = argvars["method"] squeeze = argvars["squeeze"] cache = argvars["cache"] _key = argvars["_key"] + vert_type = argvars["vert_type"] + low_thresh = argvars["low_thresh"] + mid_thresh = argvars["mid_thresh"] + high_thresh = argvars["high_thresh"] + missing = argvars["missing"] + if cache is None: cache = {} @@ -499,6 +508,20 @@ def set_cloudfrac_metadata(): outattrs.update(copy_var.attrs) outdimnames = [None] * result.ndim + # For printing units + unitstr = ("Pa" if vert_type.lower() == "pres" + or vert_type.lower() == "pressure" else "m") + + # For setting the threholds in metdata + if vert_type.lower() == "pres" or vert_type.lower() == "pressure": + _low_thresh = 97000. if low_thresh is None else low_thresh + _mid_thresh = 80000. if mid_thresh is None else mid_thresh + _high_thresh = 45000. if high_thresh is None else high_thresh + elif vert_type.lower() == "height_msl" or "height_agl": + _low_thresh = 300. if low_thresh is None else low_thresh + _mid_thresh = 2000. if mid_thresh is None else mid_thresh + _high_thresh = 6000. if high_thresh is None else high_thresh + # Right dims outdimnames[-2:] = copy_var.dims[-2:] # Left dims @@ -507,6 +530,11 @@ def set_cloudfrac_metadata(): outattrs["description"] = "low, mid, high clouds" outattrs["MemoryOrder"] = "XY" outattrs["units"] = "%" + outattrs["low_thresh"] = "{} {}".format(_low_thresh, unitstr) + outattrs["mid_thresh"] = "{} {}".format(_mid_thresh, unitstr) + outattrs["high_thresh"] = "{} {}".format(_high_thresh, unitstr) + outattrs["_FillValue"] = missing + outattrs["missing_value"] = missing outname = "cloudfrac" # xarray doesn't line up coordinate dimensions based on @@ -529,6 +557,7 @@ def set_cloudfrac_metadata(): return func_wrapper + def set_latlon_metadata(xy=False): """A decorator that sets the metadata for a wrapped latlon function's output. @@ -613,7 +642,8 @@ def set_latlon_metadata(xy=False): return da return func_wrapper - + + def set_height_metadata(geopt=False): """A decorator that sets the metadata for a wrapped height function's output. @@ -704,6 +734,7 @@ def set_height_metadata(geopt=False): dims=outdimnames, coords=outcoords, attrs=outattrs) return func_wrapper + def _set_horiz_meta(wrapped, instance, args, kwargs): """A decorator implementation that sets the metadata for a wrapped horizontal interpolation function. @@ -798,7 +829,8 @@ def _set_horiz_meta(wrapped, instance, args, kwargs): return DataArray(result, name=outname, dims=outdimnames, coords=outcoords, attrs=outattrs) - + + def _set_cross_meta(wrapped, instance, args, kwargs): """A decorator implementation that sets the metadata for a wrapped cross \ section interpolation function. @@ -1012,7 +1044,6 @@ def _set_cross_meta(wrapped, instance, args, kwargs): return DataArray(result, name=outname, dims=outdimnames, coords=outcoords, attrs=outattrs) - def _set_line_meta(wrapped, instance, args, kwargs): """A decorator implementation that sets the metadata for a wrapped line @@ -1770,6 +1801,7 @@ def set_alg_metadata(alg_ndims, refvarname, return func_wrapper + def set_smooth_metdata(): @wrapt.decorator def func_wrapper(wrapped, instance, args, kwargs): @@ -1994,14 +2026,14 @@ def set_cape_alg_metadata(is2d, copyarg="pres_hpa"): return func_wrapper -def set_cloudfrac_alg_metadata(copyarg="pres"): +def set_cloudfrac_alg_metadata(copyarg="vert"): """A decorator that sets the metadata for the wrapped raw cloud fraction diagnostic function. Args: copyarg (:obj:`str`): The wrapped function argument to use for - copying dimension names. Default is 'pres'. + copying dimension names. Default is 'vert'. Returns: @@ -2025,6 +2057,16 @@ def set_cloudfrac_alg_metadata(copyarg="pres"): result = wrapped(*args, **kwargs) + argvals = from_args(wrapped, (copyarg, "low_thresh", + "mid_thresh", "high_thresh", + "missing"), + *args, **kwargs) + cp = argvals[copyarg] + low_thresh = argvals["low_thresh"] + mid_thresh = argvals["mid_thresh"] + high_thresh = argvals["high_thresh"] + missing = argvals["missing"] + # Default dimension names outdims = ["dim_{}".format(i) for i in py3range(result.ndim)] @@ -2034,15 +2076,17 @@ def set_cloudfrac_alg_metadata(copyarg="pres"): outattrs["description"] = "low, mid, high clouds" outattrs["units"] = "%" outattrs["MemoryOrder"] = "XY" + outattrs["low_thresh"] = low_thresh + outattrs["mid_thresh"] = mid_thresh + outattrs["high_thresh"] = high_thresh + outattrs["_FillValue"] = missing + outattrs["missing_value"] = missing - - p = from_args(wrapped, copyarg, *args, **kwargs)[copyarg] - - if isinstance(p, DataArray): + if isinstance(cp, DataArray): # Right dims - outdims[-2:] = p.dims[-2:] + outdims[-2:] = cp.dims[-2:] # Left dims - outdims[1:-2] = p.dims[0:-3] + outdims[1:-2] = cp.dims[0:-3] outcoords = {} diff --git a/src/wrf/routines.py b/src/wrf/routines.py index b72423b..4aed3d8 100644 --- a/src/wrf/routines.py +++ b/src/wrf/routines.py @@ -111,7 +111,8 @@ _VALID_KARGS = {"cape2d" : ["missing"], "uvmet_wspd_wdir" : ["units"], "uvmet10_wspd_wdir" : ["units"], "ctt" : [], - "cloudfrac" : [], + "cloudfrac" : ["vert_type", "low_thresh", + "mid_thresh", "high_thresh"], "default" : [] } diff --git a/src/wrf/specialdec.py b/src/wrf/specialdec.py index c093ca5..8d5efc7 100644 --- a/src/wrf/specialdec.py +++ b/src/wrf/specialdec.py @@ -383,6 +383,7 @@ def cape_left_iter(alg_dtype=np.float64): return func_wrapper + def cloudfrac_left_iter(alg_dtype=np.float64): """A decorator to handle iterating over the leftmost dimensions for the cloud fraction diagnostic. @@ -409,46 +410,45 @@ def cloudfrac_left_iter(alg_dtype=np.float64): """ @wrapt.decorator def func_wrapper(wrapped, instance, args, kwargs): - # The cape calculations use an ascending vertical pressure coordinate new_args = list(args) new_kwargs = dict(kwargs) - p = args[0] + vert = args[0] rh = args[1] - num_left_dims = p.ndim - 3 - orig_dtype = p.dtype + num_left_dims = vert.ndim - 3 + orig_dtype = vert.dtype - # No special left side iteration, build the output from the cape,cin - # result + # No special left side iteration, build the output from the + # low, mid, high results. if (num_left_dims == 0): - low, med, high = wrapped(*new_args, **new_kwargs) + low, mid, high = wrapped(*new_args, **new_kwargs) output_dims = (3,) - output_dims += p.shape[-2:] + output_dims += vert.shape[-2:] output = np.empty(output_dims, orig_dtype) output[0,:] = low[:] - output[1,:] = med[:] + output[1,:] = mid[:] output[2,:] = high[:] return output - # Initial output is ...,cape_cin,nz,ny,nx to create contiguous views - outdims = p.shape[0:num_left_dims] + # Initial output is ...,low_mid_high,nz,ny,nx to create contiguous views + outdims = vert.shape[0:num_left_dims] extra_dims = tuple(outdims) # Copy the left-most dims for iteration outdims += (3,) # low_mid_high - outdims += p.shape[-2:] + outdims += vert.shape[-2:] outview_array = np.empty(outdims, alg_dtype) # Create the output array where the leftmost dim is the cloud type output_dims = (3,) output_dims += extra_dims - output_dims += p.shape[-2:] + output_dims += vert.shape[-2:] output = np.empty(output_dims, orig_dtype) has_missing = False @@ -456,25 +456,25 @@ def cloudfrac_left_iter(alg_dtype=np.float64): for left_idxs in iter_left_indexes(extra_dims): left_and_slice_idxs = left_idxs + (slice(None),) low_idxs = left_idxs + (0, slice(None)) - med_idxs = left_idxs + (1, slice(None)) + mid_idxs = left_idxs + (1, slice(None)) high_idxs = left_idxs + (2, slice(None)) low_output_idxs = (0,) + left_idxs + (slice(None),) - med_output_idxs = (1,) + left_idxs + (slice(None),) + mid_output_idxs = (1,) + left_idxs + (slice(None),) high_output_idxs = (2,) + left_idxs + (slice(None),) - new_args[0] = p[left_and_slice_idxs] + new_args[0] = vert[left_and_slice_idxs] new_args[1] = rh[left_and_slice_idxs] # Skip the possible empty/missing arrays for the join method - # Note: Masking handled by cape.py or computation.py, so only + # Note: Masking handled by cloudfrac.py or computation.py, so only # supply the fill values here. skip_missing = False for arg in (new_args[0:2]): if isinstance(arg, np.ma.MaskedArray): if arg.mask.all(): output[low_output_idxs] = missing - output[med_output_idxs] = missing + output[mid_output_idxs] = missing output[high_output_idxs] = missing skip_missing = True @@ -484,14 +484,14 @@ def cloudfrac_left_iter(alg_dtype=np.float64): continue lowview = outview_array[low_idxs] - medview = outview_array[med_idxs] + midview = outview_array[mid_idxs] highview = outview_array[high_idxs] new_kwargs["lowview"] = lowview - new_kwargs["medview"] = medview + new_kwargs["midview"] = midview new_kwargs["highview"] = highview - low, med, high = wrapped(*new_args, **new_kwargs) + low, mid, high = wrapped(*new_args, **new_kwargs) # Make sure the result is the same data as what got passed in # Can delete this once everything works @@ -501,8 +501,8 @@ def cloudfrac_left_iter(alg_dtype=np.float64): output[low_output_idxs] = ( outview_array[low_idxs].astype(orig_dtype)) - output[med_output_idxs] = ( - outview_array[med_idxs].astype(orig_dtype)) + output[mid_output_idxs] = ( + outview_array[mid_idxs].astype(orig_dtype)) output[high_output_idxs] = ( outview_array[high_idxs].astype(orig_dtype)) diff --git a/test/comp_utest.py b/test/comp_utest.py index 846466f..6af64d2 100644 --- a/test/comp_utest.py +++ b/test/comp_utest.py @@ -512,7 +512,7 @@ def get_args(varname, wrfnc, timeidx, method, squeeze): tkel = tk(full_p, full_t) relh = rh(qv, full_p, tkel) - return (full_p, relh) + return (full_p, relh, 0, 97000., 80000., 45000.) class WRFVarsTest(ut.TestCase): @@ -538,7 +538,7 @@ def make_func(varname, wrfnc, timeidx, method, squeeze, meta): if meta: self.assertEqual(result.dims, ref.dims) - + return func diff --git a/test/listBug.ncl b/test/listBug.ncl index 49b81f0..8e9745a 100644 --- a/test/listBug.ncl +++ b/test/listBug.ncl @@ -1,6 +1,18 @@ +; Bug1: This segfaults l = NewList("fifo") name = "foo" ListAppend(l, (/name/)) print(l) print(l[0]) name = "bar" + +; Bug2 Variables disappear +a = addfile("/Users/ladwig/Documents/wrf_files/wrfout_d02_2010-06-13_21:00:00.nc", "r") +b := wrf_user_getvar(a, "slp", -1) +c = NewList("fifo") +ListAppend(c, (/b/)) +b := wrf_user_getvar(a, "rh", -1) +ListAppend(c, (/b/)) + +print(c[0]) +print(c[1]) ; Variables start disappearing diff --git a/test/utests.py b/test/utests.py index 234239a..b7b26f1 100644 --- a/test/utests.py +++ b/test/utests.py @@ -170,7 +170,11 @@ def make_test(varname, wrf_in, referent, multi=False, repeat=3, pynio=False): tol = 2/100. atol = 0.1 #print (np.amax(np.abs(to_np(my_vals) - ref_vals))) - nt.assert_allclose(to_np(my_vals), ref_vals, tol, atol) + try: + nt.assert_allclose(to_np(my_vals), ref_vals, tol, atol) + except: + print (np.amax(np.abs(to_np(my_vals) - ref_vals))) + raise return test From c9fdc533d0ca282d866615b4400dd975c9ee47e2 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Tue, 31 Oct 2017 15:56:28 -0600 Subject: [PATCH 07/43] Added additional error text. --- fortran/wrf_user.f90 | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/fortran/wrf_user.f90 b/fortran/wrf_user.f90 index 28adadd..78433e3 100644 --- a/fortran/wrf_user.f90 +++ b/fortran/wrf_user.f90 @@ -325,7 +325,7 @@ SUBROUTINE DCOMPUTESEAPRS(nx, ny, nz, z, t, p, q, sea_level_pressure, & INTEGER :: i, j, k INTEGER :: klo, khi - INTEGER :: errcnt + INTEGER :: errcnt, bad_i, bad_j, bad_sfp !REAL(KIND=8) :: plo, phi, tlo, thi, zlo, zhi !REAL(KIND=8) :: p_at_pconst, t_at_pconst, z_at_pconst @@ -340,6 +340,9 @@ SUBROUTINE DCOMPUTESEAPRS(nx, ny, nz, z, t, p, q, sea_level_pressure, & errstat = 0 errcnt = 0 + bad_i = -1 + bad_j = -1 + bad_sfp = -1 !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i,j,k,found) REDUCTION(+:errcnt) DO j = 1,ny @@ -358,6 +361,14 @@ SUBROUTINE DCOMPUTESEAPRS(nx, ny, nz, z, t, p, q, sea_level_pressure, & IF (level(i,j) == -1) THEN errcnt = errcnt + 1 + !$OMP CRITICAL + ! Only do this the first time + IF (bad_i .EQ. -1) THEN + bad_i = i + bad_j = j + bad_sfp = p(i,j,1) / 100. + END IF + !$OMP END CRITICAL END IF END DO END DO @@ -365,7 +376,7 @@ SUBROUTINE DCOMPUTESEAPRS(nx, ny, nz, z, t, p, q, sea_level_pressure, & IF (errcnt > 0) THEN errstat = ALGERR - errmsg = "Error in finding 100 hPa up" + WRITE(errmsg,*) "Error in finding 100 hPa up. i=", bad_i, "j=", bad_j, "sfc_p=", bad_sfp RETURN END IF @@ -380,6 +391,12 @@ SUBROUTINE DCOMPUTESEAPRS(nx, ny, nz, z, t, p, q, sea_level_pressure, & IF (klo == khi) THEN errcnt = errcnt + 1 + !$OMP CRITICAL + IF (bad_i .EQ. -1) THEN + bad_i = i + bad_j = j + END IF + !$OMP END CRITICAL END IF ! This is the readable version of the code below. Don't delete this! @@ -414,7 +431,7 @@ SUBROUTINE DCOMPUTESEAPRS(nx, ny, nz, z, t, p, q, sea_level_pressure, & IF (errcnt > 0) THEN errstat = ALGERR - errmsg = "Error trapping levels" + WRITE(errmsg,*) "Error trapping levels at i=", bad_i, "j=", bad_j RETURN END IF From 4135d06edb041da02fbd75fe2e37044d466d45dc Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Thu, 2 Nov 2017 16:35:50 -0600 Subject: [PATCH 08/43] Fix issue with incorrect number of gridpoints being computed via the xy routine. Fixes #30 . --- src/wrf/interputils.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/wrf/interputils.py b/src/wrf/interputils.py index 114df41..c4af626 100644 --- a/src/wrf/interputils.py +++ b/src/wrf/interputils.py @@ -158,18 +158,19 @@ def _calc_xy(xdim, ydim, pivot_point=None, angle=None, raise ValueError("end_point {} is outside of domain " "with shape {}".format(end_point, (xdim, ydim))) + # From the original NCL code, but the error above will be thrown + # instead. if ( x1 > xdim-1 ): - x1 = xdim + x1 = xdim - 1 if ( y1 > ydim-1): - y1 = ydim + y1 = ydim - 1 else: raise ValueError("invalid start/end or pivot/angle arguments") dx = x1 - x0 dy = y1 - y0 distance = (dx*dx + dy*dy)**0.5 - npts = int(distance) - dxy = distance/npts + npts = int(distance) + 1 xy = np.zeros((npts,2), "float") From ea7aba57b2c4703c524cbaea337258c8831971ad Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Thu, 2 Nov 2017 16:36:41 -0600 Subject: [PATCH 09/43] Updated OpenMP directives. --- fortran/calc_uh.f90 | 13 +++++-- fortran/rip_cape.f90 | 58 +++++++++++++-------------- fortran/wrf_fctt.f90 | 45 +++++++++------------ fortran/wrf_pvo.f90 | 34 ++++++++-------- fortran/wrf_user.f90 | 86 +++++++++++++++++++---------------------- fortran/wrf_vinterp.f90 | 28 +++++++------- 6 files changed, 126 insertions(+), 138 deletions(-) diff --git a/fortran/calc_uh.f90 b/fortran/calc_uh.f90 index b99a48e..6eccb91 100644 --- a/fortran/calc_uh.f90 +++ b/fortran/calc_uh.f90 @@ -61,7 +61,10 @@ SUBROUTINE DCALCUH(nx, ny, nz, nzp1, zp, mapfct, dx, dy, uhmnhgt, uhmxhgt, us, & twodx = 2.0*dx twody = 2.0*dy - !$OMP PARALLEL DO COLLAPSE(3) + + !$OMP PARALLEL + + !$OMP DO COLLAPSE(3) DO k=2,nz-2 DO j=2,ny-1 DO i=2,nx-1 @@ -73,13 +76,13 @@ SUBROUTINE DCALCUH(nx, ny, nz, nzp1, zp, mapfct, dx, dy, uhmnhgt, uhmxhgt, us, & END DO END DO END DO - !$OMP END PARALLEL DO + !$OMP END DO ! Integrate over depth uhminhgt to uhmxhgt AGL ! ! WRITE(6,'(a,f12.1,a,f12.1,a)') & ! 'Calculating UH from ',uhmnhgt,' to ',uhmxhgt,' m AGL' - !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i, j, k, zbot, ztop, kbot, ktop, & + !$OMP DO COLLAPSE(2) PRIVATE(i, j, k, zbot, ztop, kbot, ktop, & !$OMP wgtlw, wbot, wtop, wsum, wmean, sum, helbot, heltop) DO j=2,ny-2 DO i=2,nx-2 @@ -147,7 +150,9 @@ SUBROUTINE DCALCUH(nx, ny, nz, nzp1, zp, mapfct, dx, dy, uhmnhgt, uhmxhgt, us, & END IF END DO END DO - !$OMP END PARALLEL DO + !$OMP END DO + + !$OMP END PARALLEL uh = uh*1000. ! Scale according to Kain et al. (2008) diff --git a/fortran/rip_cape.f90 b/fortran/rip_cape.f90 index c4429b4..d474702 100644 --- a/fortran/rip_cape.f90 +++ b/fortran/rip_cape.f90 @@ -85,11 +85,11 @@ REAL(KIND=8) FUNCTION TONPSADIABAT(thte, prs, psadithte, psadiprs, psaditmk, gam rang1 = h1 - l1 mid1 = (h1 + l1) / 2 DO WHILE(rang1 .GT. 1) - if(thte .GE. psadithte(mid1)) then + IF (thte .GE. psadithte(mid1)) THEN l1 = mid1 - else + ELSE h1 = mid1 - end if + END IF rang1 = h1 - l1 mid1 = (h1 + l1) / 2 END DO @@ -103,17 +103,17 @@ REAL(KIND=8) FUNCTION TONPSADIABAT(thte, prs, psadithte, psadiprs, psaditmk, gam ! END IF ! END DO - ip = -1 + ip = -1 l2 = 1 h2 = 149 rang2 = h2 - l2 mid2 = (h2 + l2) / 2 DO WHILE(rang2 .GT. 1) - if(prs .LE. psadiprs(mid2)) then + IF (prs .LE. psadiprs(mid2)) THEN l2 = mid2 - else + ELSE h2 = mid2 - end if + END IF rang2 = h2 - l2 mid2 = (h2 + l2) / 2 END DO @@ -386,8 +386,8 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& !$OMP i,j,k,kpar) DO j = 1,mjy DO i = 1,mix - cape(i,j,1) = 0.d0 - cin(i,j,1) = 0.d0 + cape(i,j,1) = 0.D0 + cin(i,j,1) = 0.D0 !!$OMP SIMD DO kpar = 2, mkzh @@ -421,7 +421,7 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& klcl = 1 END IF -!!$OMP SIMD lastprivate(qvplift,tmklift,ghtlift,tvlift,tmkenv,qvpenv,tvenv,eslift,facden) + !!$OMP SIMD lastprivate(qvplift,tmklift,ghtlift,tvlift,tmkenv,qvpenv,tvenv,eslift,facden) DO k = kpar,1,-1 ! For arrays that go bottom to top kk = kk + 1 @@ -684,7 +684,7 @@ SUBROUTINE DCAPECALC2D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& ! kg/kg (should range from 0.000 to 0.025) ! -!$OMP PARALLEL DO COLLAPSE(3) + !$OMP PARALLEL DO COLLAPSE(3) DO j = 1,mjy DO i = 1,mix DO k = 1,mkzh @@ -695,7 +695,7 @@ SUBROUTINE DCAPECALC2D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& END DO END DO END DO -!$OMP END PARALLEL DO + !$OMP END PARALLEL DO ! calculated the pressure at full sigma levels (a set of pressure @@ -715,29 +715,29 @@ SUBROUTINE DCAPECALC2D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& !nthreads = omp_get_num_threads() -!$OMP PARALLEL DO COLLAPSE(2) PRIVATE(tlcl, ethpari, & -!$OMP zlcl, kk, ilcl, klcl, tmklift, tvenv, tvlift, ghtlift, & -!$OMP facden, tmkenv, qvpenv, eslift, qvplift, buoy, benamin, & -!$OMP benaccum, zrel, kmax, dz, elfound, & -!$OMP kel, klfc, pavg, p2, p1, totthe, totqvp, totprs, & -!$OMP i,j,k,kpar, kpar1, kpar2, qvppari, tmkpari,p, pup, pdn, q, th, & -!$OMP pp1, pp2, ethmax, eth_temp, klev) + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(tlcl, ethpari, & + !$OMP zlcl, kk, ilcl, klcl, tmklift, tvenv, tvlift, ghtlift, & + !$OMP facden, tmkenv, qvpenv, eslift, qvplift, buoy, benamin, & + !$OMP benaccum, zrel, kmax, dz, elfound, & + !$OMP kel, klfc, pavg, p2, p1, totthe, totqvp, totprs, & + !$OMP i,j,k,kpar, kpar1, kpar2, qvppari, tmkpari,p, pup, pdn, q, th, & + !$OMP pp1, pp2, ethmax, eth_temp, klev) DO j = 1,mjy DO i = 1,mix - cape(i,j,1) = 0.d0 - cin(i,j,1) = 0.d0 + cape(i,j,1) = 0.D0 + cin(i,j,1) = 0.D0 ! find parcel with max theta-e in lowest 3 km agl. - ethmax = -1.d0 - eth_temp = -1.d0 + ethmax = -1.D0 + eth_temp = -1.D0 DO k = 1, mkzh - IF (ght_new(k,i,j)-ter(i,j) .LT. 3000.d0) THEN + IF (ght_new(k,i,j)-ter(i,j) .LT. 3000.D0) THEN tlcl = TLCLC1 / (LOG(tmk_new(k,i,j)**TLCLC2/& (MAX(qvp_new(k,i,j), 1.d-15)*prs_new(k,i,j)/(EPS+MAX(qvp_new(k,i,j), 1.d-15))))-TLCLC3)+& TLCLC4 - eth_temp(k) = tmk_new(k,i,j) * (1000.d0/prs_new(k,i,j))**& - (GAMMA*(1.d0 + GAMMAMD*(MAX(qvp_new(k,i,j), 1.d-15))))*& + eth_temp(k) = tmk_new(k,i,j) * (1000.D0/prs_new(k,i,j))**& + (GAMMA*(1.D0 + GAMMAMD*(MAX(qvp_new(k,i,j), 1.d-15))))*& EXP((THTECON1/tlcl - THTECON2)*(MAX(qvp_new(k,i,j), 1.d-15))*& - (1.d0 + THTECON3*(MAX(qvp_new(k,i,j), 1.d-15)))) + (1.D0 + THTECON3*(MAX(qvp_new(k,i,j), 1.d-15)))) END IF END DO klev = mkzh @@ -755,8 +755,8 @@ SUBROUTINE DCAPECALC2D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& ! Establish average properties of that parcel ! (over depth of approximately davg meters) - !davg = 500.d0 - pavg = 500.d0 * prs_new(kpar1,i,j)*& + !davg = 500.D0 + pavg = 500.D0 * prs_new(kpar1,i,j)*& G/(RD*tvirtual(tmk_new(kpar1,i,j), qvp_new(kpar1,i,j))) p2 = MIN(prs_new(kpar1,i,j)+.5d0*pavg, prsf(mkzh,i,j)) p1 = p2 - pavg diff --git a/fortran/wrf_fctt.f90 b/fortran/wrf_fctt.f90 index 97da1b9..a1c97da 100644 --- a/fortran/wrf_fctt.f90 +++ b/fortran/wrf_fctt.f90 @@ -18,23 +18,15 @@ SUBROUTINE wrfcttcalc(prs, tk, qci, qcw, qvp, ght, ter, ctt, haveqci, nz, ns, ew ! LOCAL VARIABLES INTEGER i,j,k,ripk - !INTEGER :: mjx,miy,mkzh - REAL(KIND=8) :: vt, opdepthu, opdepthd, dp - REAL(KIND=8) :: ratmix, arg1, arg2, agl_hgt - REAL(KIND=8) :: fac, prsctt - !REAL(KIND=8) :: eps,ussalr,rgas,grav,abscoefi,abscoef,celkel,wrfout - !REAL(KIND=8) :: ght(ew,ns,nz),stuff(ew,ns) - !REAL(KIND=8), DIMENSION(ew,ns,nz) :: pf(ns,ew,nz),p1,p2 + REAL(KIND=8) :: opdepthu, opdepthd, dp, arg1, fac, prsctt, ratmix + REAL(KIND=8) :: arg2, agl_hgt, vt REAL(KIND=8), DIMENSION(ew,ns,nz) :: pf REAL(KIND=8) :: p1, p2 - !mjx = ew - !miy = ns - !mkzh = nz + !$OMP PARALLEL - prsctt = 0 ! removes the warning - -! Calculate the surface pressure + ! Calculate the surface pressure + !$OMP DO COLLAPSE(2) DO j=1,ns DO i=1,ew ratmix = .001D0*qvp(i,j,1) @@ -46,7 +38,9 @@ SUBROUTINE wrfcttcalc(prs, tk, qci, qcw, qvp, ght, ter, ctt, haveqci, nz, ns, ew pf(i,j,nz) = prs(i,j,1)*(vt/(vt + USSALR*(agl_hgt)))**(arg1) END DO END DO + !$OMP END DO + !$OMP DO COLLAPSE(3) DO k=1,nz-1 DO j=1,ns DO i=1,ew @@ -55,26 +49,27 @@ SUBROUTINE wrfcttcalc(prs, tk, qci, qcw, qvp, ght, ter, ctt, haveqci, nz, ns, ew END DO END DO END DO + !$OMP END DO + !$OMP DO COLLAPSE(2) PRIVATE(i, j, k, ripk, opdepthd, opdepthu, & + !$OMP prsctt, dp, p1, p2, fac, arg1) DO j=1,ns DO i=1,ew opdepthd = 0.D0 k = 0 + prsctt = 0 -! Integrate downward from model top, calculating path at full -! model vertical levels. - -!20 opdepthu=opdepthd + ! Integrate downward from model top, calculating path at full + ! model vertical levels. DO k=1, nz opdepthu = opdepthd - !k=k+1 ripk = nz - k + 1 - IF (k .EQ. 1) THEN - dp = 200.D0*(pf(i,j,1) - prs(i,j,nz)) ! should be in Pa - ELSE + IF (k .NE. 1) THEN dp = 100.D0*(pf(i,j,k) - pf(i,j,k-1)) ! should be in Pa + ELSE + dp = 200.D0*(pf(i,j,1) - prs(i,j,nz)) ! should be in Pa END IF IF (haveqci .EQ. 0) then @@ -89,7 +84,6 @@ SUBROUTINE wrfcttcalc(prs, tk, qci, qcw, qvp, ght, ter, ctt, haveqci, nz, ns, ew END IF IF (opdepthd .LT. 1. .AND. k .LT. nz) THEN - !GOTO 20 CYCLE ELSE IF (opdepthd .LT. 1. .AND. k .EQ. nz) THEN @@ -111,15 +105,14 @@ SUBROUTINE wrfcttcalc(prs, tk, qci, qcw, qvp, ght, ter, ctt, haveqci, nz, ns, ew fac = (prsctt - p1)/(p2 - p1) arg1 = fac*(tk(i,j,ripk) - tk(i,j,ripk+1)) - CELKEL ctt(i,j) = tk(i,j,ripk+1) + arg1 - !GOTO 40 EXIT END IF END DO END DO END DO -! 30 CONTINUE -! 40 CONTINUE -! 190 CONTINUE + !$OMP END DO + + !$OMP END PARALLEL RETURN END SUBROUTINE wrfcttcalc diff --git a/fortran/wrf_pvo.f90 b/fortran/wrf_pvo.f90 index 209a257..cef3ed9 100644 --- a/fortran/wrf_pvo.f90 +++ b/fortran/wrf_pvo.f90 @@ -23,29 +23,29 @@ SUBROUTINE DCOMPUTEABSVORT(av, u, v, msfu, msfv, msft, cor, dx, dy, nx, ny, nz,& REAL(KIND=8) :: dsy, dsx, dudy, dvdx, avort REAL(KIND=8) :: mm - ! PRINT*,'nx,ny,nz,nxp1,nyp1' - ! PRINT*,nx,ny,nz,nxp1,nyp1 + !$OMP PARALLEL DO COLLAPSE(3) PRIVATE(i, j, k, jp1, jm1, ip1, im1, & + !$OMP dsx, dsy, mm, dudy, dvdx, avort) DO k = 1,nz DO j = 1,ny - jp1 = MIN(j+1, ny) - jm1 = MAX(j-1, 1) DO i = 1,nx + jp1 = MIN(j+1, ny) + jm1 = MAX(j-1, 1) ip1 = MIN(i+1, nx) im1 = MAX(i-1, 1) - ! PRINT *,jp1,jm1,ip1,im1 dsx = (ip1 - im1) * dx dsy = (jp1 - jm1) * dy mm = msft(i,j)*msft(i,j) - ! PRINT *,j,i,u(i,jp1,k),msfu(i,jp1),u(i,jp1,k)/msfu(i,jp1) dudy = 0.5D0*(u(i,jp1,k)/msfu(i,jp1) + u(i+1,jp1,k)/msfu(i+1,jp1) - & u(i,jm1,k)/msfu(i,jm1) - u(i+1,jm1,k)/msfu(i+1,jm1))/dsy*mm dvdx = 0.5D0*(v(ip1,j,k)/msfv(ip1,j) + v(ip1,j+1,k)/msfv(ip1,j+1) - & v(im1,j,k)/msfv(im1,j) - v(im1,j+1,k)/msfv(im1,j+1))/dsx*mm avort = dvdx - dudy + cor(i,j) av(i,j,k) = avort*1.D5 + END DO END DO END DO + !$OMP END PARALLEL DO RETURN @@ -80,22 +80,23 @@ SUBROUTINE DCOMPUTEPV(pv, u, v, theta, prs, msfu, msfv, msft, cor, dx, dy, nx, & REAL(KIND=8) :: dsy, dsx, dp, dudy, dvdx, dudp, dvdp, dthdp, avort REAL(KIND=8) :: dthdx, dthdy, mm - ! PRINT*,'nx,ny,nz,nxp1,nyp1' - ! PRINT*,nx,ny,nz,nxp1,nyp1 + !$OMP PARALLEL DO COLLAPSE(3) PRIVATE(i, j, k, kp1, km1, jp1, jm1, ip1, & + !$OMP im1, dsx, dsy, mm, dudy, dvdx, avort, & + !$OMP dp, dudp, dvdp, dthdp, dthdx, dthdy) DO k = 1,nz - kp1 = MIN(k+1, nz) - km1 = MAX(k-1, 1) DO J = 1,ny - jp1 = MIN(j+1, ny) - jm1 = MAX(j-1, 1) DO i = 1,nx + kp1 = MIN(k+1, nz) + km1 = MAX(k-1, 1) + jp1 = MIN(j+1, ny) + jm1 = MAX(j-1, 1) ip1 = MIN(i+1, nx) im1 = MAX(i-1, 1) - ! PRINT *,jp1,jm1,ip1,im1 + dsx = (ip1 - im1)*dx dsy = (jp1 - jm1)*dy mm = msft(i,j)*msft(i,j) - ! PRINT *,j,i,u(i,jp1,k),msfu(i,jp1),u(i,jp1,k)/msfu(i,jp1) + dudy = 0.5D0*(u(i,jp1,k)/msfu(i,jp1) + u(i+1,jp1,k)/msfu(i+1,jp1) - & u(i,jm1,k)/msfu(i,jm1) - u(i+1,jm1,k)/msfu(i+1,jm1))/dsy*mm dvdx = 0.5D0*(v(ip1,j,k)/msfv(ip1,j) + v(ip1,j+1,k)/msfv(ip1,j+1) - & @@ -108,14 +109,11 @@ SUBROUTINE DCOMPUTEPV(pv, u, v, theta, prs, msfu, msfv, msft, cor, dx, dy, nx, & dthdx = (theta(ip1,j,k) - theta(im1,j,k))/dsx*msft(i,j) dthdy = (theta(i,jp1,k) - theta(i,jm1,k))/dsy*msft(i,j) pv(i,j,k) = -G*(dthdp*avort - dvdp*dthdx + dudp*dthdy)*10000.D0 - ! if(i.eq.300 .and. j.eq.300) then - ! PRINT*,'avort,dudp,dvdp,dthdp,dthdx,dthdy,pv' - ! PRINT*,avort,dudp,dvdp,dthdp,dthdx,dthdy,pv(i,j,k) - ! endif pv(i,j,k) = pv(i,j,k)*1.D2 END DO END DO END DO + !$OMP END PARALLEL DO RETURN diff --git a/fortran/wrf_user.f90 b/fortran/wrf_user.f90 index 78433e3..618b864 100644 --- a/fortran/wrf_user.f90 +++ b/fortran/wrf_user.f90 @@ -327,9 +327,9 @@ SUBROUTINE DCOMPUTESEAPRS(nx, ny, nz, z, t, p, q, sea_level_pressure, & INTEGER :: klo, khi INTEGER :: errcnt, bad_i, bad_j, bad_sfp - !REAL(KIND=8) :: plo, phi, tlo, thi, zlo, zhi - !REAL(KIND=8) :: p_at_pconst, t_at_pconst, z_at_pconst - !REAL(KIND=8) :: z_half_lowest + REAL(KIND=8) :: plo, phi, tlo, thi, zlo, zhi + REAL(KIND=8) :: p_at_pconst, t_at_pconst, z_at_pconst + REAL(KIND=8) :: z_half_lowest LOGICAL :: l1, l2, l3, found @@ -399,32 +399,18 @@ SUBROUTINE DCOMPUTESEAPRS(nx, ny, nz, z, t, p, q, sea_level_pressure, & !$OMP END CRITICAL END IF - ! This is the readable version of the code below. Don't delete this! - !plo = p(i,j,klo) - !phi = p(i,j,khi) - !tlo = t(i,j,klo)*(1.D0 + 0.608D0*q(i,j,klo)) - !thi = t(i,j,khi)*(1.D0 + 0.608D0*q(i,j,khi)) - !zlo = z(i,j,klo) - !zhi = z(i,j,khi) - !p_at_pconst = p(i,j,1) - PCONST - !t_at_pconst = thi - (thi-tlo)*LOG(p_at_pconst/phi)*LOG(plo/phi) - !z_at_pconst = zhi - (zhi-zlo)*LOG(p_at_pconst/phi)*LOG(plo/phi) + plo = p(i,j,klo) + phi = p(i,j,khi) + tlo = t(i,j,klo)*(1.D0 + 0.608D0*q(i,j,klo)) + thi = t(i,j,khi)*(1.D0 + 0.608D0*q(i,j,khi)) + zlo = z(i,j,klo) + zhi = z(i,j,khi) + p_at_pconst = p(i,j,1) - PCONST + t_at_pconst = thi - (thi-tlo)*LOG(p_at_pconst/phi)*LOG(plo/phi) + z_at_pconst = zhi - (zhi-zlo)*LOG(p_at_pconst/phi)*LOG(plo/phi) ! - !t_surf(i,j) = t_at_pconst * (p(i,j,1)/p_at_pconst)**(USSALR*RD/G) - !t_sea_level(i,j) = t_at_pconst + USSALR*z_at_pconst - - ! The same code as above with temporaries removed to improve vectorization - t_surf(i,j) = ((t(i,j,khi)*(1.D0 + 0.608D0*q(i,j,khi))) - & - ((t(i,j,khi)*(1.D0 + 0.608D0*q(i,j,khi)))-(t(i,j,klo)*& - (1.D0 + 0.608D0*q(i,j,klo))))*LOG((p(i,j,1) - PCONST)/p(i,j,khi))& - *LOG(p(i,j,klo)/p(i,j,khi))) * (p(i,j,1)/(p(i,j,1) - PCONST))**(USSALR*RD/G) - - t_sea_level(i,j) = t(i,j,khi)*(1.D0 + 0.608D0*q(i,j,khi)) - & - ((t(i,j,khi)*(1.D0 + 0.608D0*q(i,j,khi)))-(t(i,j,klo)*& - (1.D0 + 0.608D0*q(i,j,klo))))*LOG((p(i,j,1) - PCONST)/& - p(i,j,khi))*LOG(p(i,j,klo)/p(i,j,khi)) + & - USSALR*(z(i,j,khi) - (z(i,j,khi)-z(i,j,klo))*& - LOG((p(i,j,1) - PCONST)/p(i,j,khi))*LOG(p(i,j,klo)/p(i,j,khi))) + t_surf(i,j) = t_at_pconst * (p(i,j,1)/p_at_pconst)**(USSALR*RD/G) + t_sea_level(i,j) = t_at_pconst + USSALR*z_at_pconst END DO END DO !$OMP END PARALLEL DO @@ -581,16 +567,18 @@ SUBROUTINE FILTER2D(a, b, nx, ny, it, missing) INTEGER :: i, j, iter + !$OMP PARALLEL + DO iter=1,it - !$OMP PARALLEL DO COLLAPSE(2) + !$OMP DO COLLAPSE(2) DO j=1,ny DO i = 1,nx b(i,j) = a(i,j) END DO END DO - !$OMP END PARALLEL DO + !$OMP END DO - !$OMP PARALLEL DO COLLAPSE(2) + !$OMP DO COLLAPSE(2) DO j=2,ny-1 DO i=1,nx IF (b(i,j-1) .EQ. missing .OR. b(i,j) .EQ. missing .OR. & @@ -601,9 +589,9 @@ SUBROUTINE FILTER2D(a, b, nx, ny, it, missing) END IF END DO END DO - !$OMP END PARALLEL DO + !$OMP END DO - !$OMP PARALLEL DO COLLAPSE(2) + !$OMP DO COLLAPSE(2) DO j=1,ny DO i=2,nx-1 IF (b(i-1,j) .EQ. missing .OR. b(i,j) .EQ. missing .OR. & @@ -614,7 +602,7 @@ SUBROUTINE FILTER2D(a, b, nx, ny, it, missing) END IF END DO END DO - !$OMP END PARALLEL DO + !$OMP END DO ! do j=1,ny ! do i=1,nx ! b(i,j) = a(i,j) @@ -632,6 +620,8 @@ SUBROUTINE FILTER2D(a, b, nx, ny, it, missing) ! enddo END DO + !$OMP END PARALLEL + RETURN END SUBROUTINE FILTER2D @@ -768,7 +758,9 @@ SUBROUTINE DCOMPUTEUVMET(u, v, uvmet, longca,longcb,flong,flat, & ! msg stands for missing value in this code ! WRITE (6,FMT=*) ' in compute_uvmet ',NX,NY,NXP1,NYP1,ISTAG - !$OMP PARALLEL DO COLLAPSE(2) + !$OMP PARALLEL + + !$OMP DO COLLAPSE(2) DO j = 1,ny DO i = 1,nx @@ -790,23 +782,23 @@ SUBROUTINE DCOMPUTEUVMET(u, v, uvmet, longca,longcb,flong,flat, & END DO END DO - !$OMP END PARALLEL DO + !$OMP END DO ! Note: Intentionally removed as many IF statements as possible from loops ! to improve vectorization. IF (istag .EQ. 0) THEN ! Not staggered IF (.NOT. is_msg_val) THEN ! No missing values used - !$OMP PARALLEL DO COLLAPSE(2) + !$OMP DO COLLAPSE(2) DO j = 1,ny DO i = 1,nx uvmet(i,j,1) = v(i,j)*longcb(i,j) + u(i,j)*longca(i,j) uvmet(i,j,2) = v(i,j)*longca(i,j) - u(i,j)*longcb(i,j) END DO END DO - !$OMP END PARALLEL DO + !$OMP END DO ELSE ! Missing values used - !$OMP PARALLEL DO COLLAPSE(2) + !$OMP DO COLLAPSE(2) DO j = 1,ny DO i = 1,nx IF ((u(i,j) .NE. umsg .AND. v(i,j) .NE. vmsg)) THEN @@ -818,14 +810,14 @@ SUBROUTINE DCOMPUTEUVMET(u, v, uvmet, longca,longcb,flong,flat, & END IF END DO END DO - !$OMP END PARALLEL DO + !$OMP END DO END IF ELSE ! Staggered IF (.NOT. is_msg_val) THEN ! No missing values used - !$OMP PARALLEL DO COLLAPSE(2) + !$OMP DO COLLAPSE(2) DO j = 1,ny DO i = 1,nx - ! This is the more readable version. Do not delete. + ! This is the more readable version. !uk = 0.5D0*(u(i,j) + u(i+1,j)) !vk = 0.5D0*(v(i,j) + v(i,j+1)) !uvmet(i,j,1) = vk*longcb(i,j) + uk*longca(i,j) @@ -838,13 +830,13 @@ SUBROUTINE DCOMPUTEUVMET(u, v, uvmet, longca,longcb,flong,flat, & END DO END DO - !$OMP END PARALLEL DO + !$OMP END DO ELSE ! Missing values used - !$OMP PARALLEL DO COLLAPSE(2) + !$OMP DO COLLAPSE(2) DO j = 1,ny DO i = 1,nx IF (u(i,j) .NE. umsg .AND. v(i,j) .NE. vmsg .AND. u(i+1,j) .NE. umsg .AND. v(i,j+1) .NE. vmsg) THEN - ! This is the more readable version. Do not delete. + ! This is the more readable version. !uk = 0.5D0*(u(i,j) + u(i+1,j)) !vk = 0.5D0*(v(i,j) + v(i,j+1)) !uvmet(i,j,1) = vk*longcb(i,j) + uk*longca(i,j) @@ -860,10 +852,12 @@ SUBROUTINE DCOMPUTEUVMET(u, v, uvmet, longca,longcb,flong,flat, & END IF END DO END DO - !$OMP END PARALLEL DO + !$OMP END DO END IF END IF + !$OMP END PARALLEL + RETURN END SUBROUTINE DCOMPUTEUVMET diff --git a/fortran/wrf_vinterp.f90 b/fortran/wrf_vinterp.f90 index ee1835f..7f7c2f1 100644 --- a/fortran/wrf_vinterp.f90 +++ b/fortran/wrf_vinterp.f90 @@ -20,24 +20,24 @@ SUBROUTINE wrf_monotonic(out, in, lvprs, cor, idir, delta, ew, ns, nz, icorsw) !NCLEND - INTEGER :: i, j, k, ripk, k300 + INTEGER :: i, j, k, k300 - k300 = 1 ! removes the warning - - !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i, j, k, ripk) FIRSTPRIVATE(k300) + !$OMP PARALLEL DO COLLAPSE(2) DO j=1,ns DO i=1,ew + IF (icorsw .EQ. 1 .AND. cor(i,j) .LT. 0.) THEN DO k=1,nz in(i,j,k) = -in(i,j,k) END DO END IF + k300 = nz + ! First find k index that is at or below (height-wise) ! the 300 hPa level. - DO k = 1,nz - ripk = nz-k+1 - IF (lvprs(i,j,k) .LE. 300.D0) THEN + DO k = nz,1,-1 + IF (lvprs(i,j,k) .GE. 300.D0) THEN k300 = k EXIT END IF @@ -184,18 +184,13 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& cvcord = 't' END IF - !miy = ns - !mjx = ew - !njx = ew - !niy = ns - -!$OMP PARALLEL DO + !$OMP PARALLEL DO COLLAPSE(2) DO j = 1,ns DO i = 1,ew tempout(i,j) = rmsg END DO END DO -!$OMP END PARALLEL DO + !$OMP END PARALLEL DO DO nreqlvs = 1,numlevels IF (cvcord .EQ. 'z') THEN @@ -257,6 +252,7 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& ifound = 1 END IF END IF + EXIT END IF END DO !end for the k loop @@ -415,6 +411,8 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& END DO !$OMP END PARALLEL DO + + IF (log_errcnt > 0) THEN errstat = ALGERR WRITE(errmsg, *) "Pres=0. Unable to take log of 0." @@ -427,7 +425,7 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& RETURN END IF - !$OMP PARALLEL DO + !$OMP PARALLEL DO COLLAPSE(2) DO j = 1,ns DO i = 1,ew dataout(i,j,nreqlvs) = tempout(i,j) From af8bc5d5d0ba4812abc3ffbb146a34fad353faa4 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Thu, 2 Nov 2017 16:53:39 -0600 Subject: [PATCH 10/43] Forgot to modify the dx and dy after adding the additional point. Fixes #30 --- src/wrf/interputils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wrf/interputils.py b/src/wrf/interputils.py index c4af626..fb2768f 100644 --- a/src/wrf/interputils.py +++ b/src/wrf/interputils.py @@ -174,8 +174,8 @@ def _calc_xy(xdim, ydim, pivot_point=None, angle=None, xy = np.zeros((npts,2), "float") - dx = dx/npts - dy = dy/npts + dx = dx/(npts-1) + dy = dy/(npts-1) for i in py3range(npts): xy[i,0] = x0 + i*dx From 2d0a7e17cebcd6be29aba0a0790a15cff9315502 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Tue, 7 Nov 2017 15:08:02 -0700 Subject: [PATCH 11/43] Added OpenMP directives. Unit tests updated for interpline and vertcross to handle the additional grid point that NCL does not yet have. --- fortran/wrf_pw.f90 | 7 ++ fortran/wrf_relhl.f90 | 9 ++- fortran/wrf_rip_phys_routines.f90 | 113 +++++++++++++++++++----------- fortran/wrf_user_dbz.f90 | 13 ++++ fortran/wrf_wind.f90 | 4 ++ test/utests.py | 24 ++++--- 6 files changed, 119 insertions(+), 51 deletions(-) diff --git a/fortran/wrf_pw.f90 b/fortran/wrf_pw.f90 index ba19ffb..275deb4 100644 --- a/fortran/wrf_pw.f90 +++ b/fortran/wrf_pw.f90 @@ -18,14 +18,21 @@ SUBROUTINE DCOMPUTEPW(p, tv, qv, ht, pw, nx, ny, nz, nzh) !REAL(KIND=8),PARAMETER :: R=287.06 pw = 0 + + !$OMP PARALLEL + DO k=1,nz + !$OMP DO COLLAPSE(2) DO j=1,ny DO i=1,nx pw(i,j) = pw(i,j) + ((p(i,j,k)/(RD*tv(i,j,k)))*qv(i,j,k)*(ht(i,j,k+1) - ht(i,j,k))) END DO END DO + !$OMP END DO END DO + !$OMP END PARALLEL + RETURN END SUBROUTINE DCOMPUTEPW diff --git a/fortran/wrf_relhl.f90 b/fortran/wrf_relhl.f90 index 9efeec8..43d4950 100644 --- a/fortran/wrf_relhl.f90 +++ b/fortran/wrf_relhl.f90 @@ -56,10 +56,10 @@ SUBROUTINE DCALRELHL(u, v, ght, ter, top, sreh, miy, mjx, mkzh) INTEGER :: i, j, k, k10, k3, ktop !REAL(KIND=8), PARAMETER :: DTR=PI/180.d0, DPR=180.d0/PI - !DO j = 1, mjx-1 + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i,j,k,k10,k3,ktop, cu, cv, x, & + !$OMP sum, dh, sdh, su, sv, ua, va, asp, adr, bsp, bdr) DO j=1, mjx DO i=1, miy - !DO i=1, miy-1 sdh = 0.D0 su = 0.D0 sv = 0.D0 @@ -82,12 +82,14 @@ SUBROUTINE DCALRELHL(u, v, ght, ter, top, sreh, miy, mjx, mkzh) IF (k10 .EQ. 0) THEN k10 = 2 ENDIF + DO k = k3, k10, -1 dh = ght(i,j,k-1) - ght(i,j,k) sdh = sdh + dh su = su + 0.5D0*dh*(u(i,j,k-1) + u(i,j,k)) sv = sv + 0.5D0*dh*(v(i,j,k-1) + v(i,j,k)) END DO + ua = su/sdh va = sv/sdh asp = SQRT(ua*ua + va*va) @@ -96,11 +98,13 @@ SUBROUTINE DCALRELHL(u, v, ght, ter, top, sreh, miy, mjx, mkzh) ELSE adr = DEG_PER_RAD * (PI + ATAN2(ua,va)) ENDIF + bsp = 0.75D0*asp bdr = adr + 30.D0 IF (bdr .GT. 360.D0) THEN bdr = bdr - 360.D0 ENDIF + cu = -bsp*SIN(bdr * RAD_PER_DEG) cv = -bsp*COS(bdr * RAD_PER_DEG) sum = 0.D0 @@ -112,6 +116,7 @@ SUBROUTINE DCALRELHL(u, v, ght, ter, top, sreh, miy, mjx, mkzh) sreh(i,j) = -sum END DO END DO + !$OMP END PARALLEL DO RETURN diff --git a/fortran/wrf_rip_phys_routines.f90 b/fortran/wrf_rip_phys_routines.f90 index 0b307f2..ad1306f 100644 --- a/fortran/wrf_rip_phys_routines.f90 +++ b/fortran/wrf_rip_phys_routines.f90 @@ -58,8 +58,16 @@ SUBROUTINE WETBULBCALC(prs, tmk, qvp, twb, nx, ny, nz, psafile, errstat, errmsg) REAL(KIND=8) :: tonpsadiabat INTEGER :: l1, h1, mid1, rang1, l2, h2, mid2, rang2 + INTEGER :: errcnt1, errcnt2, bad_i, bad_j, bad_k + REAL(KIND=8) :: bad_p, bad_eth !INTEGER :: ip, ipch, jt, jtch + errcnt1 = 0 + errcnt2 = 0 + bad_i = -1 + bad_j = -1 + bad_k = -1 + ! Before looping, set lookup table for getting temperature on ! a pseudoadiabat. @@ -69,6 +77,9 @@ SUBROUTINE WETBULBCALC(prs, tmk, qvp, twb, nx, ny, nz, psafile, errstat, errmsg) RETURN END IF + !$OMP PARALLEL DO COLLAPSE(3) PRIVATE (i, j, k, jt, ip, q, t, p, e, tlcl, & + !$OMP eth, fracip, fracip2, fracjt, fracjt2, l1, h1, mid1, rang1, l2, h2, & + !$OMP mid2, rang2, tonpsadiabat) REDUCTION(+:errcnt1, errcnt2) DO k=1,nz DO j=1,ny DO i=1,nx @@ -100,52 +111,45 @@ SUBROUTINE WETBULBCALC(prs, tmk, qvp, twb, nx, ny, nz, psafile, errstat, errmsg) rang1 = h1 - l1 mid1 = (h1 + l1) / 2 DO WHILE(rang1 .GT. 1) - if(eth .GE. psadithte(mid1)) then + IF (eth .GE. psadithte(mid1)) THEN l1 = mid1 - else + ELSE h1 = mid1 - end if + END IF rang1 = h1 - l1 mid1 = (h1 + l1) / 2 END DO jt = l1 -! jt=-1 -! DO jtch=1,150-1 -! IF (eth .GE. PSADITHTE(jtch) .AND. eth .LT. PSADITHTE(jtch+1)) THEN -! jt = jtch -! EXIT -! ENDIF -! END DO - - ip = -1 - l2 = 1 - h2 = 149 - rang2 = h2 - l2 - mid2 = (h2 + l2) / 2 - DO WHILE(rang2 .GT. 1) - if(p .LE. psadiprs(mid2)) then - l2 = mid2 - else - h2 = mid2 - end if - rang2 = h2 - l2 - mid2 = (h2 + l2) / 2 - END DO - ip = l2 - -! ip=-1 -! DO ipch=1,150-1 -! IF (p .LE. PSADIPRS(ipch) .AND. p .GT. PSADIPRS(ipch+1)) THEN -! ip = ipch -! EXIT -! ENDIF -! END DO + ip = -1 + l2 = 1 + h2 = 149 + rang2 = h2 - l2 + mid2 = (h2 + l2) / 2 + DO WHILE(rang2 .GT. 1) + IF (p .LE. psadiprs(mid2)) THEN + l2 = mid2 + ELSE + h2 = mid2 + END IF + rang2 = h2 - l2 + mid2 = (h2 + l2) / 2 + END DO + ip = l2 IF (jt .EQ. -1 .OR. ip .EQ. -1) THEN - errstat = ALGERR - WRITE(errmsg, *) "Outside of lookup table bounds. prs,thte=", p, eth - RETURN + errcnt1 = errcnt1 + 1 + !$OMP CRITICAL + ! Only do this the first time + IF (bad_i .EQ. -1) THEN + bad_i = i + bad_j = j + bad_k = k + bad_p = p + bad_eth = eth + END IF + !$OMP END CRITICAL + CYCLE ENDIF fracjt = (eth - PSADITHTE(jt))/(PSADITHTE(jt+1) - PSADITHTE(jt)) @@ -153,12 +157,21 @@ SUBROUTINE WETBULBCALC(prs, tmk, qvp, twb, nx, ny, nz, psafile, errstat, errmsg) fracip = (PSADIPRS(ip) - p)/(PSADIPRS(ip) - PSADIPRS(ip+1)) fracip2 = 1. - fracip + IF (PSADITMK(ip,jt) .GT. 1e9 .OR. PSADITMK(ip+1,jt) .GT. 1e9 .OR. & PSADITMK(ip,jt+1) .GT. 1e9 .OR. PSADITMK(ip+1,jt+1) .GT. 1e9) THEN - !PRINT*,'Tried to access missing tmperature in lookup table.' - errstat = ALGERR - WRITE(errmsg, *) "Prs and Thte probably unreasonable. prs, thte=", p, eth - RETURN + errcnt2 = errcnt2 + 1 + !$OMP CRITICAL + ! Only do this the first time + IF (bad_i .EQ. -1) THEN + bad_i = i + bad_j = j + bad_k = k + bad_p = p + bad_eth = eth + END IF + !$OMP END CRITICAL + CYCLE ENDIF tonpsadiabat = fracip2*fracjt2*PSADITMK(ip,jt) + & @@ -173,6 +186,20 @@ SUBROUTINE WETBULBCALC(prs, tmk, qvp, twb, nx, ny, nz, psafile, errstat, errmsg) END DO END DO + !$OMP END PARALLEL DO + + IF (errcnt1 > 0) THEN + errstat = ALGERR + WRITE(errmsg, *) "Outside of lookup table bounds. i=", bad_i, ",j=", bad_j, ",k=", bad_k, ",p=", bad_p, ",eth=", bad_eth + RETURN + END IF + + IF (errcnt2 > 0) THEN + errstat = ALGERR + WRITE(errmsg, *) "PRS and THTE unreasonable. i=", bad_i, ",j=", bad_j, ",k=", bad_k, ",p=", bad_p, ",eth=", bad_eth + RETURN + END IF + RETURN END SUBROUTINE WETBULBCALC @@ -229,6 +256,7 @@ SUBROUTINE OMGCALC(qvp, tmk, www, prs, omg, mx, my, mz) INTEGER :: i, j, k !REAL(KIND=8), PARAMETER :: GRAV=9.81, RGAS=287.04, EPS=0.622 + !$OMP PARALLEL DO COLLAPSE(3) DO k=1,mz DO j=1,my DO i=1,mx @@ -238,6 +266,7 @@ SUBROUTINE OMGCALC(qvp, tmk, www, prs, omg, mx, my, mz) END DO END DO END DO + !$OMP END PARALLEL DO RETURN @@ -289,6 +318,7 @@ SUBROUTINE VIRTUAL_TEMP(temp, ratmix, tv, nx, ny, nz) INTEGER :: i,j,k !REAL(KIND=8),PARAMETER :: EPS = 0.622D0 + !$OMP PARALLEL DO COLLAPSE(3) DO k=1,nz DO j=1,ny DO i=1,nx @@ -296,6 +326,7 @@ SUBROUTINE VIRTUAL_TEMP(temp, ratmix, tv, nx, ny, nz) END DO END DO END DO + !$OMP END PARALLEL DO RETURN diff --git a/fortran/wrf_user_dbz.f90 b/fortran/wrf_user_dbz.f90 index d1151f0..d2c6098 100644 --- a/fortran/wrf_user_dbz.f90 +++ b/fortran/wrf_user_dbz.f90 @@ -77,7 +77,10 @@ SUBROUTINE CALCDBZ(prs, tmk, qvp, qra, qsn, qgr, sn0, ivarint, iliqskin, dbz, nx REAL(KIND=8), PARAMETER :: RN0_S = 2.D7 REAL(KIND=8), PARAMETER :: RN0_G = 4.D6 + !$OMP PARALLEL + ! Force all Q arrays to be 0.0 or greater. + !$OMP DO COLLAPSE(3) DO k = 1,nz DO j = 1,ny DO i = 1,nx @@ -96,10 +99,12 @@ SUBROUTINE CALCDBZ(prs, tmk, qvp, qra, qsn, qgr, sn0, ivarint, iliqskin, dbz, nx END DO END DO END DO + !$OMP END DO ! Input pressure is Pa, but we need hPa in calculations IF (sn0 .EQ. 0) THEN + !$OMP DO COLLAPSE(3) DO k = 1,nz DO j = 1,ny DO i = 1,nx @@ -110,12 +115,17 @@ SUBROUTINE CALCDBZ(prs, tmk, qvp, qra, qsn, qgr, sn0, ivarint, iliqskin, dbz, nx END DO END DO END DO + !$OMP END DO END IF factor_r = GAMMA_SEVEN*1.D18*(1.D0/(PI*RHO_R))**1.75D0 factor_s = GAMMA_SEVEN*1.D18*(1.D0/(PI*RHO_S))**1.75D0*(RHO_S/RHOWAT)**2*ALPHA factor_g = GAMMA_SEVEN*1.D18*(1.D0/(PI*RHO_G))**1.75D0*(RHO_G/RHOWAT)**2*ALPHA + + !$OMP DO COLLAPSE(3) PRIVATE(i, j, k, temp_c, virtual_t, gonv, ronv, sonv, & + !$OMP factorb_g, factorb_s, rhoair, z_e) & + !$OMP FIRSTPRIVATE(factor_r, factor_s, factor_g) DO k = 1,nz DO j = 1,ny DO i = 1,nx @@ -171,6 +181,9 @@ SUBROUTINE CALCDBZ(prs, tmk, qvp, qra, qsn, qgr, sn0, ivarint, iliqskin, dbz, nx END DO END DO END DO + !$OMP END DO + + !$OMP END PARALLEL RETURN diff --git a/fortran/wrf_wind.f90 b/fortran/wrf_wind.f90 index b08c019..88d022e 100644 --- a/fortran/wrf_wind.f90 +++ b/fortran/wrf_wind.f90 @@ -13,11 +13,13 @@ SUBROUTINE DCOMPUTEWSPD(wspd, u, v, nx, ny) INTEGER i, j + !$OMP PARALLEL DO COLLAPSE(2) DO j = 1,ny DO i = 1,nx wspd(i,j) = SQRT(u(i,j)*u(i,j) + v(i,j)*v(i,j)) END DO END DO + !$OMP END PARALLEL DO END SUBROUTINE DCOMPUTEWSPD @@ -38,11 +40,13 @@ SUBROUTINE DCOMPUTEWDIR(wdir, u, v, nx, ny) INTEGER i, j + !$OMP PARALLEL DO COLLAPSE(2) DO j = 1,ny DO i = 1,nx wdir(i,j) = MOD(270.0 - ATAN2(v(i,j), u(i,j)) * DEG_PER_RAD, 360.) END DO END DO + !$OMP END PARALLEL DO END SUBROUTINE DCOMPUTEWDIR diff --git a/test/utests.py b/test/utests.py index b7b26f1..51f0a97 100644 --- a/test/utests.py +++ b/test/utests.py @@ -143,7 +143,11 @@ def make_test(varname, wrf_in, referent, multi=False, repeat=3, pynio=False): tol = .5/100.0 atol = 0 # NCL uses different constants and doesn't use same # handrolled virtual temp in method - nt.assert_allclose(to_np(my_vals), ref_vals, tol, atol) + try: + nt.assert_allclose(to_np(my_vals), ref_vals, tol, atol) + except AssertionError: + print (np.amax(np.abs(to_np(my_vals) - ref_vals))) + raise elif (varname == "cape_2d"): cape_2d = getvar(in_wrfnc, varname, timeidx=timeidx) tol = 0/100. @@ -264,22 +268,25 @@ def make_interp_test(varname, wrf_in, referent, multi=False, pivot_point = CoordPair(hts.shape[-1] / 2, hts.shape[-2] / 2) ht_cross = vertcross(hts, p, pivot_point=pivot_point, angle=90.) - - nt.assert_allclose(to_np(ht_cross), ref_ht_cross, rtol=.01) + + # Note: Until the bug is fixed in NCL, the wrf-python cross + # sections will contain one extra point + nt.assert_allclose(to_np(ht_cross)[...,0:-1], ref_ht_cross, rtol=.01) # Test opposite p_cross1 = vertcross(p,hts,pivot_point=pivot_point, angle=90.0) - - nt.assert_allclose(to_np(p_cross1), + + nt.assert_allclose(to_np(p_cross1)[...,0:-1], ref_p_cross, rtol=.01) # Test point to point start_point = CoordPair(0, hts.shape[-2]/2) end_point = CoordPair(-1,hts.shape[-2]/2) - + + p_cross2 = vertcross(p,hts,start_point=start_point, end_point=end_point) - + nt.assert_allclose(to_np(p_cross1), to_np(p_cross2)) @@ -292,7 +299,8 @@ def make_interp_test(varname, wrf_in, referent, multi=False, t2_line1 = interpline(t2, pivot_point=pivot_point, angle=90.0) - nt.assert_allclose(to_np(t2_line1), ref_t2_line) + # Note: After NCL is fixed, remove the slice. + nt.assert_allclose(to_np(t2_line1)[...,0:-1], ref_t2_line) # Test point to point start_point = CoordPair(0, t2.shape[-2]/2) From 7d637dff04a66512042f8d10c450fdb1642a9181 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Thu, 9 Nov 2017 16:59:04 -0700 Subject: [PATCH 12/43] Sets cutoff point for Lambert Conformal so that southern hemisphere works. Fixes #31. --- src/wrf/projection.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/wrf/projection.py b/src/wrf/projection.py index f8b95d5..029bfa3 100644 --- a/src/wrf/projection.py +++ b/src/wrf/projection.py @@ -574,12 +574,16 @@ class LambertConformal(WrfProj): def _cartopy(self): if not cartopy_enabled(): return None - + + # Set cutoff to -30 for NH, +30.0 for SH. + cutoff = -30.0 if self.moad_cen_lat >= 0 else 30.0 + _cartopy = crs.LambertConformal( central_longitude = self.stand_lon, central_latitude = self.moad_cen_lat, standard_parallels = self._std_parallels, - globe = self._globe()) + globe = self._globe(), + cutoff = cutoff) return _cartopy From ddd879586fcfcda4f6275702cad13dda5568680a Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Tue, 14 Nov 2017 16:00:29 -0700 Subject: [PATCH 13/43] Fix typo with missing geobounds argument for PolarStereographic. --- src/wrf/projection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wrf/projection.py b/src/wrf/projection.py index 029bfa3..d5b6670 100644 --- a/src/wrf/projection.py +++ b/src/wrf/projection.py @@ -791,7 +791,7 @@ class PolarStereographic(WrfProj): return _pyngl - def _basemap(self, **kwargs): + def _basemap(self, geobounds, **kwargs): if not basemap_enabled(): return None From fbb2555eae396344c9eeb9b4408e868f14d6c5e9 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Thu, 16 Nov 2017 16:22:25 -0700 Subject: [PATCH 14/43] Performance improvements --- fortran/wrf_vinterp.f90 | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/fortran/wrf_vinterp.f90 b/fortran/wrf_vinterp.f90 index 7f7c2f1..bbcf168 100644 --- a/fortran/wrf_vinterp.f90 +++ b/fortran/wrf_vinterp.f90 @@ -184,14 +184,6 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& cvcord = 't' END IF - !$OMP PARALLEL DO COLLAPSE(2) - DO j = 1,ns - DO i = 1,ew - tempout(i,j) = rmsg - END DO - END DO - !$OMP END PARALLEL DO - DO nreqlvs = 1,numlevels IF (cvcord .EQ. 'z') THEN ! Convert rlevel to meters from km @@ -211,6 +203,7 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& !$OMP vt, tlev, gammam, e, tlcl) REDUCTION (+:log_errcnt, interp_errcnt) DO j=1,ns DO i=1,ew + tempout(i,j) = rmsg ! Get the interpolated value that is within the model domain ifound = 0 DO k = 1,nz-1 @@ -411,8 +404,6 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& END DO !$OMP END PARALLEL DO - - IF (log_errcnt > 0) THEN errstat = ALGERR WRITE(errmsg, *) "Pres=0. Unable to take log of 0." From b0a6f9e26836ea56c691f6fd7a0e09d452c8b0f9 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Thu, 16 Nov 2017 16:25:06 -0700 Subject: [PATCH 15/43] Performance improvements. --- src/wrf/interp.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/wrf/interp.py b/src/wrf/interp.py index a2e67aa..8741596 100755 --- a/src/wrf/interp.py +++ b/src/wrf/interp.py @@ -584,9 +584,6 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False, terht = get_terrain(wrfin, timeidx, units="m", method=method, squeeze=squeeze, cache=cache, meta=False, _key=_key) - t = get_theta(wrfin, timeidx, units="k", - method=method, squeeze=squeeze, cache=cache, - meta=False, _key=_key) tk = get_temp(wrfin, timeidx, units="k", method=method, squeeze=squeeze, cache=cache, meta=False, _key=_key) @@ -596,9 +593,6 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False, ght = get_height(wrfin, timeidx, msl=True, units="m", method=method, squeeze=squeeze, cache=cache, meta=False, _key=_key) - ht_agl = get_height(wrfin, timeidx, msl=False, units="m", - method=method, squeeze=squeeze, cache=cache, - meta=False, _key=_key) smsfp = _smooth2d(sfp, 3) @@ -613,10 +607,18 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False, vcord_array = np.exp(-ght/sclht) elif vert_coord == "ght_agl": + ht_agl = get_height(wrfin, timeidx, msl=False, units="m", + method=method, squeeze=squeeze, cache=cache, + meta=False, _key=_key) + vcor = 3 vcord_array = np.exp(-ht_agl/sclht) elif vert_coord in ("theta", "th"): + t = get_theta(wrfin, timeidx, units="k", + method=method, squeeze=squeeze, cache=cache, + meta=False, _key=_key) + vcor = 4 idir = 1 icorsw = 0 From 3056a34ea21b6e05cb1dd40581e464a4d953975e Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Thu, 16 Nov 2017 16:46:03 -0700 Subject: [PATCH 16/43] Fix parallel computation problem with wrf_monotonic --- fortran/wrf_vinterp.f90 | 14 +++++++------- src/wrf/interp.py | 14 ++++++-------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/fortran/wrf_vinterp.f90 b/fortran/wrf_vinterp.f90 index bbcf168..3357050 100644 --- a/fortran/wrf_vinterp.f90 +++ b/fortran/wrf_vinterp.f90 @@ -20,24 +20,24 @@ SUBROUTINE wrf_monotonic(out, in, lvprs, cor, idir, delta, ew, ns, nz, icorsw) !NCLEND - INTEGER :: i, j, k, k300 + INTEGER :: i, j, k, ripk, k300 - !$OMP PARALLEL DO COLLAPSE(2) + k300 = 1 ! removes the warning + + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i, j, k, ripk) FIRSTPRIVATE(k300) DO j=1,ns DO i=1,ew - IF (icorsw .EQ. 1 .AND. cor(i,j) .LT. 0.) THEN DO k=1,nz in(i,j,k) = -in(i,j,k) END DO END IF - k300 = nz - ! First find k index that is at or below (height-wise) ! the 300 hPa level. - DO k = nz,1,-1 - IF (lvprs(i,j,k) .GE. 300.D0) THEN + DO k = 1,nz + ripk = nz-k+1 + IF (lvprs(i,j,k) .LE. 300.D0) THEN k300 = k EXIT END IF diff --git a/src/wrf/interp.py b/src/wrf/interp.py index 8741596..a2e67aa 100755 --- a/src/wrf/interp.py +++ b/src/wrf/interp.py @@ -584,6 +584,9 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False, terht = get_terrain(wrfin, timeidx, units="m", method=method, squeeze=squeeze, cache=cache, meta=False, _key=_key) + t = get_theta(wrfin, timeidx, units="k", + method=method, squeeze=squeeze, cache=cache, + meta=False, _key=_key) tk = get_temp(wrfin, timeidx, units="k", method=method, squeeze=squeeze, cache=cache, meta=False, _key=_key) @@ -593,6 +596,9 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False, ght = get_height(wrfin, timeidx, msl=True, units="m", method=method, squeeze=squeeze, cache=cache, meta=False, _key=_key) + ht_agl = get_height(wrfin, timeidx, msl=False, units="m", + method=method, squeeze=squeeze, cache=cache, + meta=False, _key=_key) smsfp = _smooth2d(sfp, 3) @@ -607,18 +613,10 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False, vcord_array = np.exp(-ght/sclht) elif vert_coord == "ght_agl": - ht_agl = get_height(wrfin, timeidx, msl=False, units="m", - method=method, squeeze=squeeze, cache=cache, - meta=False, _key=_key) - vcor = 3 vcord_array = np.exp(-ht_agl/sclht) elif vert_coord in ("theta", "th"): - t = get_theta(wrfin, timeidx, units="k", - method=method, squeeze=squeeze, cache=cache, - meta=False, _key=_key) - vcor = 4 idir = 1 icorsw = 0 From d66a81a98a514c0f5350adc5695d6cda897ebf6d Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Fri, 17 Nov 2017 14:27:54 -0700 Subject: [PATCH 17/43] Fixed wrf_monotonic performance issues caused by an accidental revert --- fortran/wrf_vinterp.f90 | 17 +++++++++++------ src/wrf/interp.py | 14 ++++++++------ 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/fortran/wrf_vinterp.f90 b/fortran/wrf_vinterp.f90 index 3357050..d5c18c1 100644 --- a/fortran/wrf_vinterp.f90 +++ b/fortran/wrf_vinterp.f90 @@ -20,13 +20,13 @@ SUBROUTINE wrf_monotonic(out, in, lvprs, cor, idir, delta, ew, ns, nz, icorsw) !NCLEND - INTEGER :: i, j, k, ripk, k300 + INTEGER :: i, j, k, k300 - k300 = 1 ! removes the warning - - !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i, j, k, ripk) FIRSTPRIVATE(k300) + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i, j, k, k300) DO j=1,ns DO i=1,ew + k300 = -1 + IF (icorsw .EQ. 1 .AND. cor(i,j) .LT. 0.) THEN DO k=1,nz in(i,j,k) = -in(i,j,k) @@ -35,14 +35,19 @@ SUBROUTINE wrf_monotonic(out, in, lvprs, cor, idir, delta, ew, ns, nz, icorsw) ! First find k index that is at or below (height-wise) ! the 300 hPa level. - DO k = 1,nz - ripk = nz-k+1 + DO k = 1,nz-1 IF (lvprs(i,j,k) .LE. 300.D0) THEN k300 = k EXIT END IF END DO + ! If the search fails for some reason, use the second to last + ! k index + IF (k300 .EQ. -1) THEN + k300 = nz-1 + END IF + DO k = k300,1,-1 IF (idir .EQ. 1) THEN out(i,j,k) = MIN(in(i,j,k), in(i,j,k+1) + delta) diff --git a/src/wrf/interp.py b/src/wrf/interp.py index a2e67aa..8741596 100755 --- a/src/wrf/interp.py +++ b/src/wrf/interp.py @@ -584,9 +584,6 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False, terht = get_terrain(wrfin, timeidx, units="m", method=method, squeeze=squeeze, cache=cache, meta=False, _key=_key) - t = get_theta(wrfin, timeidx, units="k", - method=method, squeeze=squeeze, cache=cache, - meta=False, _key=_key) tk = get_temp(wrfin, timeidx, units="k", method=method, squeeze=squeeze, cache=cache, meta=False, _key=_key) @@ -596,9 +593,6 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False, ght = get_height(wrfin, timeidx, msl=True, units="m", method=method, squeeze=squeeze, cache=cache, meta=False, _key=_key) - ht_agl = get_height(wrfin, timeidx, msl=False, units="m", - method=method, squeeze=squeeze, cache=cache, - meta=False, _key=_key) smsfp = _smooth2d(sfp, 3) @@ -613,10 +607,18 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False, vcord_array = np.exp(-ght/sclht) elif vert_coord == "ght_agl": + ht_agl = get_height(wrfin, timeidx, msl=False, units="m", + method=method, squeeze=squeeze, cache=cache, + meta=False, _key=_key) + vcor = 3 vcord_array = np.exp(-ht_agl/sclht) elif vert_coord in ("theta", "th"): + t = get_theta(wrfin, timeidx, units="k", + method=method, squeeze=squeeze, cache=cache, + meta=False, _key=_key) + vcor = 4 idir = 1 icorsw = 0 From 819cdfe0783f55522823a7da9a0530e1d4875179 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Fri, 17 Nov 2017 16:39:42 -0700 Subject: [PATCH 18/43] Cleaned up cape fortran routine. Fixed issue with generators not being copied correctly due to changes in python (the python works as expected now). Fixed a uniqueness problem with coordinate caching which was causing problems in jupyter notebook when files were changed. Fixed an issue with the cache test script failing due to unitialized thread local data in child threads. Fixes #34. Fixes #14. --- fortran/eqthecalc.f90 | 3 +- fortran/rip_cape.f90 | 950 ++++++++++++++++++++--------------------- src/wrf/config.py | 107 +++-- src/wrf/util.py | 76 ++-- test/cachetest.py | 7 +- test/generator_test.py | 17 + 6 files changed, 617 insertions(+), 543 deletions(-) create mode 100644 test/generator_test.py diff --git a/fortran/eqthecalc.f90 b/fortran/eqthecalc.f90 index d805be9..33cad88 100644 --- a/fortran/eqthecalc.f90 +++ b/fortran/eqthecalc.f90 @@ -32,8 +32,7 @@ SUBROUTINE DEQTHECALC(qvp, tmk, prs, eth, miy, mjx, mkzh) REAL(KIND=8) :: tlcl INTEGER :: i, j, k - ! Note: removing temporaries did not improve performance for this algorithm. - !$OMP PARALLEL DO COLLAPSE(3) PRIVATE(i,j,k,q,t,p,e,tlcl) + !$OMP PARALLEL DO COLLAPSE(3) PRIVATE(i, j, k, q, t, p, e, tlcl) DO k = 1,mkzh DO j = 1,mjx DO i = 1,miy diff --git a/fortran/rip_cape.f90 b/fortran/rip_cape.f90 index d474702..d6a5fec 100644 --- a/fortran/rip_cape.f90 +++ b/fortran/rip_cape.f90 @@ -227,24 +227,27 @@ SUBROUTINE DPFCALC(prs, sfp, pf, mix, mjy, mkzh, ter_follow) INTEGER :: i,j,k + !$OMP PARALLEL DO COLLAPSE(3) DO j = 1,mjy - DO i = 1,mix - DO k = 1,mkzh - IF (k .EQ. mkzh) THEN - ! terrain-following data - IF (ter_follow .EQ. 1) THEN - pf(k,i,j) = sfp(i,j) - ! pressure-level data - ELSE - pf(k,i,j) = .5D0 * (3.D0*prs(k,i,j) - prs(k-1,i,j)) - END IF - ELSE - pf(k,i,j) = .5D0 * (prs(k+1,i,j) + prs(k,i,j)) - END IF - END DO - END DO + DO i = 1,mix + DO k = 1,mkzh + IF (k .EQ. mkzh) THEN + ! terrain-following data + IF (ter_follow .EQ. 1) THEN + pf(k,i,j) = sfp(i,j) + ! pressure-level data + ELSE + pf(k,i,j) = .5D0 * (3.D0*prs(k,i,j) - prs(k-1,i,j)) + END IF + ELSE + pf(k,i,j) = .5D0 * (prs(k+1,i,j) + prs(k,i,j)) + END IF + END DO + END DO END DO + !$OMP END PARALLEL DO + RETURN END SUBROUTINE DPFCALC @@ -351,19 +354,16 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& ! calculated the pressure at full sigma levels (a set of pressure ! levels that bound the layers represented by the vertical grid points) - !CALL cpu_time(t1) - !CALL OMP_SET_NUM_THREADS(16) - - !$OMP PARALLEL DO + !$OMP PARALLEL DO COLLAPSE(3) DO j = 1,mjy - DO i = 1,mix - DO k = 1,mkzh - prs_new(k,i,j) = prs(i,j,k) - tmk_new(k,i,j) = tmk(i,j,k) - qvp_new(k,i,j) = qvp(i,j,k) - ght_new(k,i,j) = ght(i,j,k) - END DO - END DO + DO i = 1,mix + DO k = 1,mkzh + prs_new(k,i,j) = prs(i,j,k) + tmk_new(k,i,j) = tmk(i,j,k) + qvp_new(k,i,j) = qvp(i,j,k) + ght_new(k,i,j) = ght(i,j,k) + END DO + END DO END DO !$OMP END PARALLEL DO @@ -383,195 +383,193 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& !$OMP facden, tmkenv, qvpenv, eslift, qvplift, buoy, benamin, & !$OMP benaccum, zrel, kmax, dz, elfound, & !$OMP kel, klfc, & - !$OMP i,j,k,kpar) + !$OMP i, j, k, kpar) DO j = 1,mjy - DO i = 1,mix - cape(i,j,1) = 0.D0 - cin(i,j,1) = 0.D0 + DO i = 1,mix + cape(i,j,1) = 0.D0 + cin(i,j,1) = 0.D0 -!!$OMP SIMD - DO kpar = 2, mkzh + !!$OMP SIMD + DO kpar = 2, mkzh - ! Calculate temperature and moisture properties of parcel - ! (note, qvppari and tmkpari already calculated above for 2d case.) + ! Calculate temperature and moisture properties of parcel + ! (note, qvppari and tmkpari already calculated above for 2d case.) - tlcl = TLCLC1/(LOG(tmk_new(kpar,i,j)**TLCLC2/(MAX(1.D-20,qvp_new(kpar,i,j)*prs_new(kpar,i,j)/ & - (EPS + qvp_new(kpar,i,j))))) - TLCLC3) + TLCLC4 + tlcl = TLCLC1/(LOG(tmk_new(kpar,i,j)**TLCLC2/(MAX(1.D-20,qvp_new(kpar,i,j)*prs_new(kpar,i,j)/ & + (EPS + qvp_new(kpar,i,j))))) - TLCLC3) + TLCLC4 - ethpari = tmk_new(kpar,i,j)*(1000.D0/prs_new(kpar,i,j))**(GAMMA*(1.D0 + GAMMAMD*qvp_new(kpar,i,j)))* & - EXP((THTECON1/tlcl - THTECON2)*qvp_new(kpar,i,j)*(1.D0 + THTECON3*qvp_new(kpar,i,j))) + ethpari = tmk_new(kpar,i,j)*(1000.D0/prs_new(kpar,i,j))**(GAMMA*(1.D0 + GAMMAMD*qvp_new(kpar,i,j)))* & + EXP((THTECON1/tlcl - THTECON2)*qvp_new(kpar,i,j)*(1.D0 + THTECON3*qvp_new(kpar,i,j))) - zlcl = ght_new(kpar,i,j) + (tmk_new(kpar,i,j) - tlcl)/(G/CP * (1.D0 + CPMD*qvp_new(kpar,i,j))) - - ! DO k = kpar,1,-1 - ! tmklift_new(k) = TONPSADIABAT(ethpari, prs_new(k,i,j), psadithte, psadiprs,& - ! psaditmk, GAMMA, errstat, errmsg) - ! END DO - ! Calculate buoyancy and relative height of lifted parcel at - ! all levels, and store in bottom up arrays. add a level at the lcl, - ! and at all points where buoyancy is zero. - ! - ! For arrays that go bottom to top - kk = 0 - ilcl = 0 - - IF (ght_new(kpar,i,j) .GE. zlcl) THEN - ! Initial parcel already saturated or supersaturated. - ilcl = 2 - klcl = 1 - END IF - - !!$OMP SIMD lastprivate(qvplift,tmklift,ghtlift,tvlift,tmkenv,qvpenv,tvenv,eslift,facden) - DO k = kpar,1,-1 - ! For arrays that go bottom to top - kk = kk + 1 - - ! Model level is below lcl - IF (ght_new(k,i,j) .LT. zlcl) THEN - tmklift = tmk_new(kpar,i,j) - G/(CP * (1.D0 + CPMD*qvp_new(kpar,i,j))) * (ght_new(k,i,j) - ght_new(kpar,i,j)) - tvenv = tmk_new(k,i,j)*(EPS + qvp_new(k,i,j))/(EPS*(1.D0 + qvp_new(k,i,j))) - tvlift = tmklift*(EPS + qvp_new(kpar,i,j))/(EPS*(1.D0 + qvp_new(kpar,i,j))) - ghtlift = ght_new(k,i,j) - ELSE IF (ght(i,j,k) .GE. zlcl .AND. ilcl .EQ. 0) THEN - ! This model level and previous model level straddle the lcl, - ! so first create a new level in the bottom-up array, at the lcl. - facden = 1.0/(ght_new(k,i,j) - ght_new(k+1,i,j)) - tmkenv = tmk_new(k+1,i,j)*((ght_new(k,i,j)-zlcl)*facden) + tmk_new(k,i,j)*((zlcl-ght_new(k+1,i,j))*facden) - qvpenv = qvp_new(k+1,i,j)*((ght_new(k,i,j)-zlcl)*facden) + qvp_new(k,i,j)*((zlcl-ght_new(k+1,i,j))*facden) - tvenv = tmkenv* (EPS + qvpenv) / (EPS * (1.D0 + qvpenv)) - tvlift = tlcl* (EPS + qvp_new(kpar,i,j)) / (EPS *(1.D0 + qvp_new(kpar,i,j))) - ghtlift = zlcl - ilcl = 1 - ELSE - tmklift = TONPSADIABAT(ethpari, prs_new(k,i,j), psadithte, psadiprs,& + zlcl = ght_new(kpar,i,j) + (tmk_new(kpar,i,j) - tlcl)/(G/CP * (1.D0 + CPMD*qvp_new(kpar,i,j))) + + ! Calculate buoyancy and relative height of lifted parcel at + ! all levels, and store in bottom up arrays. add a level at the lcl, + ! and at all points where buoyancy is zero. + ! + ! For arrays that go bottom to top + kk = 0 + ilcl = 0 + + IF (ght_new(kpar,i,j) .GE. zlcl) THEN + ! Initial parcel already saturated or supersaturated. + ilcl = 2 + klcl = 1 + END IF + + !!$OMP SIMD lastprivate(qvplift,tmklift,ghtlift,tvlift,tmkenv,qvpenv,tvenv,eslift,facden) + DO k = kpar,1,-1 + ! For arrays that go bottom to top + kk = kk + 1 + + ! Model level is below lcl + IF (ght_new(k,i,j) .LT. zlcl) THEN + tmklift = tmk_new(kpar,i,j) - G/(CP * (1.D0 + CPMD*qvp_new(kpar,i,j)))*& + (ght_new(k,i,j) - ght_new(kpar,i,j)) + tvenv = tmk_new(k,i,j)*(EPS + qvp_new(k,i,j))/(EPS*(1.D0 + qvp_new(k,i,j))) + tvlift = tmklift*(EPS + qvp_new(kpar,i,j))/(EPS*(1.D0 + qvp_new(kpar,i,j))) + ghtlift = ght_new(k,i,j) + ELSE IF (ght(i,j,k) .GE. zlcl .AND. ilcl .EQ. 0) THEN + ! This model level and previous model level straddle the lcl, + ! so first create a new level in the bottom-up array, at the lcl. + facden = 1.0/(ght_new(k,i,j) - ght_new(k+1,i,j)) + tmkenv = tmk_new(k+1,i,j)*((ght_new(k,i,j)-zlcl)*facden) + tmk_new(k,i,j)*& + ((zlcl-ght_new(k+1,i,j))*facden) + qvpenv = qvp_new(k+1,i,j)*((ght_new(k,i,j)-zlcl)*facden) + qvp_new(k,i,j)*& + ((zlcl-ght_new(k+1,i,j))*facden) + tvenv = tmkenv* (EPS + qvpenv) / (EPS * (1.D0 + qvpenv)) + tvlift = tlcl* (EPS + qvp_new(kpar,i,j)) / (EPS *(1.D0 + qvp_new(kpar,i,j))) + ghtlift = zlcl + ilcl = 1 + ELSE + tmklift = TONPSADIABAT(ethpari, prs_new(k,i,j), psadithte, psadiprs,& psaditmk, GAMMA, errstat, errmsg) - eslift = EZERO*EXP(ESLCON1*(tmklift - CELKEL)/(tmklift - ESLCON2)) - qvplift = EPS*eslift/(prs_new(k,i,j) - eslift) - tvenv = tmk_new(k,i,j) * (EPS + qvp_new(k,i,j)) / (EPS * (1.D0 + qvp_new(k,i,j))) - tvlift = tmklift*(EPS + qvplift) / (EPS * (1.D0 + qvplift)) - ghtlift = ght_new(k,i,j) - END IF - ! Buoyancy - buoy(kk) = G*(tvlift - tvenv)/tvenv - zrel(kk) = ghtlift - ght_new(kpar,i,j) - IF ((kk .GT. 1) .AND. (buoy(kk)*buoy(kk-1) .LT. 0.0D0)) THEN - ! Parcel ascent curve crosses sounding curve, so create a new level - ! in the bottom-up array at the crossing. - kk = kk + 1 - buoy(kk) = buoy(kk-1) - zrel(kk) = zrel(kk-1) - buoy(kk-1) = 0.D0 - zrel(kk-1) = zrel(kk-2) + buoy(kk-2)/& - (buoy(kk-2) - buoy(kk))*(zrel(kk) - zrel(kk-2)) - END IF - IF (ilcl .EQ. 1) THEN - klcl = kk - ilcl = 2 - CYCLE - END IF - - END DO + eslift = EZERO*EXP(ESLCON1*(tmklift - CELKEL)/(tmklift - ESLCON2)) + qvplift = EPS*eslift/(prs_new(k,i,j) - eslift) + tvenv = tmk_new(k,i,j) * (EPS + qvp_new(k,i,j)) / (EPS * (1.D0 + qvp_new(k,i,j))) + tvlift = tmklift*(EPS + qvplift) / (EPS * (1.D0 + qvplift)) + ghtlift = ght_new(k,i,j) + END IF + ! Buoyancy + buoy(kk) = G*(tvlift - tvenv)/tvenv + zrel(kk) = ghtlift - ght_new(kpar,i,j) + IF ((kk .GT. 1) .AND. (buoy(kk)*buoy(kk-1) .LT. 0.0D0)) THEN + ! Parcel ascent curve crosses sounding curve, so create a new level + ! in the bottom-up array at the crossing. + kk = kk + 1 + buoy(kk) = buoy(kk-1) + zrel(kk) = zrel(kk-1) + buoy(kk-1) = 0.D0 + zrel(kk-1) = zrel(kk-2) + buoy(kk-2)/& + (buoy(kk-2) - buoy(kk))*(zrel(kk) - zrel(kk-2)) + END IF + IF (ilcl .EQ. 1) THEN + klcl = kk + ilcl = 2 + CYCLE + END IF + + END DO - kmax = kk - ! IF (kmax .GT. 150) THEN - ! print *,'kmax got too big' - ! errstat = ALGERR - ! WRITE(errmsg, *) 'capecalc3d: kmax got too big. kmax=',kmax - ! RETURN - ! END IF - - ! If no lcl was found, set klcl to kmax. it is probably not really - ! at kmax, but this will make the rest of the routine behave - ! properly. - IF (ilcl .EQ. 0) klcl=kmax - - ! Get the accumulated buoyant energy from the parcel's starting - ! point, at all levels up to the top level. - benaccum(1) = 0.0D0 - benamin = 9d9 - DO k = 2,kmax - dz = zrel(k) - zrel(k-1) - benaccum(k) = benaccum(k-1) + .5D0*dz*(buoy(k-1) + buoy(k)) - IF (benaccum(k) .LT. benamin) THEN - benamin = benaccum(k) - END IF - END DO - ! Determine equilibrium level (el), which we define as the highest - ! level of non-negative buoyancy above the lcl. note, this may be - ! the top level if the parcel is still buoyant there. - - elfound = .FALSE. - DO k = kmax,klcl,-1 - IF (buoy(k) .GE. 0.D0) THEN - ! k of equilibrium level - kel = k - elfound = .TRUE. - EXIT - END IF - END DO - - ! If we got through that loop, then there is no non-negative - ! buoyancy above the lcl in the sounding. in these situations, - ! both cape and cin will be set to -0.1 j/kg. (see below about - ! missing values in v6.1.0). also, where cape is - ! non-zero, cape and cin will be set to a minimum of +0.1 j/kg, so - ! that the zero contour in either the cin or cape fields will - ! circumscribe regions of non-zero cape. - - ! In v6.1.0 of ncl, we added a _fillvalue attribute to the return - ! value of this function. at that time we decided to change -0.1 - ! to a more appropriate missing value, which is passed into this - ! routine as cmsg. - - IF (.NOT. elfound) THEN - !print *,'el not found' - cape(i,j,kpar) = cmsg - cin(i,j,kpar) = cmsg - klfc = kmax - CYCLE - END IF - - ! If there is an equilibrium level, then cape is positive. we'll - ! define the level of free convection (lfc) as the point below the - ! el, but at or above the lcl, where accumulated buoyant energy is a - ! minimum. the net positive area (accumulated buoyant energy) from - ! the lfc up to the el will be defined as the cape, and the net - ! negative area (negative of accumulated buoyant energy) from the - ! parcel starting point to the lfc will be defined as the convective - ! inhibition (cin). - - ! First get the lfc according to the above definition. - benamin = 9D9 - klfc = kmax - DO k = klcl,kel - IF (benaccum(k) .LT. benamin) THEN - benamin = benaccum(k) - klfc = k - END IF - END DO - - ! Now we can assign values to cape and cin - - cape(i,j,kpar) = MAX(benaccum(kel)-benamin, 0.1D0) - cin(i,j,kpar) = MAX(-benamin, 0.1D0) - - ! cin is uninteresting when cape is small (< 100 j/kg), so set - ! cin to -0.1 (see note about missing values in v6.1.0) in - ! that case. - - ! In v6.1.0 of ncl, we added a _fillvalue attribute to the return - ! value of this function. at that time we decided to change -0.1 - ! to a more appropriate missing value, which is passed into this - ! routine as cmsg. - - IF (cape(i,j,kpar) .LT. 100.D0) cin(i,j,kpar) = cmsg - - END DO - END DO + kmax = kk + ! IF (kmax .GT. 150) THEN + ! print *,'kmax got too big' + ! errstat = ALGERR + ! WRITE(errmsg, *) 'capecalc3d: kmax got too big. kmax=',kmax + ! RETURN + ! END IF + + ! If no lcl was found, set klcl to kmax. it is probably not really + ! at kmax, but this will make the rest of the routine behave + ! properly. + IF (ilcl .EQ. 0) klcl=kmax + + ! Get the accumulated buoyant energy from the parcel's starting + ! point, at all levels up to the top level. + benaccum(1) = 0.0D0 + benamin = 9d9 + DO k = 2,kmax + dz = zrel(k) - zrel(k-1) + benaccum(k) = benaccum(k-1) + .5D0*dz*(buoy(k-1) + buoy(k)) + IF (benaccum(k) .LT. benamin) THEN + benamin = benaccum(k) + END IF + END DO + ! Determine equilibrium level (el), which we define as the highest + ! level of non-negative buoyancy above the lcl. note, this may be + ! the top level if the parcel is still buoyant there. + + elfound = .FALSE. + DO k = kmax,klcl,-1 + IF (buoy(k) .GE. 0.D0) THEN + ! k of equilibrium level + kel = k + elfound = .TRUE. + EXIT + END IF + END DO + + ! If we got through that loop, then there is no non-negative + ! buoyancy above the lcl in the sounding. in these situations, + ! both cape and cin will be set to -0.1 j/kg. (see below about + ! missing values in v6.1.0). also, where cape is + ! non-zero, cape and cin will be set to a minimum of +0.1 j/kg, so + ! that the zero contour in either the cin or cape fields will + ! circumscribe regions of non-zero cape. + + ! In v6.1.0 of ncl, we added a _fillvalue attribute to the return + ! value of this function. at that time we decided to change -0.1 + ! to a more appropriate missing value, which is passed into this + ! routine as cmsg. + + IF (.NOT. elfound) THEN + !print *,'el not found' + cape(i,j,kpar) = cmsg + cin(i,j,kpar) = cmsg + klfc = kmax + CYCLE + END IF + + ! If there is an equilibrium level, then cape is positive. we'll + ! define the level of free convection (lfc) as the point below the + ! el, but at or above the lcl, where accumulated buoyant energy is a + ! minimum. the net positive area (accumulated buoyant energy) from + ! the lfc up to the el will be defined as the cape, and the net + ! negative area (negative of accumulated buoyant energy) from the + ! parcel starting point to the lfc will be defined as the convective + ! inhibition (cin). + + ! First get the lfc according to the above definition. + benamin = 9D9 + klfc = kmax + DO k = klcl,kel + IF (benaccum(k) .LT. benamin) THEN + benamin = benaccum(k) + klfc = k + END IF + END DO + + ! Now we can assign values to cape and cin + + cape(i,j,kpar) = MAX(benaccum(kel)-benamin, 0.1D0) + cin(i,j,kpar) = MAX(-benamin, 0.1D0) + + ! cin is uninteresting when cape is small (< 100 j/kg), so set + ! cin to -0.1 (see note about missing values in v6.1.0) in + ! that case. + + ! In v6.1.0 of ncl, we added a _fillvalue attribute to the return + ! value of this function. at that time we decided to change -0.1 + ! to a more appropriate missing value, which is passed into this + ! routine as cmsg. + + IF (cape(i,j,kpar) .LT. 100.D0) cin(i,j,kpar) = cmsg + + END DO + END DO END DO -!$OMP END PARALLEL DO - !CALL cpu_time(t2) - !print *,'Time taken in seconds ',(t2-t1) + !$OMP END PARALLEL DO + RETURN END SUBROUTINE DCAPECALC3D @@ -590,8 +588,6 @@ END SUBROUTINE DCAPECALC3D ! the cape and cin arrays. Also, LCL and LFC heights ! are put in the k=mkzh-1 and k=mkzh-2 slabs of the cin array. ! - - ! Important! The z-indexes must be arranged so that mkzh (max z-index) is the ! surface pressure. So, pressure must be ordered in ascending order before ! calling this routine. Other variables must be ordered the same (p,tk,q,z). @@ -686,14 +682,14 @@ SUBROUTINE DCAPECALC2D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& !$OMP PARALLEL DO COLLAPSE(3) DO j = 1,mjy - DO i = 1,mix - DO k = 1,mkzh - prs_new(k,i,j) = prs(i,j,k) - tmk_new(k,i,j) = tmk(i,j,k) - qvp_new(k,i,j) = qvp(i,j,k) - ght_new(k,i,j) = ght(i,j,k) - END DO - END DO + DO i = 1,mix + DO k = 1,mkzh + prs_new(k,i,j) = prs(i,j,k) + tmk_new(k,i,j) = tmk(i,j,k) + qvp_new(k,i,j) = qvp(i,j,k) + ght_new(k,i,j) = ght(i,j,k) + END DO + END DO END DO !$OMP END PARALLEL DO @@ -711,274 +707,272 @@ SUBROUTINE DCAPECALC2D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& RETURN END IF - !CALL OMP_SET_NUM_THREADS(16) - !nthreads = omp_get_num_threads() - - !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(tlcl, ethpari, & !$OMP zlcl, kk, ilcl, klcl, tmklift, tvenv, tvlift, ghtlift, & !$OMP facden, tmkenv, qvpenv, eslift, qvplift, buoy, benamin, & !$OMP benaccum, zrel, kmax, dz, elfound, & !$OMP kel, klfc, pavg, p2, p1, totthe, totqvp, totprs, & - !$OMP i,j,k,kpar, kpar1, kpar2, qvppari, tmkpari,p, pup, pdn, q, th, & + !$OMP i, j, k, kpar, kpar1, kpar2, qvppari, tmkpari,p, pup, pdn, q, th, & !$OMP pp1, pp2, ethmax, eth_temp, klev) DO j = 1,mjy - DO i = 1,mix - cape(i,j,1) = 0.D0 - cin(i,j,1) = 0.D0 - ! find parcel with max theta-e in lowest 3 km agl. - ethmax = -1.D0 - eth_temp = -1.D0 - DO k = 1, mkzh - IF (ght_new(k,i,j)-ter(i,j) .LT. 3000.D0) THEN - tlcl = TLCLC1 / (LOG(tmk_new(k,i,j)**TLCLC2/& - (MAX(qvp_new(k,i,j), 1.d-15)*prs_new(k,i,j)/(EPS+MAX(qvp_new(k,i,j), 1.d-15))))-TLCLC3)+& - TLCLC4 - eth_temp(k) = tmk_new(k,i,j) * (1000.D0/prs_new(k,i,j))**& - (GAMMA*(1.D0 + GAMMAMD*(MAX(qvp_new(k,i,j), 1.d-15))))*& - EXP((THTECON1/tlcl - THTECON2)*(MAX(qvp_new(k,i,j), 1.d-15))*& - (1.D0 + THTECON3*(MAX(qvp_new(k,i,j), 1.d-15)))) - END IF - END DO - klev = mkzh - DO k = 1,mkzh - IF (eth_temp(k) .GT. ethmax) THEN - klev = k - ethmax = eth_temp(k) - END IF - END DO - - kpar1 = klev - kpar2 = klev - - - ! Establish average properties of that parcel - ! (over depth of approximately davg meters) - - !davg = 500.D0 - pavg = 500.D0 * prs_new(kpar1,i,j)*& - G/(RD*tvirtual(tmk_new(kpar1,i,j), qvp_new(kpar1,i,j))) - p2 = MIN(prs_new(kpar1,i,j)+.5d0*pavg, prsf(mkzh,i,j)) - p1 = p2 - pavg - totthe = 0.D0 - totqvp = 0.D0 - totprs = 0.D0 - DO k = mkzh,2,-1 - IF (prsf(k,i,j) .LE. p1) EXIT !GOTO 35 - IF (prsf(k-1,i,j) .GE. p2) CYCLE !GOTO 34 - p = prs_new(k,i,j) - pup = prsf(k,i,j) - pdn = prsf(k-1,i,j) - !q = MAX(qvp_new(k,i,j),1.D-15) - th = tmk_new(k,i,j)*(1000.D0/prs_new(k,i,j))**(GAMMA*(1.D0 + GAMMAMD*MAX(qvp_new(k,i,j),1.D-15))) - pp1 = MAX(p1,pdn) - pp2 = MIN(p2,pup) - IF (pp2 .GT. pp1) THEN - ! deltap = pp2 - pp1 - totqvp = totqvp + MAX(qvp_new(k,i,j),1.D-15)*(pp2 - pp1) - totthe = totthe + th*(pp2 - pp1) - totprs = totprs + (pp2 - pp1) - END IF - END DO - qvppari = totqvp/totprs - tmkpari = (totthe/totprs)*& - (prs_new(kpar1,i,j)/1000.D0)**(GAMMA*(1.D0+GAMMAMD*qvp_new(kpar1,i,j))) - -!CALL CPU_TIME(t3) - DO kpar = kpar1, kpar2 - - ! Calculate temperature and moisture properties of parcel - ! (note, qvppari and tmkpari already calculated above for 2d - ! case.) - - !prspari = prs_new(kpar,i,j) - !ghtpari = ght_new(kpar,i,j) - gammam = GAMMA * (1.D0 + GAMMAMD*qvppari) - cpm = CP * (1.D0 + CPMD*qvppari) - - e = MAX(1.D-20,qvppari*prs_new(kpar,i,j)/(EPS + qvppari)) - tlcl = TLCLC1/(LOG(tmkpari**TLCLC2/e) - TLCLC3) + TLCLC4 - ethpari = tmkpari*(1000.D0/prs_new(kpar,i,j))**(GAMMA*(1.D0 + GAMMAMD*qvppari))*& - EXP((THTECON1/tlcl - THTECON2)*qvppari*(1.D0 + THTECON3*qvppari)) - zlcl = ght_new(kpar,i,j) + (tmkpari - tlcl)/(G/cpm) - - ! Calculate buoyancy and relative height of lifted parcel at - ! all levels, and store in bottom up arrays. add a level at the - ! lcl, - ! and at all points where buoyancy is zero. - ! - ! - ! For arrays that go bottom to top - kk = 0 - ilcl = 0 - - IF (ght_new(kpar,i,j) .GE. zlcl) THEN - ! Initial parcel already saturated or supersaturated. - ilcl = 2 - klcl = 1 - END IF - - k = kpar - DO k = kpar,1,-1 - ! For arrays that go bottom to top - kk = kk + 1 - - ! Model level is below lcl - IF (ght_new(k,i,j) .LT. zlcl) THEN - tmklift = tmk_new(kpar,i,j) - G/(CP * (1.D0 + CPMD*qvp_new(kpar,i,j))) * (ght_new(k,i,j) - ght_new(kpar,i,j)) - tvenv = tmk_new(k,i,j)*(EPS + qvp_new(k,i,j))/(EPS*(1.D0 + qvp_new(k,i,j))) - tvlift = tmklift*(EPS + qvp_new(kpar,i,j))/(EPS*(1.D0 + qvp_new(kpar,i,j))) - ghtlift = ght_new(k,i,j) - ELSE IF (ght(i,j,k) .GE. zlcl .AND. ilcl .EQ. 0) THEN - ! This model level and previous model level straddle the lcl, - ! so first create a new level in the bottom-up array, at the lcl. - facden = 1/(ght_new(k,i,j) - ght_new(k+1,i,j)) - tmkenv = tmk_new(k+1,i,j)*((ght_new(k,i,j)-zlcl)*facden) + tmk_new(k,i,j)*((zlcl-ght_new(k+1,i,j))*facden) - qvpenv = qvp_new(k+1,i,j)*((ght_new(k,i,j)-zlcl)*facden) + qvp_new(k,i,j)*((zlcl-ght_new(k+1,i,j))*facden) - tvenv = tmkenv* (EPS + qvpenv) / (EPS * (1.D0 + qvpenv)) - tvlift = tlcl* (EPS + qvp_new(kpar,i,j)) / (EPS *(1.D0 + qvp_new(kpar,i,j))) - ghtlift = zlcl - ilcl = 1 - ELSE - tmklift = TONPSADIABAT(ethpari, prs_new(k,i,j), psadithte, psadiprs,& - psaditmk, GAMMA, errstat, errmsg) - eslift = EZERO*EXP(ESLCON1*(tmklift - CELKEL)/(tmklift - ESLCON2)) - qvplift = EPS*eslift/(prs_new(k,i,j) - eslift) - tvenv = tmk_new(k,i,j) * (EPS + qvp_new(k,i,j)) / (EPS * (1.D0 + qvp_new(k,i,j))) - tvlift = tmklift*(EPS + qvplift) / (EPS * (1.D0 + qvplift)) - ghtlift = ght_new(k,i,j) - END IF - ! Buoyancy - buoy(kk) = G*(tvlift - tvenv)/tvenv - zrel(kk) = ghtlift - ght_new(kpar,i,j) - IF ((kk .GT. 1) .AND. (buoy(kk)*buoy(kk-1) .LT. 0.0D0)) THEN - ! Parcel ascent curve crosses sounding curve, so create a new level - ! in the bottom-up array at the crossing. - kk = kk + 1 - buoy(kk) = buoy(kk-1) - zrel(kk) = zrel(kk-1) - buoy(kk-1) = 0.D0 - zrel(kk-1) = zrel(kk-2) + buoy(kk-2)/& - (buoy(kk-2) - buoy(kk))*(zrel(kk) - zrel(kk-2)) - END IF - IF (ilcl .EQ. 1) THEN - klcl = kk - ilcl = 2 - CYCLE - END IF - - END DO - - kmax = kk - ! IF (kmax .GT. 150) THEN - ! errstat = ALGERR - ! WRITE(errmsg, *) 'capecalc3d: kmax got too big. kmax=',kmax - ! RETURN - ! END IF - - ! If no lcl was found, set klcl to kmax. it is probably not - ! really - ! at kmax, but this will make the rest of the routine behave - ! properly. - IF (ilcl .EQ. 0) klcl=kmax - - ! Get the accumulated buoyant energy from the parcel's starting - ! point, at all levels up to the top level. - benaccum(1) = 0.0D0 - benamin = 9d9 - DO k = 2,kmax - dz = zrel(k) - zrel(k-1) - benaccum(k) = benaccum(k-1) + .5D0*dz*(buoy(k-1) + buoy(k)) - IF (benaccum(k) .LT. benamin) THEN - benamin = benaccum(k) - END IF - END DO - ! Determine equilibrium level (el), which we define as the highest - ! level of non-negative buoyancy above the lcl. note, this may be - ! the top level if the parcel is still buoyant there. - - elfound = .FALSE. - DO k = kmax,klcl,-1 - IF (buoy(k) .GE. 0.D0) THEN - ! k of equilibrium level - kel = k - elfound = .TRUE. - EXIT - END IF - END DO - - ! If we got through that loop, then there is no non-negative - ! buoyancy above the lcl in the sounding. in these situations, - ! both cape and cin will be set to -0.1 j/kg. (see below about - ! missing values in v6.1.0). also, where cape is - ! non-zero, cape and cin will be set to a minimum of +0.1 j/kg, so - ! that the zero contour in either the cin or cape fields will - ! circumscribe regions of non-zero cape. - ! In v6.1.0 of ncl, we added a _fillvalue attribute to the return - ! value of this function. at that time we decided to change -0.1 - ! to a more appropriate missing value, which is passed into this - ! routine as cmsg. - - IF (.NOT. elfound) THEN - cape(i,j,kpar) = cmsg - cin(i,j,kpar) = cmsg - klfc = kmax - CYCLE - END IF - - - ! If there is an equilibrium level, then cape is positive. - ! we'll - ! define the level of free convection (lfc) as the point below - ! the - ! el, but at or above the lcl, where accumulated buoyant energy - ! is a - ! minimum. the net positive area (accumulated buoyant energy) - ! from - ! the lfc up to the el will be defined as the cape, and the net - ! negative area (negative of accumulated buoyant energy) from - ! the - ! parcel starting point to the lfc will be defined as the - ! convective - ! inhibition (cin). - - ! First get the lfc according to the above definition. - benamin = 9D9 - klfc = kmax - DO k = klcl,kel - IF (benaccum(k) .LT. benamin) THEN - benamin = benaccum(k) - klfc = k - END IF - END DO - - ! Now we can assign values to cape and cin - - cape(i,j,kpar) = MAX(benaccum(kel)-benamin, 0.1D0) - cin(i,j,kpar) = MAX(-benamin, 0.1D0) - - ! cin is uninteresting when cape is small (< 100 j/kg), so set - ! cin to -0.1 (see note about missing values in v6.1.0) in - ! that case. - - ! In v6.1.0 of ncl, we added a _fillvalue attribute to the return - ! value of this function. at that time we decided to change -0.1 - ! to a more appropriate missing value, which is passed into this - ! routine as cmsg. - - IF (cape(i,j,kpar) .LT. 100.D0) cin(i,j,kpar) = cmsg - - END DO - - cape(i,j,mkzh) = cape(i,j,kpar1) - cin(i,j,mkzh) = cin(i,j,kpar1) - ! meters agl - cin(i,j,mkzh-1) = zrel(klcl) + ght_new(kpar,i,j) - ter(i,j) - ! meters agl - cin(i,j,mkzh-2) = zrel(klfc) + ght_new(kpar,i,j) - ter(i,j) - - END DO + DO i = 1,mix + cape(i,j,1) = 0.D0 + cin(i,j,1) = 0.D0 + ! find parcel with max theta-e in lowest 3 km agl. + ethmax = -1.D0 + eth_temp = -1.D0 + DO k = 1, mkzh + IF (ght_new(k,i,j)-ter(i,j) .LT. 3000.D0) THEN + tlcl = TLCLC1 / (LOG(tmk_new(k,i,j)**TLCLC2/& + (MAX(qvp_new(k,i,j), 1.d-15)*prs_new(k,i,j)/(EPS+MAX(qvp_new(k,i,j), 1.d-15))))-TLCLC3)+& + TLCLC4 + eth_temp(k) = tmk_new(k,i,j) * (1000.D0/prs_new(k,i,j))**& + (GAMMA*(1.D0 + GAMMAMD*(MAX(qvp_new(k,i,j), 1.d-15))))*& + EXP((THTECON1/tlcl - THTECON2)*(MAX(qvp_new(k,i,j), 1.d-15))*& + (1.D0 + THTECON3*(MAX(qvp_new(k,i,j), 1.d-15)))) + END IF + END DO + klev = mkzh + DO k = 1,mkzh + IF (eth_temp(k) .GT. ethmax) THEN + klev = k + ethmax = eth_temp(k) + END IF + END DO + + kpar1 = klev + kpar2 = klev + + + ! Establish average properties of that parcel + ! (over depth of approximately davg meters) + + !davg = 500.D0 + pavg = 500.D0 * prs_new(kpar1,i,j)*& + G/(RD*tvirtual(tmk_new(kpar1,i,j), qvp_new(kpar1,i,j))) + p2 = MIN(prs_new(kpar1,i,j)+.5d0*pavg, prsf(mkzh,i,j)) + p1 = p2 - pavg + totthe = 0.D0 + totqvp = 0.D0 + totprs = 0.D0 + DO k = mkzh,2,-1 + IF (prsf(k,i,j) .LE. p1) EXIT !GOTO 35 + IF (prsf(k-1,i,j) .GE. p2) CYCLE !GOTO 34 + p = prs_new(k,i,j) + pup = prsf(k,i,j) + pdn = prsf(k-1,i,j) + !q = MAX(qvp_new(k,i,j),1.D-15) + th = tmk_new(k,i,j)*(1000.D0/prs_new(k,i,j))**(GAMMA*(1.D0 + GAMMAMD*MAX(qvp_new(k,i,j),1.D-15))) + pp1 = MAX(p1,pdn) + pp2 = MIN(p2,pup) + IF (pp2 .GT. pp1) THEN + ! deltap = pp2 - pp1 + totqvp = totqvp + MAX(qvp_new(k,i,j),1.D-15)*(pp2 - pp1) + totthe = totthe + th*(pp2 - pp1) + totprs = totprs + (pp2 - pp1) + END IF + END DO + qvppari = totqvp/totprs + tmkpari = (totthe/totprs)*& + (prs_new(kpar1,i,j)/1000.D0)**(GAMMA*(1.D0+GAMMAMD*qvp_new(kpar1,i,j))) + + DO kpar = kpar1, kpar2 + + ! Calculate temperature and moisture properties of parcel + ! (note, qvppari and tmkpari already calculated above for 2d + ! case.) + + !prspari = prs_new(kpar,i,j) + !ghtpari = ght_new(kpar,i,j) + gammam = GAMMA * (1.D0 + GAMMAMD*qvppari) + cpm = CP * (1.D0 + CPMD*qvppari) + + e = MAX(1.D-20,qvppari*prs_new(kpar,i,j)/(EPS + qvppari)) + tlcl = TLCLC1/(LOG(tmkpari**TLCLC2/e) - TLCLC3) + TLCLC4 + ethpari = tmkpari*(1000.D0/prs_new(kpar,i,j))**(GAMMA*(1.D0 + GAMMAMD*qvppari))*& + EXP((THTECON1/tlcl - THTECON2)*qvppari*(1.D0 + THTECON3*qvppari)) + zlcl = ght_new(kpar,i,j) + (tmkpari - tlcl)/(G/cpm) + + ! Calculate buoyancy and relative height of lifted parcel at + ! all levels, and store in bottom up arrays. add a level at the + ! lcl, + ! and at all points where buoyancy is zero. + ! + ! + ! For arrays that go bottom to top + kk = 0 + ilcl = 0 + + IF (ght_new(kpar,i,j) .GE. zlcl) THEN + ! Initial parcel already saturated or supersaturated. + ilcl = 2 + klcl = 1 + END IF + + k = kpar + DO k = kpar,1,-1 + ! For arrays that go bottom to top + kk = kk + 1 + + ! Model level is below lcl + IF (ght_new(k,i,j) .LT. zlcl) THEN + tmklift = tmk_new(kpar,i,j) - G/(CP * (1.D0 + CPMD*qvp_new(kpar,i,j)))*& + (ght_new(k,i,j) - ght_new(kpar,i,j)) + tvenv = tmk_new(k,i,j)*(EPS + qvp_new(k,i,j))/(EPS*(1.D0 + qvp_new(k,i,j))) + tvlift = tmklift*(EPS + qvp_new(kpar,i,j))/(EPS*(1.D0 + qvp_new(kpar,i,j))) + ghtlift = ght_new(k,i,j) + ELSE IF (ght(i,j,k) .GE. zlcl .AND. ilcl .EQ. 0) THEN + ! This model level and previous model level straddle the lcl, + ! so first create a new level in the bottom-up array, at the lcl. + facden = 1/(ght_new(k,i,j) - ght_new(k+1,i,j)) + tmkenv = tmk_new(k+1,i,j)*((ght_new(k,i,j)-zlcl)*facden) + tmk_new(k,i,j)*& + ((zlcl-ght_new(k+1,i,j))*facden) + qvpenv = qvp_new(k+1,i,j)*((ght_new(k,i,j)-zlcl)*facden) + qvp_new(k,i,j)*& + ((zlcl-ght_new(k+1,i,j))*facden) + tvenv = tmkenv* (EPS + qvpenv) / (EPS * (1.D0 + qvpenv)) + tvlift = tlcl* (EPS + qvp_new(kpar,i,j)) / (EPS *(1.D0 + qvp_new(kpar,i,j))) + ghtlift = zlcl + ilcl = 1 + ELSE + tmklift = TONPSADIABAT(ethpari, prs_new(k,i,j), psadithte, psadiprs,& + psaditmk, GAMMA, errstat, errmsg) + eslift = EZERO*EXP(ESLCON1*(tmklift - CELKEL)/(tmklift - ESLCON2)) + qvplift = EPS*eslift/(prs_new(k,i,j) - eslift) + tvenv = tmk_new(k,i,j) * (EPS + qvp_new(k,i,j)) / (EPS * (1.D0 + qvp_new(k,i,j))) + tvlift = tmklift*(EPS + qvplift) / (EPS * (1.D0 + qvplift)) + ghtlift = ght_new(k,i,j) + END IF + ! Buoyancy + buoy(kk) = G*(tvlift - tvenv)/tvenv + zrel(kk) = ghtlift - ght_new(kpar,i,j) + IF ((kk .GT. 1) .AND. (buoy(kk)*buoy(kk-1) .LT. 0.0D0)) THEN + ! Parcel ascent curve crosses sounding curve, so create a new level + ! in the bottom-up array at the crossing. + kk = kk + 1 + buoy(kk) = buoy(kk-1) + zrel(kk) = zrel(kk-1) + buoy(kk-1) = 0.D0 + zrel(kk-1) = zrel(kk-2) + buoy(kk-2)/& + (buoy(kk-2) - buoy(kk))*(zrel(kk) - zrel(kk-2)) + END IF + IF (ilcl .EQ. 1) THEN + klcl = kk + ilcl = 2 + CYCLE + END IF + END DO + + kmax = kk + ! IF (kmax .GT. 150) THEN + ! errstat = ALGERR + ! WRITE(errmsg, *) 'capecalc3d: kmax got too big. kmax=',kmax + ! RETURN + ! END IF + + ! If no lcl was found, set klcl to kmax. it is probably not + ! really + ! at kmax, but this will make the rest of the routine behave + ! properly. + IF (ilcl .EQ. 0) klcl=kmax + + ! Get the accumulated buoyant energy from the parcel's starting + ! point, at all levels up to the top level. + benaccum(1) = 0.0D0 + benamin = 9d9 + DO k = 2,kmax + dz = zrel(k) - zrel(k-1) + benaccum(k) = benaccum(k-1) + .5D0*dz*(buoy(k-1) + buoy(k)) + IF (benaccum(k) .LT. benamin) THEN + benamin = benaccum(k) + END IF + END DO + ! Determine equilibrium level (el), which we define as the highest + ! level of non-negative buoyancy above the lcl. note, this may be + ! the top level if the parcel is still buoyant there. + + elfound = .FALSE. + DO k = kmax,klcl,-1 + IF (buoy(k) .GE. 0.D0) THEN + ! k of equilibrium level + kel = k + elfound = .TRUE. + EXIT + END IF + END DO + + ! If we got through that loop, then there is no non-negative + ! buoyancy above the lcl in the sounding. in these situations, + ! both cape and cin will be set to -0.1 j/kg. (see below about + ! missing values in v6.1.0). also, where cape is + ! non-zero, cape and cin will be set to a minimum of +0.1 j/kg, so + ! that the zero contour in either the cin or cape fields will + ! circumscribe regions of non-zero cape. + ! In v6.1.0 of ncl, we added a _fillvalue attribute to the return + ! value of this function. at that time we decided to change -0.1 + ! to a more appropriate missing value, which is passed into this + ! routine as cmsg. + + IF (.NOT. elfound) THEN + cape(i,j,kpar) = cmsg + cin(i,j,kpar) = cmsg + klfc = kmax + CYCLE + END IF + + + ! If there is an equilibrium level, then cape is positive. + ! we'll + ! define the level of free convection (lfc) as the point below + ! the + ! el, but at or above the lcl, where accumulated buoyant energy + ! is a + ! minimum. the net positive area (accumulated buoyant energy) + ! from + ! the lfc up to the el will be defined as the cape, and the net + ! negative area (negative of accumulated buoyant energy) from + ! the + ! parcel starting point to the lfc will be defined as the + ! convective + ! inhibition (cin). + + ! First get the lfc according to the above definition. + benamin = 9D9 + klfc = kmax + DO k = klcl,kel + IF (benaccum(k) .LT. benamin) THEN + benamin = benaccum(k) + klfc = k + END IF + END DO + + ! Now we can assign values to cape and cin + + cape(i,j,kpar) = MAX(benaccum(kel)-benamin, 0.1D0) + cin(i,j,kpar) = MAX(-benamin, 0.1D0) + + ! cin is uninteresting when cape is small (< 100 j/kg), so set + ! cin to -0.1 (see note about missing values in v6.1.0) in + ! that case. + + ! In v6.1.0 of ncl, we added a _fillvalue attribute to the return + ! value of this function. at that time we decided to change -0.1 + ! to a more appropriate missing value, which is passed into this + ! routine as cmsg. + + IF (cape(i,j,kpar) .LT. 100.D0) cin(i,j,kpar) = cmsg + + END DO + + cape(i,j,mkzh) = cape(i,j,kpar1) + cin(i,j,mkzh) = cin(i,j,kpar1) + ! meters agl + cin(i,j,mkzh-1) = zrel(klcl) + ght_new(kpar,i,j) - ter(i,j) + ! meters agl + cin(i,j,mkzh-2) = zrel(klfc) + ght_new(kpar,i,j) - ter(i,j) + + END DO END DO -!$OMP END PARALLEL DO + !$OMP END PARALLEL DO + RETURN END SUBROUTINE DCAPECALC2D diff --git a/src/wrf/config.py b/src/wrf/config.py index c7f28b2..556f632 100644 --- a/src/wrf/config.py +++ b/src/wrf/config.py @@ -2,35 +2,64 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) from threading import local +import wrapt _local_config = local() -_local_config.xarray_enabled = True -_local_config.cartopy_enabled = True -_local_config.basemap_enabled = True -_local_config.pyngl_enabled = True -_local_config.cache_size = 20 - -try: - from xarray import DataArray -except ImportError: - _local_config.xarray_enabled = False - -try: - from cartopy import crs -except ImportError: - _local_config.cartopy_enabled = False + +def _init_local(): + global _local_config -try: - from mpl_toolkits.basemap import Basemap -except ImportError: - _local_config.basemap_enabled = False + _local_config.xarray_enabled = True + _local_config.cartopy_enabled = True + _local_config.basemap_enabled = True + _local_config.pyngl_enabled = True + _local_config.cache_size = 20 + _local_config.initialized = True -try: - from Ngl import Resources -except ImportError: - _local_config.pyngl_enabled = False + try: + from xarray import DataArray + except ImportError: + _local_config.xarray_enabled = False + + try: + from cartopy import crs + except ImportError: + _local_config.cartopy_enabled = False + + try: + from mpl_toolkits.basemap import Basemap + except ImportError: + _local_config.basemap_enabled = False + + try: + from Ngl import Resources + except ImportError: + _local_config.pyngl_enabled = False + +# Initialize the main thread's configuration +_init_local() + +def init_local(): + """A decorator that initializes thread local data if necessary.""" + @wrapt.decorator + def func_wrapper(wrapped, instance, args, kwargs): + global _local_config + try: + init = _local_config.init + except AttributeError: + _init_local() + else: + if not init: + _init_local() + + return wrapped(*args, **kwargs) + + return func_wrapper + + +@init_local() def xarray_enabled(): """Return True if xarray is installed and enabled. @@ -43,18 +72,21 @@ def xarray_enabled(): return _local_config.xarray_enabled +@init_local() def disable_xarray(): """Disable xarray.""" global _local_config _local_config.xarray_enabled = False - + +@init_local() def enable_xarray(): """Enable xarray.""" global _local_config _local_config.xarray_enabled = True - + +@init_local() def cartopy_enabled(): """Return True if cartopy is installed and enabled. @@ -67,18 +99,21 @@ def cartopy_enabled(): return _local_config.cartopy_enabled +@init_local() def enable_cartopy(): """Enable cartopy.""" global _local_config _local_config.cartopy_enabled = True - + +@init_local() def disable_cartopy(): """Disable cartopy.""" global _local_config _local_config.cartopy_enabled = True - + +@init_local() def basemap_enabled(): """Return True if basemap is installed and enabled. @@ -91,17 +126,21 @@ def basemap_enabled(): return _local_config.basemap_enabled +@init_local() def enable_basemap(): """Enable basemap.""" global _local_config _local_config.basemap_enabled = True - + +@init_local() def disable_basemap(): """Disable basemap.""" global _local_config _local_config.basemap_enabled = True - + + +@init_local() def pyngl_enabled(): """Return True if pyngl is installed and enabled. @@ -114,18 +153,21 @@ def pyngl_enabled(): return _local_config.pyngl_enabled +@init_local() def enable_pyngl(): """Enable pyngl.""" global _local_config _local_config.pyngl_enabled = True - + +@init_local() def disable_pyngl(): """Disable pyngl.""" global _local_config _local_config.pyngl_enabled = True - + +@init_local() def set_cache_size(size): """Set the maximum number of items that the threadlocal cache can retain. @@ -143,7 +185,8 @@ def set_cache_size(size): global _local_config _local_config.cache_size = size - + +@init_local() def get_cache_size(): """Return the maximum number of items that the threadlocal cache can retain. diff --git a/src/wrf/util.py b/src/wrf/util.py index 6c520e1..fff88ec 100644 --- a/src/wrf/util.py +++ b/src/wrf/util.py @@ -209,11 +209,30 @@ def _generator_copy(gen): module = getmodule(gen.gi_frame) if module is not None: - res = module.get(funcname)(**argvals.locals) + try: + try: + argd = {key:argvals.locals[key] for key in argvals.args} + res = module.get(funcname)(**argd) + except AttributeError: + res = getattr(module, funcname)(**argd) + except: + # This is the old way it used to work, but it looks like this was + # fixed by Python. + try: + res = module.get(funcname)(**argvals.locals) + except AttributeError: + res = getattr(module, funcname)(**argvals.locals) else: # Created in jupyter or the python interpreter import __main__ - res = getattr(__main__, funcname)(**argvals.locals) + + try: + argd = {key:argvals.locals[key] for key in argvals.args} + res = getattr(__main__, funcname)(**argd) + except: + # This was the old way it used to work, but appears to have + # been fixed by Python. + res = getattr(__main__, funcname)(**argvals.locals) return res @@ -2583,26 +2602,6 @@ def get_proj_params(wrfin):#, timeidx=0, varname=None): "DX", "DY")) return proj_params -# multitime = is_multi_time_req(timeidx) -# if not multitime: -# time_idx_or_slice = timeidx -# else: -# time_idx_or_slice = slice(None) -# -# if varname is not None: -# if not is_coordvar(varname): -# coord_names = getattr(wrfin.variables[varname], -# "coordinates").split() -# lon_coord = coord_names[0] -# lat_coord = coord_names[1] -# else: -# lat_coord, lon_coord = get_coord_pairs(varname) -# else: -# lat_coord, lon_coord = latlon_coordvars(wrfin.variables) -# -# return (wrfin.variables[lat_coord][time_idx_or_slice,:], -# wrfin.variables[lon_coord][time_idx_or_slice,:], -# proj_params) def from_args(func, argnames, *args, **kwargs): @@ -2936,18 +2935,31 @@ def psafilepath(): return os.path.join(os.path.dirname(__file__), "data", "psadilookup.dat") -def get_id(obj): - """Return the object id. +def get_filepath(obj): - The object id is used as a caching key for various routines. If the + try: + path = obj.filepath() + except AttributeError: + try: + path = obj.file.path + except: + raise ValueError("file contains no path information") + + return path + +def get_id(obj, prefix=''): + """Return the cache id. + + The cache id is used as a caching key for various routines. If the object type is a mapping, then the result will also be a - mapping of each key to the object id for the value. Otherwise, only the - object id is returned. + mapping of each key to the object id for the value. Args: obj (:obj:`object`): Any object type. + prefix (:obj:`str`): A string to help with recursive calls. + Returns: :obj:`int` or :obj:`dict`: If the *obj* parameter is not a mapping, @@ -2955,12 +2967,18 @@ def get_id(obj): key to the object id for the value is returned. """ + if not is_multi_file(obj): + return hash(prefix + get_filepath(obj)) + + # For sequences, the hashing string will be the list ID and the + # path for the first file in the sequence if not is_mapping(obj): - return id(obj) + _next = next(iter(obj)) + return get_id(_next, prefix + str(id(obj))) # For each key in the mapping, recursively call get_id until # until a non-mapping is found - return {key : get_id(val) for key,val in viewitems(obj)} + return {key : get_id(val, prefix) for key,val in viewitems(obj)} def geo_bounds(var=None, wrfin=None, varname=None, timeidx=0, method="cat", diff --git a/test/cachetest.py b/test/cachetest.py index cc7efee..fad5e88 100644 --- a/test/cachetest.py +++ b/test/cachetest.py @@ -2,7 +2,10 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) from threading import Thread -from Queue import Queue +try: + from Queue import Queue +except ImportError: + from queue import Queue from collections import OrderedDict import unittest as ut @@ -62,4 +65,4 @@ class CacheTest(ut.TestCase): if __name__ == "__main__": - ut.main() \ No newline at end of file + ut.main() diff --git a/test/generator_test.py b/test/generator_test.py new file mode 100644 index 0000000..6a45d89 --- /dev/null +++ b/test/generator_test.py @@ -0,0 +1,17 @@ +from __future__ import (absolute_import, division, print_function, unicode_literals) + +from wrf import getvar +from netCDF4 import Dataset as nc +#ncfile = nc("/Users/ladwig/Documents/wrf_files/wrfout_d01_2016-02-25_18_00_00") +ncfile = nc("/Users/ladwig/Documents/wrf_files/wrfout_d01_2016-10-07_00_00_00") + +def gen_seq(): + wrfseq = [ncfile, ncfile, ncfile] + for wrf in wrfseq: + yield wrf + +p_gen = getvar(gen_seq(), "P", method="join") + +print(p_gen) +del p_gen + From a7b3714e4f542d451d0fffe98f92de32d2c4599d Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Fri, 17 Nov 2017 16:42:54 -0700 Subject: [PATCH 19/43] Fixed type with the iterable test. --- test/ipynb/WRF_python_demo.ipynb | 376 ++++++------------------------- 1 file changed, 68 insertions(+), 308 deletions(-) diff --git a/test/ipynb/WRF_python_demo.ipynb b/test/ipynb/WRF_python_demo.ipynb index 8df9d49..977aaed 100644 --- a/test/ipynb/WRF_python_demo.ipynb +++ b/test/ipynb/WRF_python_demo.ipynb @@ -2,10 +2,7 @@ "cells": [ { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "# 1.0 Basic Variable Extraction" ] @@ -13,11 +10,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from __future__ import (absolute_import, division, print_function, unicode_literals)\n", @@ -32,9 +25,6 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false, - "deletable": true, - "editable": true, "scrolled": false }, "outputs": [], @@ -45,10 +35,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "### 1.0.1 DataArray attributes: 'dims', 'coords', 'attrs'" ] @@ -56,11 +43,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "print(\"dims: \", p.dims)\n", @@ -71,10 +54,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "### 1.0.2 Removing implicit 'squeeze' behavior to preserve single sized dimensions" ] @@ -82,11 +62,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "p_nosqueeze = getvar(ncfile, \"P\", timeidx=0, squeeze=False)\n", @@ -95,10 +71,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "### 1.0.3 Single element metadata" ] @@ -106,11 +79,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "print (p_nosqueeze[0,0,100,200])\n", @@ -119,10 +88,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "### 1.0.4 Disabling/Enabling xarray" ] @@ -130,11 +96,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from wrf import disable_xarray, enable_xarray\n", @@ -157,20 +119,14 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "# 2.0 Sequences of Input Files " ] }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "## 2.0.1 Combining via the 'cat' method" ] @@ -178,11 +134,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from wrf import ALL_TIMES\n", @@ -196,10 +148,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "## 2.0.2 Combining via the 'join' method" ] @@ -207,11 +156,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "p_join = getvar(wrflist, \"P\", timeidx=ALL_TIMES, method=\"join\")\n", @@ -220,10 +165,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "Note how the Time dimension was replaced with the file dimension, due to the 'squeezing' of the Time dimension.\n", "\n", @@ -234,11 +176,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from wrf import ALL_TIMES\n", @@ -249,10 +187,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "## 2.0.3 Dictionary Sequences" ] @@ -260,11 +195,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "wrf_dict = {\"label1\" : [ncfile, ncfile],\n", @@ -276,10 +207,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "## 2.0.4 Generator Sequences" ] @@ -287,11 +215,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "def gen_seq():\n", @@ -306,10 +230,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "## 2.0.5 Custom Iterable Classes" ] @@ -317,11 +238,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "class FileGen(object):\n", @@ -334,7 +251,7 @@ " return self\n", " \n", " def next(self):\n", - " if self._i >= self._total:\n", + " if self._i > self._total:\n", " raise StopIteration\n", " else:\n", " val = self.ncfile[self._i]\n", @@ -347,7 +264,7 @@ "\n", "obj_gen = FileGen(ncfile, 3)\n", "\n", - "p_obj_gen = getvar(gen_seq(), \"P\", method=\"join\", squeeze=False)\n", + "p_obj_gen = getvar(obj_gen, \"P\", method=\"join\", squeeze=False)\n", "print(p_obj_gen)\n", "\n", "del p_obj_gen\n", @@ -356,10 +273,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "# 3.0 WRF Variable Computational Routines" ] @@ -367,11 +281,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "wrf_vars = [\"avo\", \"eth\", \"cape_2d\", \"cape_3d\", \"ctt\", \"dbz\", \"mdbz\", \n", @@ -390,10 +300,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "(Note all of the NaNs in the above routines which produce missing values (e.g. cape_2d). xarray always converts all masked_array missing values to NaN in order to work with pandas. To get back the original missing values in a numpy masked_array, you need to use the 'to_np' method from wrf.)" ] @@ -401,11 +308,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from wrf import to_np\n", @@ -417,11 +320,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "keys = [x for x in vard.keys()]\n", @@ -431,20 +330,14 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "## 3.1 Interpolation Routines" ] }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "### 3.1.1 Horizontal Level Interpolation" ] @@ -452,11 +345,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "# 500 MB Heights\n", @@ -472,10 +361,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "### 3.1.2 Vertical Cross Section Interpolation" ] @@ -483,11 +369,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "# Pressure using pivot and angle\n", @@ -515,11 +397,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "# Pressure using pivot and angle\n", @@ -570,11 +448,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "# Pressure using pivot and angle\n", @@ -626,10 +500,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "### 3.1.3 Interpolate 2D Variable to a Line" ] @@ -637,11 +508,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "# T2 using pivot and angle\n", @@ -685,10 +552,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "### 3.1.4 Vertical Coordinate Interpolation" ] @@ -696,11 +560,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from wrf import vinterp, getvar\n", @@ -782,10 +642,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "## 3.2 Lat/Lon to X/Y Routines" ] @@ -793,11 +650,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from wrf.latlon import xy_to_ll, ll_to_xy \n", @@ -835,9 +688,7 @@ { "cell_type": "markdown", "metadata": { - "collapsed": true, - "deletable": true, - "editable": true + "collapsed": true }, "source": [ "# 4.0 Plotting with Cartopy" @@ -846,11 +697,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "%matplotlib inline" @@ -859,11 +706,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "# SLP\n", @@ -905,11 +748,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "# SLP\n", @@ -986,11 +825,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "# 500 MB Heights and Winds\n", @@ -1043,9 +878,6 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false, - "deletable": true, - "editable": true, "scrolled": false }, "outputs": [], @@ -1075,11 +907,7 @@ }, { "cell_type": "markdown", - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "# Multi-time Moving Domain Files" ] @@ -1087,11 +915,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "import os\n", @@ -1111,11 +935,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "p = getvar(ncfiles, \"ctt\", timeidx=ALL_TIMES)" @@ -1124,11 +944,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "print (p)\n", @@ -1138,11 +954,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "print (p.attrs[\"projection\"])\n" @@ -1151,11 +963,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "ncfiles[2].variables[\"XTIME\"][:]\n" @@ -1164,11 +972,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "p = getvar(ncfiles, \"P\", timeidx=None, method=\"cat\", meta=True, squeeze=True)\n" @@ -1177,11 +981,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "print (p)" @@ -1190,11 +990,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "print (type(p.coords[\"Time\"]))" @@ -1203,11 +999,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "import datetime\n", @@ -1220,11 +1012,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "wrf_vars = [\"avo\", \"eth\", \"cape_2d\", \"cape_3d\", \"ctt\", \"dbz\", \"mdbz\", \n", @@ -1249,11 +1037,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "import os\n", @@ -1290,11 +1074,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "import os\n", @@ -1335,11 +1115,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from wrf import getvar\n" @@ -1348,22 +1124,14 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from wrf.latlon import xy_to_ll, ll_to_xy \n", @@ -1390,11 +1158,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from glob import glob\n", @@ -1435,11 +1199,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": true, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [] } @@ -1460,9 +1220,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", - "version": "2.7.12" + "version": "2.7.13" } }, "nbformat": 4, - "nbformat_minor": 0 + "nbformat_minor": 1 } From 7b79ca3ae4576f202430b428fa85252103f558e4 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Tue, 28 Nov 2017 16:14:18 -0700 Subject: [PATCH 20/43] created wrappers for openmp runtime routines --- fortran/omp.F90 | 698 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 698 insertions(+) create mode 100644 fortran/omp.F90 diff --git a/fortran/omp.F90 b/fortran/omp.F90 new file mode 100644 index 0000000..2ac66e9 --- /dev/null +++ b/fortran/omp.F90 @@ -0,0 +1,698 @@ +MODULE omp_constants +#ifdef _OPENMP + USE omp_lib + INTEGER, PARAMETER :: fomp_sched_kind = omp_sched_kind + INTEGER, PARAMETER :: fomp_nest_lock_kind = omp_nest_lock_kind + INTEGER, PARAMETER :: fomp_lock_kind = omp_lock_kind + INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_static = omp_sched_static + INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_dynamic = omp_sched_dynamic + INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_guided = omp_sched_guided + INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_auto = omp_sched_auto +#else + INTEGER, PARAMETER :: fomp_sched_kind = 4 + INTEGER, PARAMETER :: fomp_lock_kind = 4 + INTEGER, PARAMETER :: fomp_nest_lock_kind = 8 + INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_static = 1 + INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_dynamic = 2 + INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_guided = 3 + INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_auto = 4 +#endif + +END MODULE omp_constants + + +SUBROUTINE fomp_set_num_threads(num_threads) +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER, INTENT(IN) :: num_threads + +#ifdef _OPENMP + CALL omp_set_num_threads(num_threads) +#endif + + RETURN + +END SUBROUTINE fomp_set_num_threads + + +FUNCTION fomp_get_num_threads() +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_num_threads + +#ifdef _OPENMP + fomp_get_num_threads = omp_get_num_threads() +#else + fomp_get_num_threads = -1 +#endif + +END FUNCTION fomp_get_num_threads + + +FUNCTION fomp_get_max_threads() +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_max_threads + +#ifdef _OPENMP + fomp_get_max_threads = omp_get_max_threads() +#else + fomp_get_max_threads = -1 +#endif + +END FUNCTION fomp_get_max_threads + + +FUNCTION fomp_get_thread_num() +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_thread_num + +#ifdef _OPENMP + fomp_get_thread_num = omp_get_thread_num() +#else + fomp_get_thread_num = -1 +#endif + +END FUNCTION fomp_get_thread_num + + +FUNCTION fomp_get_num_procs() +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_num_procs + +#ifdef _OPENMP + fomp_get_num_procs = omp_get_num_procs() +#else + fomp_get_num_procs = -1 +#endif + +END FUNCTION fomp_get_num_procs + + +FUNCTION fomp_in_parallel() +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + LOGICAL :: fomp_in_parallel + +#ifdef _OPENMP + fomp_in_parallel = omp_in_parallel() +#else + fomp_in_parallel = .FALSE. +#endif + +END FUNCTION fomp_in_parallel + + +SUBROUTINE fomp_set_dynamic(dynamic_threads) +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + LOGICAL, INTENT(IN) :: dynamic_threads + +#ifdef _OPENMP + CALL omp_set_dynamic(dynamic_threads) +#endif + + RETURN + +END SUBROUTINE fomp_set_dynamic + + +FUNCTION fomp_get_dynamic() +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + LOGICAL :: fomp_get_dynamic + +#ifdef _OPENMP + fomp_get_dynamic = omp_get_dynamic() +#else + fomp_get_dynamic = .FALSE. +#endif + +END FUNCTION fomp_get_dynamic + + +SUBROUTINE fomp_set_nested(nested) +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + LOGICAL, INTENT(IN) :: nested + +#ifdef _OPENMP + CALL omp_set_nested(nested) +#endif + + RETURN + +END SUBROUTINE fomp_set_nested + + +FUNCTION fomp_get_nested() +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + LOGICAL :: fomp_get_nested + +#ifdef _OPENMP + fomp_get_nested = omp_get_nested() +#else + fomp_get_nested = .FALSE. +#endif + +END FUNCTION fomp_get_nested + + +SUBROUTINE fomp_set_schedule(kind, modifier) +#ifdef _OPENMP + USE omp_lib +#endif + USE omp_constants, ONLY : fomp_sched_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_sched_kind), INTENT(IN) :: kind + INTEGER, INTENT(IN) :: modifier + +#ifdef _OPENMP + CALL omp_set_schedule(kind, modifier) +#endif + + RETURN + +END SUBROUTINE fomp_set_schedule + + +SUBROUTINE fomp_get_schedule(kind, modifier) +#ifdef _OPENMP + USE omp_lib +#endif + USE omp_constants, ONLY : fomp_sched_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_sched_kind), INTENT(OUT) :: kind + INTEGER, INTENT(OUT) :: modifier + +#ifdef _OPENMP + CALL omp_get_schedule(kind, modifier) +#else + kind = -1 + modifier = -1 +#endif + + RETURN + +END SUBROUTINE fomp_get_schedule + + +FUNCTION fomp_get_thread_limit() +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_thread_limit + +#ifdef _OPENMP + fomp_get_thread_limit = omp_get_thread_limit() +#else + fomp_get_thread_limit = -1 +#endif + +END FUNCTION fomp_get_thread_limit + + +SUBROUTINE fomp_set_max_active_levels(max_levels) +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + +#ifdef _OPENMP + INTEGER, INTENT(IN) :: max_levels +#else + INTEGER, INTENT(IN) :: max_levels + + max_levels = -1 +#endif + +#ifdef _OPENMP + CALL omp_set_max_active_levels(max_levels) +#endif + + RETURN + +END SUBROUTINE fomp_set_max_active_levels + + +FUNCTION fomp_get_max_active_levels() +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_max_active_levels + +#ifdef _OPENMP + fomp_get_max_active_levels = omp_get_max_active_levels() +#else + fomp_get_max_active_levels = -1 +#endif + +END FUNCTION fomp_get_max_active_levels + + +FUNCTION fomp_get_level() +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_level + +#ifdef _OPENMP + fomp_get_level = omp_get_level() +#else + fomp_get_level = -1 +#endif + +END FUNCTION fomp_get_level + + +FUNCTION fomp_get_ancestor_thread_num(level) +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER, INTENT(IN) :: level + INTEGER :: fomp_get_ancestor_thread_num + +#ifdef _OPENMP + fomp_get_ancestor_thread_num = omp_get_ancestor_thread_num(level) +#else + fomp_get_ancestor_thread_num = -1 +#endif + +END FUNCTION fomp_get_ancestor_thread_num + + +FUNCTION fomp_get_team_size(level) +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER, INTENT(IN) :: level + INTEGER :: fomp_get_team_size + +#ifdef _OPENMP + fomp_get_team_size = omp_get_team_size(level) +#else + fomp_get_team_size = -1 +#endif + +END FUNCTION fomp_get_team_size + + +FUNCTION fomp_get_active_level() +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_active_level + +#ifdef _OPENMP + fomp_get_active_level = omp_get_active_level() +#else + fomp_get_active_level = -1 +#endif + +END FUNCTION fomp_get_active_level + + +FUNCTION fomp_in_final() +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + LOGICAL :: fomp_in_final + +#ifdef _OPENMP + fomp_in_final = omp_in_final() +#else + fomp_in_final = .FALSE. +#endif + +END FUNCTION fomp_in_final + + +SUBROUTINE fomp_init_lock(svar) +#ifdef _OPENMP + USE omp_lib +#endif + USE omp_constants, ONLY : fomp_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_lock_kind), INTENT(OUT) :: svar + +#ifdef _OPENMP + CALL omp_init_lock(svar) +#endif + + RETURN + +END SUBROUTINE fomp_init_lock + + +SUBROUTINE fomp_init_nest_lock(nvar) +#ifdef _OPENMP + USE omp_lib +#endif + USE omp_constants, ONLY : fomp_nest_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_nest_lock_kind), INTENT(OUT) :: nvar + +#ifdef _OPENMP + CALL omp_init_nest_lock(nvar) +#endif + + RETURN + +END SUBROUTINE fomp_init_nest_lock + + +SUBROUTINE fomp_destroy_lock(svar) +#ifdef _OPENMP + USE omp_lib +#endif + USE omp_constants, ONLY : fomp_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_lock_kind), INTENT(INOUT) :: svar + +#ifdef _OPENMP + CALL omp_destroy_lock(svar) +#endif + + RETURN + +END SUBROUTINE fomp_destroy_lock + + +SUBROUTINE fomp_destroy_nest_lock(nvar) +#ifdef _OPENMP + USE omp_lib +#endif + USE omp_constants, ONLY : fomp_nest_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_nest_lock_kind), INTENT(INOUT) :: nvar + +#ifdef _OPENMP + CALL omp_destroy_nest_lock(nvar) +#endif + + RETURN + +END SUBROUTINE fomp_destroy_nest_lock + + +SUBROUTINE fomp_set_lock(svar) +#ifdef _OPENMP + USE omp_lib +#endif + USE omp_constants, ONLY : fomp_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_lock_kind), INTENT(INOUT) :: svar + +#ifdef _OPENMP + CALL omp_set_lock(svar) +#endif + + RETURN + +END SUBROUTINE fomp_set_lock + + +SUBROUTINE fomp_set_nest_lock(nvar) +#ifdef _OPENMP + USE omp_lib +#endif + USE omp_constants, ONLY : fomp_nest_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_nest_lock_kind), INTENT(INOUT) :: nvar + +#ifdef _OPENMP + CALL omp_set_nest_lock(nvar) +#endif + + RETURN + +END SUBROUTINE fomp_set_nest_lock + + +SUBROUTINE fomp_unset_lock(svar) +#ifdef _OPENMP + USE omp_lib +#endif + USE omp_constants, ONLY : fomp_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_lock_kind), INTENT(INOUT) :: svar + +#ifdef _OPENMP + CALL omp_unset_lock(svar) +#endif + + RETURN + +END SUBROUTINE fomp_unset_lock + + +SUBROUTINE fomp_unset_nest_lock(nvar) +#ifdef _OPENMP + USE omp_lib +#endif + USE omp_constants, ONLY : fomp_nest_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_nest_lock_kind), INTENT(INOUT) :: nvar + +#ifdef _OPENMP + CALL omp_unset_nest_lock(nvar) +#endif + + RETURN + +END SUBROUTINE fomp_unset_nest_lock + + +FUNCTION fomp_test_lock(svar) +#ifdef _OPENMP + USE omp_lib +#endif + USE omp_constants, ONLY : fomp_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_lock_kind), INTENT(INOUT) :: svar + LOGICAL :: fomp_test_lock + +#ifdef _OPENMP + fomp_test_lock = omp_test_lock(svar) +#else + fomp_test_lock = .FALSE. +#endif + + RETURN + +END FUNCTION fomp_in_final + + +FUNCTION fomp_test_nest_lock(nvar) +#ifdef _OPENMP + USE omp_lib +#endif + USE omp_constants, ONLY : fomp_nest_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_nest_lock_kind), INTENT(INOUT) :: nvar + INTEGER :: fomp_test_nest_lock + +#ifdef _OPENMP + fomp_test_lock = omp_test_nest_lock(nvar) +#else + fomp_test_lock = -1 +#endif + + RETURN + +END FUNCTION fomp_test_nest_lock + + +FUNCTION fomp_get_wtime() +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + REAL (KIND=8) :: fomp_get_wtime + +#ifdef _OPENMP + fomp_get_wtime = omp_get_wtime() +#else + fomp_get_wtime = -1 +#endif + + RETURN + +END FUNCTION fomp_get_wtime + + +FUNCTION fomp_get_wtick() +#ifdef _OPENMP + USE omp_lib +#endif + + IMPLICIT NONE + + !f2py threadsafe + + REAL (KIND=8) :: fomp_get_wtick + +#ifdef _OPENMP + fomp_get_wtick = omp_get_wtick() +#else + fomp_get_wtick = -1 +#endif + + RETURN + +END FUNCTION fomp_get_wtick + + From 03468fc050a42fc84506b1edf16fe82d3332b6b8 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Tue, 28 Nov 2017 16:18:16 -0700 Subject: [PATCH 21/43] renamed omp.F90 to omp.f90.in --- fortran/{omp.F90 => omp.f90.in} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename fortran/{omp.F90 => omp.f90.in} (100%) diff --git a/fortran/omp.F90 b/fortran/omp.f90.in similarity index 100% rename from fortran/omp.F90 rename to fortran/omp.f90.in From a3c3ddad27b718ada2047f95b349de4621435c02 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Tue, 28 Nov 2017 16:22:48 -0700 Subject: [PATCH 22/43] reverted name --- fortran/{omp.f90.in => omp.F90} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename fortran/{omp.f90.in => omp.F90} (100%) diff --git a/fortran/omp.f90.in b/fortran/omp.F90 similarity index 100% rename from fortran/omp.f90.in rename to fortran/omp.F90 From 83573db03f1c8f9e2b62630ccc29fb16038ce6c8 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Tue, 28 Nov 2017 16:28:39 -0700 Subject: [PATCH 23/43] Renamed omp.F90 to be ompgen.F90. --- fortran/{omp.F90 => ompgen.F90} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename fortran/{omp.F90 => ompgen.F90} (100%) diff --git a/fortran/omp.F90 b/fortran/ompgen.F90 similarity index 100% rename from fortran/omp.F90 rename to fortran/ompgen.F90 From 1c8ca7e287b301ab1b497e94ca0b16ef751eba7d Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Fri, 1 Dec 2017 16:13:19 -0700 Subject: [PATCH 24/43] Added OpenMP runtime library wrappers and build scripts --- build_scripts/gnu_no_omp.sh | 8 + build_scripts/gnu_omp.sh | 9 + fortran/omp.f90 | 667 ++++++++++++++++++++++++++++++++++++ fortran/ompgen.F90 | 94 ++--- fortran/rip_cape.f90 | 1 - fortran/wrffortran.pyf | 155 +++++++++ setup.py | 1 + src/wrf/api.py | 36 +- src/wrf/constants.py | 9 +- src/wrf/extension.py | 149 +++++++- 10 files changed, 1084 insertions(+), 45 deletions(-) create mode 100755 build_scripts/gnu_no_omp.sh create mode 100755 build_scripts/gnu_omp.sh create mode 100644 fortran/omp.f90 diff --git a/build_scripts/gnu_no_omp.sh b/build_scripts/gnu_no_omp.sh new file mode 100755 index 0000000..6f73784 --- /dev/null +++ b/build_scripts/gnu_no_omp.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +cd ../fortran +gfortran -E ompgen.F90 -cpp -o omp.f90 +f2py *.f90 -m _wrffortran -h wrffortran.pyf --overwrite-signature +cd .. +python setup.py clean --all +python setup.py config_fc --f90flags="-mtune=generic" build_ext build diff --git a/build_scripts/gnu_omp.sh b/build_scripts/gnu_omp.sh new file mode 100755 index 0000000..cf66f27 --- /dev/null +++ b/build_scripts/gnu_omp.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +cd ../fortran +gfortran -E ompgen.F90 -fopenmp -cpp -o omp.f90 +f2py *.f90 -m _wrffortran -h wrffortran.pyf --overwrite-signature --no-wrap-functions +cd .. +python setup.py clean --all +python setup.py config_fc --f90flags="-mtune=generic -fopenmp" build_ext --libraries="gomp" build + diff --git a/fortran/omp.f90 b/fortran/omp.f90 new file mode 100644 index 0000000..3da26d4 --- /dev/null +++ b/fortran/omp.f90 @@ -0,0 +1,667 @@ +# 1 "ompgen.F90" +# 1 "" +# 1 "" +# 1 "ompgen.F90" +MODULE omp_constants +# 52 "ompgen.F90" + INTEGER, PARAMETER :: fomp_sched_kind = 4 + INTEGER, PARAMETER :: fomp_lock_kind = 4 + INTEGER, PARAMETER :: fomp_nest_lock_kind = 8 + INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_static = 1 + INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_dynamic = 2 + INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_guided = 3 + INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_auto = 4 + + +END MODULE omp_constants + + +SUBROUTINE fomp_set_num_threads(num_threads) + + + + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER, INTENT(IN) :: num_threads + + + + + +END SUBROUTINE fomp_set_num_threads + + +FUNCTION fomp_get_num_threads() + + + + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_num_threads + + + + + fomp_get_num_threads = -1 + + +END FUNCTION fomp_get_num_threads + + +FUNCTION fomp_get_max_threads() + + + + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_max_threads + + + + + fomp_get_max_threads = -1 + + +END FUNCTION fomp_get_max_threads + + +FUNCTION fomp_get_thread_num() + + + + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_thread_num + + + + + fomp_get_thread_num = -1 + + +END FUNCTION fomp_get_thread_num + + +FUNCTION fomp_get_num_procs() + + + + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_num_procs + + + + + fomp_get_num_procs = -1 + + +END FUNCTION fomp_get_num_procs + + +FUNCTION fomp_in_parallel() + + + + + IMPLICIT NONE + + !f2py threadsafe + + LOGICAL :: fomp_in_parallel + + + + + fomp_in_parallel = .FALSE. + + +END FUNCTION fomp_in_parallel + + +SUBROUTINE fomp_set_dynamic(dynamic_threads) + + + + + IMPLICIT NONE + + !f2py threadsafe + + LOGICAL, INTENT(IN) :: dynamic_threads + + + + + +END SUBROUTINE fomp_set_dynamic + + +FUNCTION fomp_get_dynamic() + + + + + IMPLICIT NONE + + !f2py threadsafe + + LOGICAL :: fomp_get_dynamic + + + + + fomp_get_dynamic = .FALSE. + + +END FUNCTION fomp_get_dynamic + + +SUBROUTINE fomp_set_nested(nested) + + + + + IMPLICIT NONE + + !f2py threadsafe + + LOGICAL, INTENT(IN) :: nested + + + + + +END SUBROUTINE fomp_set_nested + + +FUNCTION fomp_get_nested() + + + + + IMPLICIT NONE + + !f2py threadsafe + + LOGICAL :: fomp_get_nested + + + + + fomp_get_nested = .FALSE. + + +END FUNCTION fomp_get_nested + + +SUBROUTINE fomp_set_schedule(kind, modifier) + + + + USE omp_constants, ONLY : fomp_sched_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_sched_kind), INTENT(IN) :: kind + INTEGER, INTENT(IN) :: modifier + + + + + +END SUBROUTINE fomp_set_schedule + + +SUBROUTINE fomp_get_schedule(kind, modifier) + + + + USE omp_constants, ONLY : fomp_sched_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_sched_kind), INTENT(OUT) :: kind + INTEGER, INTENT(OUT) :: modifier + + + + + kind = -1 + modifier = -1 + + +END SUBROUTINE fomp_get_schedule + + +FUNCTION fomp_get_thread_limit() + + + + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_thread_limit + + + + + fomp_get_thread_limit = -1 + + +END FUNCTION fomp_get_thread_limit + + +SUBROUTINE fomp_set_max_active_levels(max_levels) + + + + + IMPLICIT NONE + + !f2py threadsafe + + + + + INTEGER, INTENT(IN) :: max_levels + + + + + + +END SUBROUTINE fomp_set_max_active_levels + + +FUNCTION fomp_get_max_active_levels() + + + + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_max_active_levels + + + + + fomp_get_max_active_levels = -1 + + +END FUNCTION fomp_get_max_active_levels + + +FUNCTION fomp_get_level() + + + + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_level + + + + + fomp_get_level = -1 + + +END FUNCTION fomp_get_level + + +FUNCTION fomp_get_ancestor_thread_num(level) + + + + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER, INTENT(IN) :: level + INTEGER :: fomp_get_ancestor_thread_num + + + + + fomp_get_ancestor_thread_num = -1 + + +END FUNCTION fomp_get_ancestor_thread_num + + +FUNCTION fomp_get_team_size(level) + + + + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER, INTENT(IN) :: level + INTEGER :: fomp_get_team_size + + + + + fomp_get_team_size = -1 + + +END FUNCTION fomp_get_team_size + + +FUNCTION fomp_get_active_level() + + + + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER :: fomp_get_active_level + + + + + fomp_get_active_level = -1 + + +END FUNCTION fomp_get_active_level + + +FUNCTION fomp_in_final() + + + + + IMPLICIT NONE + + !f2py threadsafe + + LOGICAL :: fomp_in_final + + + + + fomp_in_final = .FALSE. + + +END FUNCTION fomp_in_final + + +SUBROUTINE fomp_init_lock(svar) + + + + USE omp_constants, ONLY : fomp_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_lock_kind), INTENT(OUT) :: svar + + + + + svar = -1 + + +END SUBROUTINE fomp_init_lock + + +SUBROUTINE fomp_init_nest_lock(nvar) + + + + USE omp_constants, ONLY : fomp_nest_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_nest_lock_kind), INTENT(OUT) :: nvar + + + + + nvar = -1 + + +END SUBROUTINE fomp_init_nest_lock + + +SUBROUTINE fomp_destroy_lock(svar) + + + + USE omp_constants, ONLY : fomp_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_lock_kind), INTENT(INOUT) :: svar + + + + + +END SUBROUTINE fomp_destroy_lock + + +SUBROUTINE fomp_destroy_nest_lock(nvar) + + + + USE omp_constants, ONLY : fomp_nest_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_nest_lock_kind), INTENT(INOUT) :: nvar + + + + + +END SUBROUTINE fomp_destroy_nest_lock + + +SUBROUTINE fomp_set_lock(svar) + + + + USE omp_constants, ONLY : fomp_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_lock_kind), INTENT(INOUT) :: svar + + + + + +END SUBROUTINE fomp_set_lock + + +SUBROUTINE fomp_set_nest_lock(nvar) + + + + USE omp_constants, ONLY : fomp_nest_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_nest_lock_kind), INTENT(INOUT) :: nvar + + + + + +END SUBROUTINE fomp_set_nest_lock + + +SUBROUTINE fomp_unset_lock(svar) + + + + USE omp_constants, ONLY : fomp_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_lock_kind), INTENT(INOUT) :: svar + + + + + +END SUBROUTINE fomp_unset_lock + + +SUBROUTINE fomp_unset_nest_lock(nvar) + + + + USE omp_constants, ONLY : fomp_nest_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_nest_lock_kind), INTENT(INOUT) :: nvar + + + + + +END SUBROUTINE fomp_unset_nest_lock + + +FUNCTION fomp_test_lock(svar) + + + + USE omp_constants, ONLY : fomp_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_lock_kind), INTENT(INOUT) :: svar + LOGICAL :: fomp_test_lock + + + + + fomp_test_lock = .FALSE. + + + + +END FUNCTION fomp_test_lock + + +FUNCTION fomp_test_nest_lock(nvar) + + + + USE omp_constants, ONLY : fomp_nest_lock_kind + + IMPLICIT NONE + + !f2py threadsafe + + INTEGER(KIND=fomp_nest_lock_kind), INTENT(INOUT) :: nvar + INTEGER :: fomp_test_nest_lock + + + + + fomp_test_nest_lock = -1 + + + + +END FUNCTION fomp_test_nest_lock + + +FUNCTION fomp_get_wtime() + + + + + IMPLICIT NONE + + !f2py threadsafe + + REAL (KIND=8) :: fomp_get_wtime + + + + + fomp_get_wtime = -1 + + + + +END FUNCTION fomp_get_wtime + + +FUNCTION fomp_get_wtick() + + + + + IMPLICIT NONE + + !f2py threadsafe + + REAL (KIND=8) :: fomp_get_wtick + + + + + fomp_get_wtick = -1 + + + + +END FUNCTION fomp_get_wtick + + diff --git a/fortran/ompgen.F90 b/fortran/ompgen.F90 index 2ac66e9..ed3d9bd 100644 --- a/fortran/ompgen.F90 +++ b/fortran/ompgen.F90 @@ -1,9 +1,49 @@ MODULE omp_constants #ifdef _OPENMP USE omp_lib - INTEGER, PARAMETER :: fomp_sched_kind = omp_sched_kind - INTEGER, PARAMETER :: fomp_nest_lock_kind = omp_nest_lock_kind - INTEGER, PARAMETER :: fomp_lock_kind = omp_lock_kind + ! f2py can't figure this out without me making a map of these kinds to + ! c types, so we're going to have to hard code the kinds below. + !INTEGER, PARAMETER :: fomp_sched_kind = omp_sched_kind + !INTEGER, PARAMETER :: fomp_nest_lock_kind = omp_nest_lock_kind + !INTEGER, PARAMETER :: fomp_lock_kind = omp_lock_kind + + ! Note: Defining these specifically is the only way I can get f2py to + ! work without doing the same thing in a mapping file. The values below + ! are for GNU on 64bit. This may not be entirely correct for non-GNU + ! compilers. In particular, it will be the locks that segfault/crash if + ! this is wrong. +#if defined(__GFORTRAN__) +#if (__SIZEOF_POINTER__ == 8) + INTEGER, PARAMETER :: fomp_sched_kind = 4 + INTEGER, PARAMETER :: fomp_lock_kind = 8 + INTEGER, PARAMETER :: fomp_nest_lock_kind = 8 +#else + INTEGER, PARAMETER :: fomp_sched_kind = 4 + INTEGER, PARAMETER :: fomp_lock_kind = 4 + INTEGER, PARAMETER :: fomp_nest_lock_kind = 4 +#endif +#elif defined(__INTEL_COMPILER_BUILD_DATE) +#if defined(__x86_64__) || defined(_M_X64) + INTEGER, PARAMETER :: fomp_sched_kind = 4 + INTEGER, PARAMETER :: fomp_lock_kind = 8 + INTEGER, PARAMETER :: fomp_nest_lock_kind = 8 +#else + INTEGER, PARAMETER :: fomp_sched_kind = 4 + INTEGER, PARAMETER :: fomp_lock_kind = 4 + INTEGER, PARAMETER :: fomp_nest_lock_kind = 4 +#endif +#elif defined(__PGI) +#if defined(__x86_64__) + INTEGER, PARAMETER :: fomp_sched_kind = 4 + INTEGER, PARAMETER :: fomp_lock_kind = 8 + INTEGER, PARAMETER :: fomp_nest_lock_kind = 8 +#else + INTEGER, PARAMETER :: fomp_sched_kind = 4 + INTEGER, PARAMETER :: fomp_lock_kind = 4 + INTEGER, PARAMETER :: fomp_nest_lock_kind = 4 +#endif +#endif + INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_static = omp_sched_static INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_dynamic = omp_sched_dynamic INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_guided = omp_sched_guided @@ -36,8 +76,6 @@ SUBROUTINE fomp_set_num_threads(num_threads) CALL omp_set_num_threads(num_threads) #endif - RETURN - END SUBROUTINE fomp_set_num_threads @@ -156,8 +194,6 @@ SUBROUTINE fomp_set_dynamic(dynamic_threads) CALL omp_set_dynamic(dynamic_threads) #endif - RETURN - END SUBROUTINE fomp_set_dynamic @@ -196,8 +232,6 @@ SUBROUTINE fomp_set_nested(nested) CALL omp_set_nested(nested) #endif - RETURN - END SUBROUTINE fomp_set_nested @@ -238,8 +272,6 @@ SUBROUTINE fomp_set_schedule(kind, modifier) CALL omp_set_schedule(kind, modifier) #endif - RETURN - END SUBROUTINE fomp_set_schedule @@ -263,8 +295,6 @@ SUBROUTINE fomp_get_schedule(kind, modifier) modifier = -1 #endif - RETURN - END SUBROUTINE fomp_get_schedule @@ -301,16 +331,12 @@ SUBROUTINE fomp_set_max_active_levels(max_levels) INTEGER, INTENT(IN) :: max_levels #else INTEGER, INTENT(IN) :: max_levels - - max_levels = -1 #endif #ifdef _OPENMP CALL omp_set_max_active_levels(max_levels) #endif - RETURN - END SUBROUTINE fomp_set_max_active_levels @@ -450,10 +476,10 @@ SUBROUTINE fomp_init_lock(svar) #ifdef _OPENMP CALL omp_init_lock(svar) +#else + svar = -1 #endif - RETURN - END SUBROUTINE fomp_init_lock @@ -471,10 +497,10 @@ SUBROUTINE fomp_init_nest_lock(nvar) #ifdef _OPENMP CALL omp_init_nest_lock(nvar) +#else + nvar = -1 #endif - RETURN - END SUBROUTINE fomp_init_nest_lock @@ -494,8 +520,6 @@ SUBROUTINE fomp_destroy_lock(svar) CALL omp_destroy_lock(svar) #endif - RETURN - END SUBROUTINE fomp_destroy_lock @@ -515,8 +539,6 @@ SUBROUTINE fomp_destroy_nest_lock(nvar) CALL omp_destroy_nest_lock(nvar) #endif - RETURN - END SUBROUTINE fomp_destroy_nest_lock @@ -536,8 +558,6 @@ SUBROUTINE fomp_set_lock(svar) CALL omp_set_lock(svar) #endif - RETURN - END SUBROUTINE fomp_set_lock @@ -557,8 +577,6 @@ SUBROUTINE fomp_set_nest_lock(nvar) CALL omp_set_nest_lock(nvar) #endif - RETURN - END SUBROUTINE fomp_set_nest_lock @@ -578,8 +596,6 @@ SUBROUTINE fomp_unset_lock(svar) CALL omp_unset_lock(svar) #endif - RETURN - END SUBROUTINE fomp_unset_lock @@ -599,8 +615,6 @@ SUBROUTINE fomp_unset_nest_lock(nvar) CALL omp_unset_nest_lock(nvar) #endif - RETURN - END SUBROUTINE fomp_unset_nest_lock @@ -623,9 +637,9 @@ FUNCTION fomp_test_lock(svar) fomp_test_lock = .FALSE. #endif - RETURN -END FUNCTION fomp_in_final + +END FUNCTION fomp_test_lock FUNCTION fomp_test_nest_lock(nvar) @@ -642,12 +656,12 @@ FUNCTION fomp_test_nest_lock(nvar) INTEGER :: fomp_test_nest_lock #ifdef _OPENMP - fomp_test_lock = omp_test_nest_lock(nvar) + fomp_test_nest_lock = omp_test_nest_lock(nvar) #else - fomp_test_lock = -1 + fomp_test_nest_lock = -1 #endif - RETURN + END FUNCTION fomp_test_nest_lock @@ -669,7 +683,7 @@ FUNCTION fomp_get_wtime() fomp_get_wtime = -1 #endif - RETURN + END FUNCTION fomp_get_wtime @@ -691,7 +705,7 @@ FUNCTION fomp_get_wtick() fomp_get_wtick = -1 #endif - RETURN + END FUNCTION fomp_get_wtick diff --git a/fortran/rip_cape.f90 b/fortran/rip_cape.f90 index d6a5fec..a56583e 100644 --- a/fortran/rip_cape.f90 +++ b/fortran/rip_cape.f90 @@ -40,7 +40,6 @@ REAL(KIND=8) FUNCTION TONPSADIABAT(thte, prs, psadithte, psadiprs, psaditmk, gam !!$OMP DECLARE SIMD (TONPSADIABAT) !!uniform(thte,prs,psadithte,psadiprs,psaditmk) !f2py threadsafe - !f2py intent(in,out) :: cape, cin IMPLICIT NONE REAL(KIND=8), INTENT(IN) :: thte diff --git a/fortran/wrffortran.pyf b/fortran/wrffortran.pyf index 41eecbb..8359fb7 100644 --- a/fortran/wrffortran.pyf +++ b/fortran/wrffortran.pyf @@ -33,6 +33,161 @@ python module _wrffortran ! in integer, optional,intent(in),check(shape(qvp,1)==mjx),depend(qvp) :: mjx=shape(qvp,1) integer, optional,intent(in),check(shape(qvp,2)==mkzh),depend(qvp) :: mkzh=shape(qvp,2) end subroutine deqthecalc + module omp_constants ! in :_wrffortran:omp.f90 + integer, parameter,optional :: fomp_nest_lock_kind=8 + integer(kind=4), parameter,optional :: fomp_sched_dynamic=2 + integer(kind=4), parameter,optional :: fomp_sched_guided=3 + integer, parameter,optional :: fomp_sched_kind=4 + integer(kind=4), parameter,optional :: fomp_sched_static=1 + integer, parameter,optional :: fomp_lock_kind=4 + integer(kind=4), parameter,optional :: fomp_sched_auto=4 + end module omp_constants + subroutine fomp_set_num_threads(num_threads) ! in :_wrffortran:omp.f90 + threadsafe + integer intent(in) :: num_threads + end subroutine fomp_set_num_threads + function fomp_get_num_threads() ! in :_wrffortran:omp.f90 + threadsafe + integer :: fomp_get_num_threads + end function fomp_get_num_threads + function fomp_get_max_threads() ! in :_wrffortran:omp.f90 + threadsafe + integer :: fomp_get_max_threads + end function fomp_get_max_threads + function fomp_get_thread_num() ! in :_wrffortran:omp.f90 + threadsafe + integer :: fomp_get_thread_num + end function fomp_get_thread_num + function fomp_get_num_procs() ! in :_wrffortran:omp.f90 + threadsafe + integer :: fomp_get_num_procs + end function fomp_get_num_procs + function fomp_in_parallel() ! in :_wrffortran:omp.f90 + threadsafe + logical :: fomp_in_parallel + end function fomp_in_parallel + subroutine fomp_set_dynamic(dynamic_threads) ! in :_wrffortran:omp.f90 + threadsafe + logical intent(in) :: dynamic_threads + end subroutine fomp_set_dynamic + function fomp_get_dynamic() ! in :_wrffortran:omp.f90 + threadsafe + logical :: fomp_get_dynamic + end function fomp_get_dynamic + subroutine fomp_set_nested(nested) ! in :_wrffortran:omp.f90 + threadsafe + logical intent(in) :: nested + end subroutine fomp_set_nested + function fomp_get_nested() ! in :_wrffortran:omp.f90 + threadsafe + logical :: fomp_get_nested + end function fomp_get_nested + subroutine fomp_set_schedule(kind,modifier) ! in :_wrffortran:omp.f90 + threadsafe + use omp_constants, only: fomp_sched_kind + integer(kind=4) intent(in) :: kind + integer intent(in) :: modifier + end subroutine fomp_set_schedule + subroutine fomp_get_schedule(kind,modifier) ! in :_wrffortran:omp.f90 + threadsafe + use omp_constants, only: fomp_sched_kind + integer(kind=4) intent(out) :: kind + integer intent(out) :: modifier + end subroutine fomp_get_schedule + function fomp_get_thread_limit() ! in :_wrffortran:omp.f90 + threadsafe + integer :: fomp_get_thread_limit + end function fomp_get_thread_limit + subroutine fomp_set_max_active_levels(max_levels) ! in :_wrffortran:omp.f90 + threadsafe + integer intent(in) :: max_levels + end subroutine fomp_set_max_active_levels + function fomp_get_max_active_levels() ! in :_wrffortran:omp.f90 + threadsafe + integer :: fomp_get_max_active_levels + end function fomp_get_max_active_levels + function fomp_get_level() ! in :_wrffortran:omp.f90 + threadsafe + integer :: fomp_get_level + end function fomp_get_level + function fomp_get_ancestor_thread_num(level) ! in :_wrffortran:omp.f90 + threadsafe + integer intent(in) :: level + integer :: fomp_get_ancestor_thread_num + end function fomp_get_ancestor_thread_num + function fomp_get_team_size(level) ! in :_wrffortran:omp.f90 + threadsafe + integer intent(in) :: level + integer :: fomp_get_team_size + end function fomp_get_team_size + function fomp_get_active_level() ! in :_wrffortran:omp.f90 + threadsafe + integer :: fomp_get_active_level + end function fomp_get_active_level + function fomp_in_final() ! in :_wrffortran:omp.f90 + threadsafe + logical :: fomp_in_final + end function fomp_in_final + subroutine fomp_init_lock(svar) ! in :_wrffortran:omp.f90 + threadsafe + use omp_constants, only: fomp_lock_kind + integer(kind=4) intent(out) :: svar + end subroutine fomp_init_lock + subroutine fomp_init_nest_lock(nvar) ! in :_wrffortran:omp.f90 + threadsafe + use omp_constants, only: fomp_nest_lock_kind + integer(kind=8) intent(out) :: nvar + end subroutine fomp_init_nest_lock + subroutine fomp_destroy_lock(svar) ! in :_wrffortran:omp.f90 + threadsafe + use omp_constants, only: fomp_lock_kind + integer(kind=4) intent(inout) :: svar + end subroutine fomp_destroy_lock + subroutine fomp_destroy_nest_lock(nvar) ! in :_wrffortran:omp.f90 + threadsafe + use omp_constants, only: fomp_nest_lock_kind + integer(kind=8) intent(inout) :: nvar + end subroutine fomp_destroy_nest_lock + subroutine fomp_set_lock(svar) ! in :_wrffortran:omp.f90 + threadsafe + use omp_constants, only: fomp_lock_kind + integer(kind=4) intent(inout) :: svar + end subroutine fomp_set_lock + subroutine fomp_set_nest_lock(nvar) ! in :_wrffortran:omp.f90 + threadsafe + use omp_constants, only: fomp_nest_lock_kind + integer(kind=8) intent(inout) :: nvar + end subroutine fomp_set_nest_lock + subroutine fomp_unset_lock(svar) ! in :_wrffortran:omp.f90 + threadsafe + use omp_constants, only: fomp_lock_kind + integer(kind=4) intent(inout) :: svar + end subroutine fomp_unset_lock + subroutine fomp_unset_nest_lock(nvar) ! in :_wrffortran:omp.f90 + threadsafe + use omp_constants, only: fomp_nest_lock_kind + integer(kind=8) intent(inout) :: nvar + end subroutine fomp_unset_nest_lock + function fomp_test_lock(svar) ! in :_wrffortran:omp.f90 + threadsafe + use omp_constants, only: fomp_lock_kind + integer(kind=4) intent(inout) :: svar + logical :: fomp_test_lock + end function fomp_test_lock + function fomp_test_nest_lock(nvar) ! in :_wrffortran:omp.f90 + threadsafe + use omp_constants, only: fomp_nest_lock_kind + integer(kind=8) intent(inout) :: nvar + integer :: fomp_test_nest_lock + end function fomp_test_nest_lock + function fomp_get_wtime() ! in :_wrffortran:omp.f90 + threadsafe + real(kind=8) :: fomp_get_wtime + end function fomp_get_wtime + function fomp_get_wtick() ! in :_wrffortran:omp.f90 + threadsafe + real(kind=8) :: fomp_get_wtick + end function fomp_get_wtick function tvirtual(temp,ratmix) ! in :_wrffortran:rip_cape.f90 threadsafe use wrf_constants, only: eps diff --git a/setup.py b/setup.py index 8f42dad..41ec9e6 100755 --- a/setup.py +++ b/setup.py @@ -22,6 +22,7 @@ ext1 = numpy.distutils.core.Extension( "fortran/wrf_pw.f90", "fortran/wrf_vinterp.f90", "fortran/wrf_wind.f90", + "fortran/omp.f90", "fortran/wrffortran.pyf"] ) diff --git a/src/wrf/api.py b/src/wrf/api.py index c5dec8c..877096d 100644 --- a/src/wrf/api.py +++ b/src/wrf/api.py @@ -10,7 +10,23 @@ from .computation import (xy, interp1d, interp2dxy, interpz3d, slp, tk, td, rh, uvmet, smooth2d, cape_2d, cape_3d, cloudfrac, ctt, dbz, srhel, udhel, avo, pvo, eth, wetbulb, tvirtual, omega, pw) -from .extension import DiagnosticError +from .extension import (DiagnosticError, omp_set_num_threads, + omp_get_num_threads, + omp_get_max_threads, omp_get_thread_num, + omp_get_num_procs, omp_in_parallel, + omp_set_dynamic, omp_get_dynamic, omp_set_nested, + omp_get_nested, omp_set_schedule, + omp_get_schedule, omp_get_thread_limit, + omp_set_max_active_levels, + omp_get_max_active_levels, omp_get_level, + omp_get_ancestor_thread_num, omp_get_team_size, + omp_get_active_level, omp_in_final, + omp_init_lock, omp_init_nest_lock, + omp_destroy_lock, omp_destroy_nest_lock, + omp_set_lock, omp_set_nest_lock, + omp_unset_lock, omp_unset_nest_lock, + omp_test_lock, omp_test_nest_lock, + omp_get_wtime, omp_get_wtick) from .interp import (interplevel, vertcross, interpline, vinterp) from .latlon import (xy_to_ll, ll_to_xy, xy_to_ll_proj, ll_to_xy_proj) from .py3compat import (viewitems, viewkeys, viewvalues, isstr, py2round, @@ -49,7 +65,23 @@ __all__ += ["xy", "interp1d", "interp2dxy", "interpz3d", "slp", "tk", "td", "rh", "uvmet", "smooth2d", "cape_2d", "cape_3d", "cloudfrac", "ctt", "dbz", "srhel", "udhel", "avo", "pvo", "eth", "wetbulb", "tvirtual", "omega", "pw"] -__all__ += ["DiagnosticError"] +__all__ += ["DiagnosticError", "omp_set_num_threads", + "omp_get_num_threads", + "omp_get_max_threads", "omp_get_thread_num", + "omp_get_num_procs", "omp_in_parallel", + "omp_set_dynamic", "omp_get_dynamic", "omp_set_nested", + "omp_get_nested", "omp_set_schedule", + "omp_get_schedule", "omp_get_thread_limit", + "omp_set_max_active_levels", + "omp_get_max_active_levels", "omp_get_level", + "omp_get_ancestor_thread_num", "omp_get_team_size", + "omp_get_active_level", "omp_in_final", + "omp_init_lock", "omp_init_nest_lock", + "omp_destroy_lock", "omp_destroy_nest_lock", + "omp_set_lock", "omp_set_nest_lock", + "omp_unset_lock", "omp_unset_nest_lock", + "omp_test_lock", "omp_test_nest_lock", + "omp_get_wtime", "omp_get_wtick"] __all__ += ["interplevel", "vertcross", "interpline", "vinterp"] __all__ += ["xy_to_ll", "ll_to_xy", "xy_to_ll_proj", "ll_to_xy_proj"] __all__ += ["viewitems", "viewkeys", "viewvalues", "isstr", "py2round", diff --git a/src/wrf/constants.py b/src/wrf/constants.py index c8270db..8c62d37 100755 --- a/src/wrf/constants.py +++ b/src/wrf/constants.py @@ -4,7 +4,7 @@ from __future__ import (absolute_import, division, print_function, import numpy as np from .py3compat import viewitems -from wrf._wrffortran import wrf_constants +from wrf._wrffortran import wrf_constants, omp_constants #: Indicates that all times should be used in a diagnostic routine. ALL_TIMES = None @@ -15,6 +15,12 @@ class Constants(object): for key,val in viewitems(wrf_constants.__dict__): setattr(Constants, key.upper(), np.asscalar(val)) +setattr(Constants, "OMP_SCHED_STATIC", omp_constants.fomp_sched_static) +setattr(Constants, "OMP_SCHED_DYNAMIC", omp_constants.fomp_sched_dynamic) +setattr(Constants, "OMP_SCHED_GUIDED", omp_constants.fomp_sched_guided) +setattr(Constants, "OMP_SCHED_AUTO", omp_constants.fomp_sched_auto) + + class ConversionFactors(object): PA_TO_HPA = .01 PA_TO_TORR = 760.0/101325.0 @@ -29,6 +35,7 @@ class ConversionFactors(object): M_TO_FT = 3.28084 M_TO_MILES = .000621371 + class ProjectionTypes(object): ZERO = 0 LAMBERT_CONFORMAL = 1 diff --git a/src/wrf/extension.py b/src/wrf/extension.py index 777e29d..6887f87 100755 --- a/src/wrf/extension.py +++ b/src/wrf/extension.py @@ -12,7 +12,23 @@ from ._wrffortran import (dcomputetk, dinterp3dz, dinterp2dxy, dinterp1d, dcomputeabsvort, dlltoij, dijtoll, deqthecalc, omgcalc, virtual_temp, wetbulbcalc, dcomputepw, wrf_monotonic, wrf_vintrp, dcomputewspd, - dcomputewdir) + dcomputewdir, + fomp_set_num_threads, fomp_get_num_threads, + fomp_get_max_threads, fomp_get_thread_num, + fomp_get_num_procs, fomp_in_parallel, + fomp_set_dynamic, fomp_get_dynamic, fomp_set_nested, + fomp_get_nested, fomp_set_schedule, + fomp_get_schedule, fomp_get_thread_limit, + fomp_set_max_active_levels, + fomp_get_max_active_levels, fomp_get_level, + fomp_get_ancestor_thread_num, fomp_get_team_size, + fomp_get_active_level, fomp_in_final, + fomp_init_lock, fomp_init_nest_lock, + fomp_destroy_lock, fomp_destroy_nest_lock, + fomp_set_lock, fomp_set_nest_lock, + fomp_unset_lock, fomp_unset_nest_lock, + fomp_test_lock, fomp_test_nest_lock, + fomp_get_wtime, fomp_get_wtick) from .decorators import (left_iteration, cast_type, extract_and_transpose, check_args) @@ -908,5 +924,136 @@ def _wdir(u, v, outview=None): v) return result + + +# OpenMP wrappers + +def omp_set_num_threads(num_threads): + fomp_set_num_threads(num_threads) + + +def omp_get_num_threads(): + return fomp_get_num_threads() + + +def omp_get_max_threads(): + return fomp_get_max_threads() + + +def omp_get_thread_num(): + return fomp_get_thread_num() + + +def omp_get_num_procs(): + return fomp_get_num_procs() + + +def omp_in_parallel(): + return fomp_in_parallel() + + +def omp_set_dynamic(dynamic_threads): + fomp_set_dynamic(dynamic_threads) + + +def omp_get_dynamic(): + return fomp_get_dynamic() + + +def omp_set_nested(nested): + fomp_set_nested(nested) + + +def omp_get_nested(): + return fomp_get_nested() + + +def omp_set_schedule(kind, modifier): + fomp_set_schedule(kind, modifier) + + +def omp_get_schedule(): + return fomp_get_schedule() + + +def omp_get_thread_limit(): + return fomp_get_thread_limit() + + +def omp_set_max_active_levels(max_levels): + omp_set_max_active_levels(max_levels) + + +def omp_get_max_active_levels(): + return fomp_get_max_active_levels() + + +def omp_get_level(): + return fomp_get_level() + + +def omp_get_ancestor_thread_num(level): + return fomp_get_ancestor_thread_num(level) + + +def omp_get_team_size(level): + return fomp_get_team_size(level) + + +def omp_get_active_level(): + return fomp_get_active_level() + + +def omp_in_final(): + return fomp_in_final() + + +def omp_init_lock(): + return fomp_init_lock() + + +def omp_init_nest_lock(): + return fomp_init_nest_lock() + + +def omp_destroy_lock(svar): + fomp_destroy_lock(svar) + + +def omp_destroy_nest_lock(nvar): + fomp_destroy_nest_lock(nvar) + + +def omp_set_lock(svar): + fomp_set_lock(svar) + + +def omp_set_nest_lock(nvar): + fomp_set_nest_lock(nvar) + + +def omp_unset_lock(svar): + fomp_unset_lock(svar) + + +def omp_unset_nest_lock(nvar): + fomp_unset_nest_lock(nvar) + + +def omp_test_lock(svar): + return fomp_test_lock(svar) + + +def omp_test_nest_lock(nvar): + return fomp_test_nest_lock(nvar) + + +def omp_get_wtime(): + return fomp_get_wtime() + + +def omp_get_wtick(): + return fomp_get_wtick() + From 69b990f1aa93e91bc3c23c37a1ab826e2e3871a6 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Mon, 4 Dec 2017 16:44:38 -0700 Subject: [PATCH 25/43] Added omp unit tests. Added some omp documentation. --- src/wrf/extension.py | 155 ++++++++++++++++++++++++++++++++++++++++++- test/test_omp.py | 121 +++++++++++++++++++++++++++++++++ 2 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 test/test_omp.py diff --git a/src/wrf/extension.py b/src/wrf/extension.py index 6887f87..cd4acd5 100755 --- a/src/wrf/extension.py +++ b/src/wrf/extension.py @@ -929,34 +929,187 @@ def _wdir(u, v, outview=None): # OpenMP wrappers def omp_set_num_threads(num_threads): + """Specify the number of threads to use. + + The omp_set_num_threads routine affects the number of threads to be used + for subsequent parallel regions that do not specify a num_threads + clause, by setting the value of the first element of the nthreads-var + ICV of the current task. + + Args: + + num_threads (a positive :obj:`int`): The number of threads. Must be + positive. + + Returns: + + None. + + """ + if num_threads < 0: + raise ValueError("'num_threads' must be a positive integer.") + fomp_set_num_threads(num_threads) def omp_get_num_threads(): + """Return the number of threads in the current team. + + The omp_get_num_threads routine returns the number of threads in the + team executing the parallel region to which the routine region binds. + If called from the sequential part of a program, this routine returns 1. + + Note: + + This function always returns 1 when called from within Python. + + Returns: + + :obj:`int`: The number of threads in the current team. + + See Also: + + :meth:`wrf.omp_get_max_threads`, :meth:`wrf.omp_set_num_threads` + + """ return fomp_get_num_threads() def omp_get_max_threads(): + """Return the maximum number of threads that can be used in a parallel + region. + + The omp_get_max_threads routine returns an upper bound on the number of + threads that could be used to form a new team if a parallel construct + without a num_threads clause were encountered after execution returns from + this routine. + + Returns: + + :obj:`int`: The number of threads in the current team. + + See Also: + + :meth:`wrf.omp_set_num_threads` + + """ return fomp_get_max_threads() def omp_get_thread_num(): + """Return the thread number, within the current team, of the + calling thread. + + The omp_get_thread_num routine returns the thread number of the calling + thread, within the team executing the parallel region to which the routine + region binds. The thread number is an integer between 0 and one less than + the value returned by omp_get_num_threads, inclusive. The thread number of + the master thread of the team is 0. The routine returns 0 if it is called + from the sequential part of a program. + + Note: + + This function always returns 0 when called from within Python. + + Returns: + + :obj:`int`: The thread number. + + See Also: + + :meth:`wrf.omp_get_num_procs` + + """ return fomp_get_thread_num() def omp_get_num_procs(): + """Return the number of processors on the device. + + The omp_get_num_procs routine returns the number of processors that are + available to the device at the time the routine is called. This value may + change between the time that it is determined by the omp_get_num_procs + routine and the time that it is read in the calling context due to system + actions outside the control of the OpenMP implementation. + + Returns: + + :obj:`int`: The number of processors. + + """ return fomp_get_num_procs() def omp_in_parallel(): + """Returns 1 if the active-levels-var ICV is greater than zero; otherwise, + it returns 0. + + The effect of the omp_in_parallel routine is to return 1 if the current + task is enclosed by an active parallel region, and the parallel region is + enclosed by the outermost initial task region on the device; otherwise it + returns 0. + + Note: + + This function always returns 0 when called from within Python. + + Returns: + + :obj:`int`: Returns 1 if the active-levels-var ICV is greater than + zero. Otherwise, it returns 0. + + """ return fomp_in_parallel() def omp_set_dynamic(dynamic_threads): + """Enables or disables dynamic adjustment of the number of threads + available for the execution of subsequent parallel regions by setting the + value of the dyn-var ICV. + + For implementations that support dynamic adjustment of the number of + threads, if the argument to omp_set_dynamic evaluates to True, dynamic + adjustment is enabled for the current task; otherwise, dynamic adjustment + is disabled for the current task. For implementations that do not support + dynamic adjustment of the number of threads this routine has no effect: + the value of dyn-var remains false. + + Args: + + dynamic_threads (:obj:`bool`): Set to True to support the dynamic + adjustment of the number of threads. Otherwise, set to False. + + Returns: + + None. + + See Also: + + :meth:`wrf.omp_get_dynamic` + + """ fomp_set_dynamic(dynamic_threads) def omp_get_dynamic(): + """Returns the value of the dyn-var ICV, which determines whether + dynamic adjustment of the number of threads is enabled or disabled. + + This routine returns 1 if dynamic adjustment of the number of threads + is enabled for the current task; it returns 0, otherwise. If an + implementation does not support dynamic adjustment of the + number of threads, then this routine always returns false. + + Returns: + + :obj:`int`: Returns 1 if dynamic thread adjustment is enabled, 0 + if disabled. + + See Also: + + :meth:`wrf.omp_set_dynamic` + + """ return fomp_get_dynamic() @@ -981,7 +1134,7 @@ def omp_get_thread_limit(): def omp_set_max_active_levels(max_levels): - omp_set_max_active_levels(max_levels) + fomp_set_max_active_levels(max_levels) def omp_get_max_active_levels(): diff --git a/test/test_omp.py b/test/test_omp.py new file mode 100644 index 0000000..ab239c9 --- /dev/null +++ b/test/test_omp.py @@ -0,0 +1,121 @@ +from __future__ import (absolute_import, division, print_function, + unicode_literals) + +import unittest as ut +import numpy.testing as nt + +from wrf import (omp_set_num_threads, omp_get_num_threads, + omp_get_max_threads, omp_get_thread_num, + omp_get_num_procs, omp_in_parallel, + omp_set_dynamic, omp_get_dynamic, omp_set_nested, + omp_get_nested, omp_set_schedule, + omp_get_schedule, omp_get_thread_limit, + omp_set_max_active_levels, + omp_get_max_active_levels, omp_get_level, + omp_get_ancestor_thread_num, omp_get_team_size, + omp_get_active_level, omp_in_final, + omp_init_lock, omp_init_nest_lock, + omp_destroy_lock, omp_destroy_nest_lock, + omp_set_lock, omp_set_nest_lock, + omp_unset_lock, omp_unset_nest_lock, + omp_test_lock, omp_test_nest_lock, + omp_get_wtime, omp_get_wtick) +from wrf import Constants + + +class OmpTest(ut.TestCase): + longMessage = True + + def test_locks(self): + l = omp_init_lock() + omp_set_lock(l) + omp_unset_lock(l) + omp_test_lock(l) + omp_destroy_lock(l) + + nl = omp_init_nest_lock() + omp_set_nest_lock(nl) + omp_unset_nest_lock(nl) + omp_test_nest_lock(nl) + omp_destroy_nest_lock(nl) + + + def test_thread_set(self): + omp_set_num_threads(4) + max_threads = omp_get_max_threads() + self.assertEqual(max_threads, 4) + + num_threads = omp_get_num_threads() + self.assertEqual(num_threads, 1) # Always 1 outside of parallel region + + thread_num = omp_get_thread_num() + self.assertEqual(thread_num, 0) # Always 0 outside of parallel region + num_procs = omp_get_num_procs() + in_parallel = omp_in_parallel() + self.assertFalse(in_parallel) # Always False outside of parallel region + + limit = omp_get_thread_limit() + + + def test_dynamic(self): + omp_set_dynamic(True) + dynamic = omp_get_dynamic() + self.assertTrue(dynamic) + + omp_set_dynamic(False) + dynamic = omp_get_dynamic() + self.assertFalse(dynamic) + + def test_nested(self): + omp_set_nested(True) + nested = omp_get_nested() + self.assertTrue(nested) + + omp_set_nested(False) + nested = omp_get_nested() + self.assertFalse(nested) + + + def test_schedule(self): + omp_set_schedule(Constants.OMP_SCHED_STATIC, 100000) + kind, modifier = omp_get_schedule() + self.assertEqual(kind, Constants.OMP_SCHED_STATIC) + self.assertEqual(modifier, 100000) + + omp_set_schedule(Constants.OMP_SCHED_DYNAMIC, 10000) + kind, modifier = omp_get_schedule() + self.assertEqual(kind, Constants.OMP_SCHED_DYNAMIC) + self.assertEqual(modifier, 10000) + + omp_set_schedule(Constants.OMP_SCHED_GUIDED, 100) + kind, modifier = omp_get_schedule() + self.assertEqual(kind, Constants.OMP_SCHED_GUIDED) + self.assertEqual(modifier, 100) + + omp_set_schedule(Constants.OMP_SCHED_AUTO, 10) + kind, modifier = omp_get_schedule() + self.assertEqual(kind, Constants.OMP_SCHED_AUTO) + self.assertNotEqual(modifier, 10) # The modifier argument is ignored, + # so it will be set to the previous + # value of 100. + + + def test_team_level(self): + omp_set_max_active_levels(10) + active_levels = omp_get_max_active_levels() + self.assertEqual(active_levels, 10) + + level = omp_get_level() + ancestor_thread = omp_get_ancestor_thread_num(level) + team_size = omp_get_team_size(level) + active_level = omp_get_active_level() + in_final = omp_in_final() + + + def test_time(self): + wtime = omp_get_wtime() + wtick = omp_get_wtick() + +if __name__ == "__main__": + ut.main() + \ No newline at end of file From 6239fc16fd9312e6c37f4ad3b5363801f12c8d63 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Fri, 8 Dec 2017 16:29:03 -0700 Subject: [PATCH 26/43] Fixed numerous issues with reading scalars and non-grid variables from the NetCDF file using getvar. Adds unit tests for reading file variables. Fixes #37 --- fortran/wrf_constants.f90 | 10 +++- src/wrf/api.py | 6 +- src/wrf/cape.py | 10 ++-- src/wrf/cloudfrac.py | 8 ++- src/wrf/computation.py | 20 +++---- src/wrf/constants.py | 39 +++++++++++++ src/wrf/decorators.py | 8 +-- src/wrf/extension.py | 8 +-- src/wrf/interp.py | 12 ++-- src/wrf/specialdec.py | 8 +-- src/wrf/util.py | 116 ++++++++++++++++++++++++++++---------- test/test_filevars.py | 51 +++++++++++++++++ 12 files changed, 227 insertions(+), 69 deletions(-) create mode 100644 test/test_filevars.py diff --git a/fortran/wrf_constants.f90 b/fortran/wrf_constants.f90 index 617e63f..336c0e3 100644 --- a/fortran/wrf_constants.f90 +++ b/fortran/wrf_constants.f90 @@ -9,7 +9,15 @@ MODULE wrf_constants REAL(KIND=8), PARAMETER :: PI = 3.1415926535897932384626433D0 REAL(KIND=8), PARAMETER :: RAD_PER_DEG = PI/180.D0 REAL(KIND=8), PARAMETER :: DEG_PER_RAD = 180.D0/PI - REAL(KIND=8), PARAMETER :: DEFAULT_FILL = 9.9692099683868690D36 + REAL(KIND=8), PARAMETER :: DEFAULT_FILL = 9.9692099683868690E36 + INTEGER(KIND=1), PARAMETER :: DEFAULT_FILL_INT8 = -127 + INTEGER(KIND=2), PARAMETER :: DEFAULT_FILL_INT16 = -32767 + INTEGER(KIND=4), PARAMETER :: DEFAULT_FILL_INT32 = -2147483647 + INTEGER(KIND=8), PARAMETER :: DEFAULT_FILL_INT64 = INT(-9223372036854775806D0, KIND=8) + REAL(KIND=4), PARAMETER :: DEFAULT_FILL_FLOAT = 9.9692099683868690E36 + REAL(KIND=8), PARAMETER :: DEFAULT_FILL_DOUBLE = 9.9692099683868690E36 + CHARACTER(LEN=1), PARAMETER :: DEFAULT_FILL_CHAR = ACHAR(0) + REAL(KIND=8), PARAMETER :: P1000MB = 100000.D0 ! j/k/kg diff --git a/src/wrf/api.py b/src/wrf/api.py index 877096d..9333b2d 100644 --- a/src/wrf/api.py +++ b/src/wrf/api.py @@ -3,7 +3,8 @@ from .config import (xarray_enabled, disable_xarray, enable_xarray, basemap_enabled, disable_basemap, enable_basemap, pyngl_enabled, enable_pyngl, disable_pyngl, set_cache_size, get_cache_size) -from .constants import ALL_TIMES, Constants, ConversionFactors, ProjectionTypes +from .constants import (ALL_TIMES, Constants, ConversionFactors, + ProjectionTypes, default_fill) from .destag import destagger from .routines import getvar from .computation import (xy, interp1d, interp2dxy, interpz3d, slp, tk, td, rh, @@ -58,7 +59,8 @@ __all__ += ["xarray_enabled", "disable_xarray", "enable_xarray", "basemap_enabled", "disable_basemap", "enable_basemap", "pyngl_enabled", "enable_pyngl", "disable_pyngl", "set_cache_size", "get_cache_size"] -__all__ += ["ALL_TIMES", "Constants", "ConversionFactors", "ProjectionTypes"] +__all__ += ["ALL_TIMES", "Constants", "ConversionFactors", "ProjectionTypes", + "default_fill"] __all__ += ["destagger"] __all__ += ["getvar"] __all__ += ["xy", "interp1d", "interp2dxy", "interpz3d", "slp", "tk", "td", diff --git a/src/wrf/cape.py b/src/wrf/cape.py index 7cb7efd..5f5ef10 100755 --- a/src/wrf/cape.py +++ b/src/wrf/cape.py @@ -6,13 +6,13 @@ import numpy.ma as ma from .extension import _tk, _cape from .destag import destagger -from .constants import Constants, ConversionFactors +from .constants import default_fill, Constants, ConversionFactors from .util import extract_vars from .metadecorators import set_cape_metadata @set_cape_metadata(is2d=True) def get_2dcape(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, - meta=True, _key=None, missing=Constants.DEFAULT_FILL): + meta=True, _key=None, missing=default_fill(np.float64)): """Return the 2d fields of CAPE, CIN, LCL, and LFC. The leftmost dimension of the returned array represents four different @@ -66,7 +66,7 @@ def get_2dcape(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, purposes only. Default is None. missing (:obj:`float`): The fill value to use for the output. - Default is :data:`wrf.Constants.DEFAULT_FILL`. + Default is :data:`wrf.default_fill(np.float64)`. Returns: :class:`xarray.DataArray` or :class:`numpy.ndarray`: The @@ -130,7 +130,7 @@ def get_2dcape(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, @set_cape_metadata(is2d=False) def get_3dcape(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, - _key=None, missing=Constants.DEFAULT_FILL): + _key=None, missing=default_fill(np.float64)): """Return the three-dimensional CAPE and CIN. The leftmost dimension of the returned array represents two different @@ -182,7 +182,7 @@ def get_3dcape(wrfin, timeidx=0, method="cat", purposes only. Default is None. missing (:obj:`float`): The fill value to use for the output. - Default is :data:`wrf.Constants.DEFAULT_FILL`. + Default is :data:`wrf.default_fill(np.float64)`. Returns: :class:`xarray.DataArray` or :class:`numpy.ndarray`: The diff --git a/src/wrf/cloudfrac.py b/src/wrf/cloudfrac.py index 38805c6..f093146 100644 --- a/src/wrf/cloudfrac.py +++ b/src/wrf/cloudfrac.py @@ -1,19 +1,21 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) -from .constants import Constants +import numpy as np +import numpy.ma as ma + +from .constants import Constants, default_fill from .extension import _tk, _rh, _cloudfrac from .metadecorators import set_cloudfrac_metadata from .util import extract_vars from .geoht import _get_geoht -import numpy.ma as ma @set_cloudfrac_metadata() def get_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, vert_type="pres", low_thresh=None, mid_thresh=None, - high_thresh=None, missing=Constants.DEFAULT_FILL): + high_thresh=None, missing=default_fill(np.float64)): """Return the cloud fraction for low, mid, and high level clouds. The leftmost dimension of the returned array represents three different diff --git a/src/wrf/computation.py b/src/wrf/computation.py index 1a0e1c3..bce2c75 100644 --- a/src/wrf/computation.py +++ b/src/wrf/computation.py @@ -4,7 +4,7 @@ from __future__ import (absolute_import, division, print_function, import numpy as np import numpy.ma as ma -from .constants import Constants +from .constants import default_fill from .extension import (_interpz3d, _interp2dxy, _interp1d, _slp, _tk, _td, _rh, _uvmet, _smooth2d, _cape, _cloudfrac, _ctt, _dbz, _srhel, _udhel, _avo, _pvo, _eth, _wetbulb, _tv, @@ -104,7 +104,7 @@ def xy(field, pivot_point=None, angle=None, start_point=None, end_point=None, @set_interp_metadata("1d") -def interp1d(field, z_in, z_out, missing=Constants.DEFAULT_FILL, +def interp1d(field, z_in, z_out, missing=default_fill(np.float64), meta=True): """Return the linear interpolation of a one-dimensional variable. @@ -128,7 +128,7 @@ def interp1d(field, z_in, z_out, missing=Constants.DEFAULT_FILL, to. Must be the same type as *z_in*. missing (:obj:`float`, optional): The fill value to use for the - output. Default is :data:`wrf.Constants.DEFAULT_FILL`. + output. Default is :data:`wrf.default_fill(np.float64)`. meta (:obj:`bool`, optional): Set to False to disable metadata and return :class:`numpy.ndarray` instead of @@ -251,7 +251,7 @@ def interp2dxy(field3d, xy, meta=True): @set_interp_metadata("horiz") -def interpz3d(field3d, vert, desiredlev, missing=Constants.DEFAULT_FILL, +def interpz3d(field3d, vert, desiredlev, missing=default_fill(np.float64), meta=True): """Return the field interpolated to a specified pressure or height level. @@ -280,7 +280,7 @@ def interpz3d(field3d, vert, desiredlev, missing=Constants.DEFAULT_FILL, Must be in the same units as the *vert* parameter. missing (:obj:`float`): The fill value to use for the output. - Default is :data:`wrf.Constants.DEFAULT_FILL`. + Default is :data:`wrf.default_fill(numpy.float64)`. meta (:obj:`bool`): Set to False to disable metadata and return :class:`numpy.ndarray` instead of @@ -713,7 +713,7 @@ def smooth2d(field, passes, meta=True): @set_cape_alg_metadata(is2d=True, copyarg="pres_hpa") def cape_2d(pres_hpa, tkel, qv, height, terrain, psfc_hpa, ter_follow, - missing=Constants.DEFAULT_FILL, meta=True): + missing=default_fill(np.float64), meta=True): """Return the two-dimensional CAPE, CIN, LCL, and LFC. This function calculates the maximum convective available potential @@ -790,7 +790,7 @@ def cape_2d(pres_hpa, tkel, qv, height, terrain, psfc_hpa, ter_follow, False for pressure level data. missing (:obj:`float`, optional): The fill value to use for the - output. Default is :data:`wrf.Constants.DEFAULT_FILL`. + output. Default is :data:`wrf.default_fill(numpy.float64)`. meta (:obj:`bool`): Set to False to disable metadata and return :class:`numpy.ndarray` instead of @@ -843,7 +843,7 @@ def cape_2d(pres_hpa, tkel, qv, height, terrain, psfc_hpa, ter_follow, @set_cape_alg_metadata(is2d=False, copyarg="pres_hpa") def cape_3d(pres_hpa, tkel, qv, height, terrain, psfc_hpa, ter_follow, - missing=Constants.DEFAULT_FILL, meta=True): + missing=default_fill(np.float64), meta=True): """Return the three-dimensional CAPE and CIN. This function calculates the maximum convective available potential @@ -926,7 +926,7 @@ def cape_3d(pres_hpa, tkel, qv, height, terrain, psfc_hpa, ter_follow, False for pressure level data. missing (:obj:`float`, optional): The fill value to use for the - output. Default is :data:`wrf.Constants.DEFAULT_FILL`. + output. Default is :data:`wrf.default_fill(numpy.float64)`. meta (:obj:`bool`): Set to False to disable metadata and return :class:`numpy.ndarray` instead of @@ -964,7 +964,7 @@ def cape_3d(pres_hpa, tkel, qv, height, terrain, psfc_hpa, ter_follow, @set_cloudfrac_alg_metadata(copyarg="vert") def cloudfrac(vert, relh, vert_inc_w_height, low_thresh, mid_thresh, - high_thresh, missing=Constants.DEFAULT_FILL, meta=True): + high_thresh, missing=default_fill(np.float64), meta=True): """Return the cloud fraction. The leftmost dimension of the returned array represents three different diff --git a/src/wrf/constants.py b/src/wrf/constants.py index 8c62d37..96dcc02 100755 --- a/src/wrf/constants.py +++ b/src/wrf/constants.py @@ -1,6 +1,8 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) +from sys import version_info +import struct import numpy as np from .py3compat import viewitems @@ -42,5 +44,42 @@ class ProjectionTypes(object): POLAR_STEREOGRAPHIC = 2 MERCATOR = 3 LAT_LON = 6 + +# Create the default fill mapping based on type. +_DEFAULT_FILL_MAP = {None: Constants.DEFAULT_FILL, + np.dtype(np.bool_) : False, + np.dtype(np.intc) : Constants.DEFAULT_FILL_INT32, # Usually true + np.dtype(np.int8) : Constants.DEFAULT_FILL_INT8, + np.dtype(np.uint8) : 255, + np.dtype(np.int16) : Constants.DEFAULT_FILL_INT16, + np.dtype(np.uint16) : 65535, + np.dtype(np.int32) : Constants.DEFAULT_FILL_INT32, + np.dtype(np.uint32) : 4294967295, + np.dtype(np.int64) : Constants.DEFAULT_FILL_INT64, + np.dtype(np.uint64) : 18446744073709551614, + np.dtype(np.float_) : Constants.DEFAULT_FILL_DOUBLE, + np.dtype(np.float32) : Constants.DEFAULT_FILL_FLOAT, + np.dtype(np.float64) : Constants.DEFAULT_FILL_DOUBLE + } + +if version_info >= (3, ): + _DEFAULT_FILL_MAP[np.int_] = Constants.DEFAULT_FILL_INT64 +else: + _DEFAULT_FILL_MAP[np.int_] = Constants.DEFAULT_FILL_INT32 + +if (struct.calcsize("P") == 8): + _DEFAULT_FILL_MAP[np.intp] = Constants.DEFAULT_FILL_INT64 +else: + _DEFAULT_FILL_MAP[np.intp] = Constants.DEFAULT_FILL_INT32 + + +# Add the integers based on python 2.x or 3.x +def default_fill(dtype=None): + dt = np.dtype(dtype) if dtype is not None else None + return _DEFAULT_FILL_MAP.get(dt, 0) + + + + \ No newline at end of file diff --git a/src/wrf/decorators.py b/src/wrf/decorators.py index 406dcec..7b1797f 100644 --- a/src/wrf/decorators.py +++ b/src/wrf/decorators.py @@ -11,7 +11,7 @@ from .units import do_conversion, check_units, dealias_and_clean_unit from .util import iter_left_indexes, from_args, to_np, combine_dims from .py3compat import viewitems, viewvalues, isstr from .config import xarray_enabled -from .constants import Constants +from .constants import default_fill if xarray_enabled(): from xarray import DataArray @@ -201,7 +201,7 @@ def left_iteration(ref_var_expected_dims, if all_masked: for output in viewvalues(outd): output[left_and_slice_idxs] = ( - Constants.DEFAULT_FILL) + default_fill(np.float64)) skip_missing = True mask_output = True break @@ -240,9 +240,9 @@ def left_iteration(ref_var_expected_dims, # Mostly when used with join if mask_output: if isinstance(output, np.ndarray): - output = ma.masked_values(output, Constants.DEFAULT_FILL) + output = ma.masked_values(output, default_fill(np.float64)) else: - output = tuple(ma.masked_values(arr, Constants.DEFAULT_FILL) + output = tuple(ma.masked_values(arr, default_fill(np.float64)) for arr in output) return output diff --git a/src/wrf/extension.py b/src/wrf/extension.py index cd4acd5..a9c31a2 100755 --- a/src/wrf/extension.py +++ b/src/wrf/extension.py @@ -3,7 +3,7 @@ from __future__ import (absolute_import, division, print_function, import numpy as np -from .constants import Constants +from .constants import Constants, default_fill from ._wrffortran import (dcomputetk, dinterp3dz, dinterp2dxy, dinterp1d, dcomputeseaprs, dfilter2d, dcomputerh, dcomputeuvmet, @@ -381,8 +381,8 @@ def _eth(qv, tk, p, outview=None): @cast_type(arg_idxs=(0,1,2,3)) @extract_and_transpose() def _uvmet(u, v, lat, lon, cen_long, cone, isstag=0, has_missing=False, - umissing=Constants.DEFAULT_FILL, vmissing=Constants.DEFAULT_FILL, - uvmetmissing=Constants.DEFAULT_FILL, outview=None): + umissing=default_fill(np.float64), vmissing=default_fill(np.float64), + uvmetmissing=default_fill(np.float64), outview=None): """Wrapper for dcomputeuvmet. Located in wrf_user.f90. @@ -795,7 +795,7 @@ def _smooth2d(field, passes, outview=None): if isinstance(field, np.ma.MaskedArray): missing = field.fill_value else: - missing = Constants.DEFAULT_FILL + missing = default_fill(np.float64) if outview is None: outview = field.copy(order="A") diff --git a/src/wrf/interp.py b/src/wrf/interp.py index 8741596..bf9e34b 100755 --- a/src/wrf/interp.py +++ b/src/wrf/interp.py @@ -11,7 +11,7 @@ from .metadecorators import set_interp_metadata from .util import extract_vars, is_staggered, get_id, to_np from .py3compat import py3range from .interputils import get_xy, get_xy_z_params, to_xy_coords -from .constants import Constants, ConversionFactors +from .constants import Constants, default_fill, ConversionFactors from .terrain import get_terrain from .geoht import get_height from .temp import get_theta, get_temp, get_eth @@ -20,7 +20,7 @@ from .pressure import get_pressure # Note: Extension decorator is good enough to handle left dims @set_interp_metadata("horiz") -def interplevel(field3d, vert, desiredlev, missing=Constants.DEFAULT_FILL, +def interplevel(field3d, vert, desiredlev, missing=default_fill(np.float64), meta=True): """Return the three-dimensional field interpolated to a horizontal plane at the specified vertical level. @@ -40,7 +40,7 @@ def interplevel(field3d, vert, desiredlev, missing=Constants.DEFAULT_FILL, Must be in the same units as the *vert* parameter. missing (:obj:`float`): The fill value to use for the output. - Default is :data:`wrf.Constants.DEFAULT_FILL`. + Default is :data:`wrf.default_fill(numpy.float64)`. meta (:obj:`bool`): Set to False to disable metadata and return :class:`numpy.ndarray` instead of @@ -90,7 +90,7 @@ def interplevel(field3d, vert, desiredlev, missing=Constants.DEFAULT_FILL, @set_interp_metadata("cross") -def vertcross(field3d, vert, levels=None, missing=Constants.DEFAULT_FILL, +def vertcross(field3d, vert, levels=None, missing=default_fill(np.float64), wrfin=None, timeidx=0, stagger=None, projection=None, pivot_point=None, angle=None, start_point=None, end_point=None, @@ -133,7 +133,7 @@ def vertcross(field3d, vert, levels=None, missing=Constants.DEFAULT_FILL, Default is None. missing (:obj:`float`): The fill value to use for the output. - Default is :data:`wrf.Constants.DEFAULT_FILL`. + Default is :data:`wrf.default_fill(numpy.float64)`. wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ iterable, optional): WRF-ARW NetCDF @@ -653,7 +653,7 @@ def vinterp(wrfin, field, vert_coord, interp_levels, extrapolate=False, if isinstance(field, ma.MaskedArray): missing = field.fill_value else: - missing = Constants.DEFAULT_FILL + missing = default_fill(np.float64) if (field.shape != p.shape): raise ValueError("'field' shape does not match other variable shapes. " diff --git a/src/wrf/specialdec.py b/src/wrf/specialdec.py index 8d5efc7..f72899b 100644 --- a/src/wrf/specialdec.py +++ b/src/wrf/specialdec.py @@ -7,7 +7,7 @@ import wrapt from .util import iter_left_indexes, to_np from .config import xarray_enabled -from .constants import Constants +from .constants import default_fill if xarray_enabled(): from xarray import DataArray @@ -74,12 +74,12 @@ def uvmet_left_iter(alg_dtype=np.float64): v_arr = to_np(v) - umissing = Constants.DEFAULT_FILL + umissing = default_fill(np.float64) if isinstance(u_arr, np.ma.MaskedArray): has_missing = True umissing = u_arr.fill_value - vmissing = Constants.DEFAULT_FILL + vmissing = default_fill(np.float64) if isinstance(v_arr, np.ma.MaskedArray): has_missing = True vmissing = v_arr.fill_value @@ -452,7 +452,7 @@ def cloudfrac_left_iter(alg_dtype=np.float64): output = np.empty(output_dims, orig_dtype) has_missing = False - missing = Constants.DEFAULT_FILL + missing = default_fill(np.float64) for left_idxs in iter_left_indexes(extra_dims): left_and_slice_idxs = left_idxs + (slice(None),) low_idxs = left_idxs + (0, slice(None)) diff --git a/src/wrf/util.py b/src/wrf/util.py index fff88ec..cba14aa 100644 --- a/src/wrf/util.py +++ b/src/wrf/util.py @@ -30,7 +30,7 @@ import numpy as np import numpy.ma as ma from .config import xarray_enabled -from .constants import Constants, ALL_TIMES +from .constants import default_fill, ALL_TIMES from .py3compat import (viewitems, viewkeys, isstr, py3range, ucode) from .cache import cache_item, get_cached_item from .geobnds import GeoBounds, NullGeoBounds @@ -39,7 +39,6 @@ from .projection import getproj if xarray_enabled(): from xarray import DataArray - from pandas import NaT _COORD_PAIR_MAP = {"XLAT" : ("XLAT", "XLONG"), @@ -1097,6 +1096,7 @@ def _find_max_time_size(wrfseq): return max_times + def _get_coord_names(wrfin, varname): # Need only the first real file @@ -1219,16 +1219,29 @@ def _build_data_array(wrfnc, varname, timeidx, is_moving_domain, is_multifile, multitime = is_multi_time_req(timeidx) time_idx_or_slice = timeidx if not multitime else slice(None) var = wrfnc.variables[varname] - data = var[time_idx_or_slice, :] + if len(var.shape) > 1: + data = var[time_idx_or_slice, :] + else: + data = var[time_idx_or_slice] # Want to preserve the time dimension if not multitime: - data = data[np.newaxis, :] + if len(var.shape) > 1: + data = data[np.newaxis, :] + else: + data = data[np.newaxis] attrs = OrderedDict(var.__dict__) dimnames = var.dimensions[-data.ndim:] - lat_coord, lon_coord, time_coord = _get_coord_names(wrfnc, varname) + lat_coord = lon_coord = time_coord = None + + try: + if dimnames[-2] == "south_north" and dimnames[-1] == "west_east": + lat_coord, lon_coord, time_coord = _get_coord_names(wrfnc, varname) + except IndexError: + pass + coords = OrderedDict() @@ -1307,10 +1320,9 @@ def _build_data_array(wrfnc, varname, timeidx, is_moving_domain, is_multifile, coords[time_coord] = (lon_coord_dims[0], [time_coord_vals[timeidx]]) - proj_params = get_proj_params(wrfnc) - proj = getproj(**proj_params) - attrs["projection"] = proj - + proj_params = get_proj_params(wrfnc) + proj = getproj(**proj_params) + attrs["projection"] = proj if dimnames[0] == "Time": t = extract_times(wrfnc, timeidx, meta=False, do_xtime=False) @@ -1374,8 +1386,13 @@ def _find_forward(wrfseq, varname, timeidx, is_moving, meta, _key): return _build_data_array(wrfnc, varname, filetimeidx, is_moving, True, _key) else: - result = wrfnc.variables[varname][filetimeidx, :] - return result[np.newaxis, :] # So that nosqueeze works + var = wrfnc.variables[varname] + if len(var.shape) > 1: + result = var[filetimeidx, :] + return result[np.newaxis, :] # So that nosqueeze works + else: + result = var[filetimeidx] + return result[np.newaxis] # So that nosqueeze works else: comboidx += numtimes @@ -1574,7 +1591,10 @@ def _cat_files(wrfseq, varname, timeidx, is_moving, squeeze, meta, _key): startidx = 0 endidx = numtimes - outdata[startidx:endidx, :] = first_var[:] + if first_var.ndim > 1: + outdata[startidx:endidx, :] = first_var[:] + else: + outdata[startidx:endidx] = first_var[:] if xarray_enabled() and meta: latname, lonname, timename = _find_coord_names(first_var.coords) @@ -1633,7 +1653,10 @@ def _cat_files(wrfseq, varname, timeidx, is_moving, squeeze, meta, _key): endidx = startidx + numtimes - outdata[startidx:endidx, :] = vardata[:] + if vardata.ndim > 1: + outdata[startidx:endidx, :] = vardata[:] + else: + outdata[startidx:endidx] = vardata[:] if xarray_enabled() and meta: if timename is not None and not timecached: @@ -1795,7 +1818,8 @@ def _join_files(wrfseq, varname, timeidx, is_moving, meta, _key): if xarray_enabled() and meta: first_var = _build_data_array(wrfnc, varname, ALL_TIMES, is_moving, True, _key) - time_coord = np.full((numfiles, maxtimes), int(NaT), "datetime64[ns]") + time_coord = np.full((numfiles, maxtimes), np.datetime64("NaT"), + "datetime64[ns]") time_coord[file_idx, 0:numtimes] = first_var.coords["Time"][:] else: first_var = wrfnc.variables[varname][:] @@ -1810,8 +1834,11 @@ def _join_files(wrfseq, varname, timeidx, is_moving, meta, _key): outdims += first_var.shape[1:] # For join, always need to start with full masked values - outdata = np.full(outdims, Constants.DEFAULT_FILL, first_var.dtype) - outdata[file_idx, 0:numtimes, :] = first_var[:] + outdata = np.full(outdims, default_fill(first_var.dtype), first_var.dtype) + if first_var.ndim > 1: + outdata[file_idx, 0:numtimes, :] = first_var[:] + else: + outdata[file_idx, 0:numtimes] = first_var[:] # Create the secondary coordinate arrays if xarray_enabled() and meta: @@ -1833,8 +1860,9 @@ def _join_files(wrfseq, varname, timeidx, is_moving, meta, _key): if timename is not None: outxtimes = get_cached_item(_key, timekey) if outxtimes is None: - outxtimes = np.full(outdims[0:2], Constants.DEFAULT_FILL, - first_var.dtype) + outxtimes = np.full(outdims[0:2], + default_fill(first_var.dtype), + first_var.dtype) outxtimes[file_idx, 0:numtimes] = first_var.coords[timename][:] else: timecached = True @@ -1843,8 +1871,9 @@ def _join_files(wrfseq, varname, timeidx, is_moving, meta, _key): if latname is not None: outlats = get_cached_item(_key, latkey) if outlats is None: - outlats = np.full(outcoorddims, Constants.DEFAULT_FILL, - first_var.dtype) + outlats = np.full(outcoorddims, + default_fill(first_var.dtype), + first_var.dtype) outlats[file_idx, 0:numtimes, :] = ( first_var.coords[latname][:]) else: @@ -1853,8 +1882,9 @@ def _join_files(wrfseq, varname, timeidx, is_moving, meta, _key): if lonname is not None: outlons = get_cached_item(_key, lonkey) if outlons is None: - outlons = np.full(outcoorddims, Constants.DEFAULT_FILL, - first_var.dtype) + outlons = np.full(outcoorddims, + default_fill(first_var.dtype), + first_var.dtype) outlons[file_idx, 0:numtimes, :] = ( first_var.coords[lonname][:]) else: @@ -1874,8 +1904,11 @@ def _join_files(wrfseq, varname, timeidx, is_moving, meta, _key): if not multitime: outvar = outvar[np.newaxis, :] - - outdata[file_idx, 0:numtimes, :] = outvar[:] + + if outvar.ndim > 1: + outdata[file_idx, 0:numtimes, :] = outvar[:] + else: + outdata[file_idx, 0:numtimes] = outvar[:] if xarray_enabled() and meta: # For join, the times are a function of fileidx @@ -1904,7 +1937,7 @@ def _join_files(wrfseq, varname, timeidx, is_moving, meta, _key): # then a mask array is needed to flag all the missing arrays with # missing values if file_times_less_than_max: - outdata = np.ma.masked_values(outdata, Constants.DEFAULT_FILL) + outdata = np.ma.masked_values(outdata, default_fill(outdata.dtype)) if xarray_enabled() and meta: # Cache the coords if applicable @@ -1931,8 +1964,8 @@ def _join_files(wrfseq, varname, timeidx, is_moving, meta, _key): outcoords["datetime"] = outdimnames[0:2], time_coord if isinstance(outdata, np.ma.MaskedArray): - outattrs["_FillValue"] = Constants.DEFAULT_FILL - outattrs["missing_value"] = Constants.DEFAULT_FILL + outattrs["_FillValue"] = default_fill(outdata.dtype) + outattrs["missing_value"] = default_fill(outdata.dtype) if timename is not None: outxtimes = outxtimes[:, time_idx_or_slice] @@ -2378,15 +2411,35 @@ def extract_times(wrfin, timeidx, method="cat", squeeze=True, cache=None, else: wrf_list = wrfin + dt = "datetime64[ns]" if not do_xtime else np.float64 + fill_value = (np.datetime64('NaT') if not do_xtime else + default_fill(np.float64)) + try: if method.lower() == "cat": time_list = [file_time for wrf_file in wrf_list for file_time in _file_times(wrf_file, do_xtime)] + time_arr = np.asarray(time_list, dtype=dt) + elif method.lower() == "join": time_list = [[file_time for file_time in _file_times(wrf_file, do_xtime)] - for wrf_file in wrf_list] + for wrf_file in wrf_list] + + num_rows = len(time_list) + num_cols = len(time_list[0]) + + time_arr = np.full((num_rows, num_cols), fill_value, dtype=dt) + for i,row in enumerate(time_list): + if len(row) == num_cols: + time_arr[i,:] = row[:] + else: + for j,val in enumerate(row): + time_arr[i,j] = val + + time_arr = ma.masked_values(time_arr, fill_value) + else: raise ValueError("invalid method argument '{}'".format(method)) except KeyError: @@ -2400,6 +2453,8 @@ def extract_times(wrfin, timeidx, method="cat", squeeze=True, cache=None, outdimnames = ["Time"] else: outdimnames = ["fileidx", "Time"] + outattrs["missing_value"] = fill_value + outattrs["_FillValue"] = fill_value if not do_xtime: outname = "times" @@ -2412,11 +2467,12 @@ def extract_times(wrfin, timeidx, method="cat", squeeze=True, cache=None, outname = "XTIME" - outarr = DataArray(time_list, name=outname, coords=outcoords, + + outarr = DataArray(time_arr, name=outname, coords=outcoords, dims=outdimnames, attrs=outattrs) else: - outarr = np.asarray(time_list, dtype="datetime64[ns]") + outarr = time_arr if not multitime: return outarr[timeidx] diff --git a/test/test_filevars.py b/test/test_filevars.py new file mode 100644 index 0000000..29381f9 --- /dev/null +++ b/test/test_filevars.py @@ -0,0 +1,51 @@ +import unittest as ut +import numpy.testing as nt +import numpy as np +import numpy.ma as ma +import os, sys +import subprocess + +from wrf import getvar, ALL_TIMES + +TEST_DIR = "/Users/ladwig/Documents/wrf_files/wrf_vortex_multi" +TEST_FILENAMES = ["wrfout_d02_2005-08-28_00:00:00", + "wrfout_d02_2005-08-28_12:00:00", + "wrfout_d02_2005-08-29_00:00:00"] +TEST_FILES = [os.path.join(TEST_DIR, x) for x in TEST_FILENAMES] + +# Python 3 +if sys.version_info > (3,): + xrange = range + + +class WRFFileVarsTest(ut.TestCase): + longMessage = True + +def make_test(ncfiles, varname): + def test(self): + t1 = getvar(ncfiles, varname, 0) + t2 = getvar(ncfiles, varname, 0, meta=False) + t3 = getvar(ncfiles, varname, ALL_TIMES) + t4 = getvar(ncfiles, varname, ALL_TIMES, meta=False) + t5 = getvar(ncfiles, varname, ALL_TIMES, method="join") + + return test + + +if __name__ == "__main__": + from netCDF4 import Dataset + + ncfiles = [Dataset(x) for x in TEST_FILES] + + file_vars = ncfiles[0].variables.keys() + + ignore_vars = [] + + for var in file_vars: + if var in ignore_vars: + continue + + test_func1 = make_test(ncfiles, var) + setattr(WRFFileVarsTest, 'test_{0}'.format(var), test_func1) + + ut.main() \ No newline at end of file From e4a192fb47eb9a80d166195e4542601c88765501 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Fri, 8 Dec 2017 16:31:23 -0700 Subject: [PATCH 27/43] Added a no-meta test for join --- test/test_filevars.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_filevars.py b/test/test_filevars.py index 29381f9..9684596 100644 --- a/test/test_filevars.py +++ b/test/test_filevars.py @@ -28,6 +28,7 @@ def make_test(ncfiles, varname): t3 = getvar(ncfiles, varname, ALL_TIMES) t4 = getvar(ncfiles, varname, ALL_TIMES, meta=False) t5 = getvar(ncfiles, varname, ALL_TIMES, method="join") + t6 = getvar(ncfiles, varname, ALL_TIMES, method="join", meta=False) return test From f5bc87562704af7d5f23ed8bf07485e526d50b09 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Mon, 11 Dec 2017 16:43:03 -0700 Subject: [PATCH 28/43] Added docstrings for OpenMP runtime. --- src/wrf/extension.py | 408 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 402 insertions(+), 6 deletions(-) diff --git a/src/wrf/extension.py b/src/wrf/extension.py index a9c31a2..669d4f2 100755 --- a/src/wrf/extension.py +++ b/src/wrf/extension.py @@ -926,7 +926,7 @@ def _wdir(u, v, outview=None): return result -# OpenMP wrappers +# OpenMP runtime wrappers def omp_set_num_threads(num_threads): """Specify the number of threads to use. @@ -1041,8 +1041,8 @@ def omp_get_num_procs(): def omp_in_parallel(): - """Returns 1 if the active-levels-var ICV is greater than zero; otherwise, - it returns 0. + """Return 1 if the active-levels-var ICV is greater than zero; + otherwise, return 0. The effect of the omp_in_parallel routine is to return 1 if the current task is enclosed by an active parallel region, and the parallel region is @@ -1063,7 +1063,7 @@ def omp_in_parallel(): def omp_set_dynamic(dynamic_threads): - """Enables or disables dynamic adjustment of the number of threads + """Enable or disable dynamic adjustment of the number of threads available for the execution of subsequent parallel regions by setting the value of the dyn-var ICV. @@ -1092,13 +1092,13 @@ def omp_set_dynamic(dynamic_threads): def omp_get_dynamic(): - """Returns the value of the dyn-var ICV, which determines whether + """Return the value of the dyn-var ICV, which determines whether dynamic adjustment of the number of threads is enabled or disabled. This routine returns 1 if dynamic adjustment of the number of threads is enabled for the current task; it returns 0, otherwise. If an implementation does not support dynamic adjustment of the - number of threads, then this routine always returns false. + number of threads, then this routine always returns 0. Returns: @@ -1114,98 +1114,494 @@ def omp_get_dynamic(): def omp_set_nested(nested): + """Enable or disable nested parallelism, by setting the nest-var ICV + + For implementations that support nested parallelism, if the argument to + omp_set_nested evaluates to True, nested parallelism is enabled for the + current task; otherwise, nested parallelism is disabled for the current + task. For implementations that do not support nested parallelism, this + routine has no effect: the value of nest-var remains False. + + Args: + + dynamic_threads (:obj:`bool`): Set to True to support nested + parallelism, otherwise False. + + Returns: + + None + + See Also: + + :meth:`wrf.omp_get_nested` + + """ fomp_set_nested(nested) def omp_get_nested(): + """Return the value of the nest-var ICV, which determines if nested + parallelism is enabled or disabled + + This routine returns 1 if nested parallelism is enabled for the current + task; it returns 0, otherwise. If an implementation does not support + nested parallelism, this routine always returns 0. + + Returns: + + :obj:`int`: Returns 1 if nested parallelism is enabled, otherwise 0. + + See Also: + + :meth:`wrf.omp_set_nested` + + """ return fomp_get_nested() def omp_set_schedule(kind, modifier): + """Set the schedule that is applied when *runtime* is used as + schedule kind, by setting the value of the run-sched-var ICV. + + The effect of this routine is to set the value of the run-sched-var ICV + of the current task to the values specified in the two arguments. The + schedule is set to the schedule type specified by the first argument kind. + It can be any of the standard schedule types or any other implementation + specific one. For the schedule types static, dynamic, and guided the + chunk_size is set to the value of the second argument, or to the default + chunk_size if the value of the second argument is less than 1; for the + schedule type auto the second argument has no meaning; for implementation + specific schedule types, the values and associated meanings of the second + argument are implementation defined. + + Args: + + kind (:obj:`int`): Must be :data:`wrf.OMP_SCHED_STATIC`, + :data:`wrf.OMP_SCHED_DYNAMIC`, :data:`wrf.OMP_SCHED_GUIDED`, + or :data:`wrf.OMP_SCHED_AUTO`. + + modifier(:obj:`int`): An implementation specific value, depending on + the choice for *kind*. This parameter is alternatively named + chunk_size in some OpenMP documentation. + + Returns: + + None + + See Also: + + :meth:`wrf.omp_get_schedule` + + """ fomp_set_schedule(kind, modifier) def omp_get_schedule(): + """Return the schedule that is applied when the runtime schedule is used. + + This routine returns the run-sched-var ICV in the task to which the routine + binds. The first item is the schedule kind, which will be one of + :data:`wrf.OMP_SCHED_STATIC`, :data:`wrf.OMP_SCHED_DYNAMIC`, + :data:`wrf.OMP_SCHED_GUIDED`, or :data:`wrf.OMP_SCHED_AUTO`. The second + item returned is the modifier, which is often named chunk_size in + OpenMP documentation. + + Returns: + + :obj:`tuple`: The first item is an :obj:`int` for the schedule *kind*. + The second items is :obj:`int` for the *modifier* (chunk_size). + + See Also: + + :meth:`wrf.omp_set_schedule` + + """ return fomp_get_schedule() def omp_get_thread_limit(): + """Return the maximum number of OpenMP threads available to participate in + the current contention group. + + The omp_get_thread_limit routine returns the value of the thread-limit-var + ICV. + + Returns: + + :obj:`int`: The number of OpenMP threads available to participate in + the current contention group. + + See Also: + + :meth:`wrf.omp_get_max_threads` + + """ return fomp_get_thread_limit() def omp_set_max_active_levels(max_levels): + """Limit the number of nested active parallel regions on the device, + by setting the max-active-levels-var ICV. + + The effect of this routine is to set the value of the max-active-levels-var + ICV to the value specified in the argument. If the number of parallel + levels requested exceeds the number of levels of parallelism supported by + the implementation, the value of the max-active-levels-var ICV will be set + to the number of parallel levels supported by the implementation. This + routine has the described effect only when called from a sequential part + of the program. When called from within an explicit parallel region, the + effect of this routine is implementation defined. + + Args: + + max_levels (:obj:`int`): The maximum number of nested active parallel + regions. + + Returns: + + None. + + See Also: + + :meth:`wrf.omp_get_max_active_levels` + + """ fomp_set_max_active_levels(max_levels) def omp_get_max_active_levels(): + """Return the value of the max-active-levels-var ICV, which determines + the maximum number of nested active parallel regions on the device + + The omp_get_max_active_levels routine returns the value of the + max-active-levels-var ICV, which determines the maximum number of nested + active parallel regions on the device. + + Returns: + + :obj:`int`: The maximum number of nested active parallel regions. + + See Also: + + :meth:`wrf.omp_set_max_active_levels` + + """ return fomp_get_max_active_levels() def omp_get_level(): + """Return the value of the levels-var ICV. + + The effect of the omp_get_level routine is to return the number of nested + parallel regions (whether active or inactive) enclosing the current task + such that all of the parallel regions are enclosed by the outermost initial + task region on the current device. + + Returns: + + :obj:`int`: The number of nested parallel regions. + + See Also: + + :meth:`wrf.omp_get_active_level` + + """ return fomp_get_level() def omp_get_ancestor_thread_num(level): + """Return, for a given nested level of the current thread, the thread + number of the ancestor of the current thread. + + The omp_get_ancestor_thread_num routine returns the thread number of the + ancestor at a given nest level of the current thread or the thread number + of the current thread. If the requested nest level is outside the range of + 0 and the nest level of the current thread, as returned by the + omp_get_level routine, the routine returns -1. + + Args: + + level (:obj:`int`): The nested level of the current thread. + + Returns: + + :obj:`int`: The thread number of the ancestor at a given nest level + of the current thread. + + See Also: + + :meth:`wrf.omp_get_max_active_levels`, :meth:`wrf.omp_get_level` + + """ return fomp_get_ancestor_thread_num(level) def omp_get_team_size(level): + """Return, for a given nested level of the current thread, the size + of the thread team to which the ancestor or the current thread belongs + + The omp_get_team_size routine returns the size of the thread team to which + the ancestor or the current thread belongs. If the requested nested level + is outside the range of 0 and the nested level of the current thread, as + returned by the omp_get_level routine, the routine returns -1. Inactive + parallel regions are regarded like active parallel regions executed with + one thread. + + Args: + + level (:obj:`int`): The nested level of the current thread. + + Returns: + + :obj:`int`: The size of the thread team. + + See Also: + + :meth:`wrf.omp_get_ancestor_thread_num` + + """ return fomp_get_team_size(level) def omp_get_active_level(): + """Return the value of the active-level-vars ICV. + + The effect of the omp_get_active_level routine is to return the number of + nested, active parallel regions enclosing the current task such that all + of the parallel regions are enclosed by the outermost initial task region + on the current device. + + Returns: + + :obj:`int`: The number of nested activate parallel regions. + + See Also: + + :meth:`wrf.omp_get_team_size` + + """ return fomp_get_active_level() def omp_in_final(): + """Return 1 (True) if the routine is executed in a final task region; + otherwise, it returns 0 (False). + + Returns: + + :obj:`int`: Return 1 if the routine is executed in a final task + region, 0 otherwise. + + """ return fomp_in_final() def omp_init_lock(): + """Initialize a simple OpenMP lock. + + Returns: + + :obj:`int`: An integer representing the lock. + + See Also: + + :meth:`wrf.omp_init_nest_lock`, :meth:`wrf.omp_destroy_lock` + + """ return fomp_init_lock() def omp_init_nest_lock(): + """Initialize a nestable OpenMP lock. + + Returns: + + :obj:`int`: An integer representing the nestable lock. + + See Also: + + :meth:`wrf.omp_init_lock` + + """ return fomp_init_nest_lock() def omp_destroy_lock(svar): + """Destroy a simple OpenMP lock. + + This sets the lock to an uninitialized state. + + Args: + + svar (:obj:`int`): An integer representing the lock. + + See Also: + + :meth:`wrf.omp_destroy_nest_lock`, :meth:`wrf.omp_init_lock` + + """ fomp_destroy_lock(svar) def omp_destroy_nest_lock(nvar): + """Destroy a nestable OpenMP lock. + + This sets the lock to an uninitialized state. + + Args: + + nvar (:obj:`int`): An integer representing the nestable lock. + + See Also: + + :meth:`wrf.omp_destroy_lock`, :meth:`wrf.omp_init_nest_lock` + + """ fomp_destroy_nest_lock(nvar) def omp_set_lock(svar): + """Set a simple OpenMP lock. + + Args: + + svar (:obj:`int`): An integer representing the lock. + + See Also: + + :meth:`wrf.omp_unset_lock`, :meth:`wrf.omp_set_nest_lock` + + """ fomp_set_lock(svar) def omp_set_nest_lock(nvar): + """Set a nestable OpenMP lock. + + Args: + + nvar (:obj:`int`): An integer representing the nestable lock. + + See Also: + + :meth:`wrf.omp_unset_nest_lock`, :meth:`wrf.omp_set_lock` + + """ fomp_set_nest_lock(nvar) def omp_unset_lock(svar): + """Unset a simple OpenMP lock. + + Args: + + svar (:obj:`int`): An integer representing the simple lock. + + See Also: + + :meth:`wrf.omp_unset_nest_lock`, :meth:`wrf.omp_set_lock` + + """ fomp_unset_lock(svar) def omp_unset_nest_lock(nvar): + """Unset a nestable OpenMP lock. + + Args: + + nvar (:obj:`int`): An integer representing the nestable lock. + + See Also: + + :meth:`wrf.omp_set_nest_lock`, :meth:`wrf.omp_unset_lock` + + """ fomp_unset_nest_lock(nvar) def omp_test_lock(svar): + """Test a simple OpenMP lock. + + This method attempts to set the lock, but does not suspend execution. + + Args: + + svar (:obj:`int`): An integer representing the simple lock. + + Returns: + + :obj:`int`: Returns 1 (True) if the lock is successfully set, + otherwise 0 (False). + + See Also: + + :meth:`wrf.test_nest_lock`, :meth:`wrf.omp_set_lock` + + """ return fomp_test_lock(svar) def omp_test_nest_lock(nvar): + """Test a nestable OpenMP lock. + + This method attempts to set the lock, but does not suspend execution. + + Args: + + nvar (:obj:`int`): An integer representing the simple lock. + + Returns: + + :obj:`int`: Returns the nesting count if successful, + otherwise 0 (False). + + See Also: + + :meth:`wrf.test_lock`, :meth:`wrf.omp_set_nest_lock` + + """ return fomp_test_nest_lock(nvar) def omp_get_wtime(): + """Return elapsed wall clock time in seconds. + + The omp_get_wtime routine returns a value equal to the elapsed wall clock + time in seconds since some “time in the past”. The actual + “time in the past” is arbitrary, but it is guaranteed not to change during + the execution of the application program. The time returned is a + “per-thread time”, so it is not required to be globally consistent across + all threads participating in an application. + + Returns: + + :obj:`float`: Returns the wall clock time in seconds. + + See Also: + + :meth:`wrf.omp_get_wtick` + + """ return fomp_get_wtime() def omp_get_wtick(): + """Return the precision of the timer used by :meth:`wrf.omp_get_wtime`. + + The omp_get_wtick routine returns a value equal to the number of + seconds between successive clock ticks of the timer used by + :meth:`wrf.omp_get_wtime`. + + Returns: + + :obj:`float`: Returns the precision of the timer. + + See Also: + + :meth:`wrf.omp_get_wtime` + + """ return fomp_get_wtick() From 7353aca4bdf453112cfcd4bd8830b7edeff21493 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Thu, 14 Dec 2017 16:09:48 -0700 Subject: [PATCH 29/43] Set the fortran OpenMP code to use the runtime scheduler so that it can be controlled inside of Python. --- fortran/calc_uh.f90 | 4 +-- fortran/eqthecalc.f90 | 3 +- fortran/rip_cape.f90 | 10 +++---- fortran/wrf_cloud_fracf.f90 | 6 ++-- fortran/wrf_fctt.f90 | 6 ++-- fortran/wrf_pvo.f90 | 4 +-- fortran/wrf_pw.f90 | 2 +- fortran/wrf_relhl.f90 | 2 +- fortran/wrf_rip_phys_routines.f90 | 7 +++-- fortran/wrf_user.f90 | 48 +++++++++++++++++-------------- fortran/wrf_user_dbz.f90 | 6 ++-- fortran/wrf_vinterp.f90 | 7 +++-- fortran/wrf_wind.f90 | 4 +-- 13 files changed, 59 insertions(+), 50 deletions(-) diff --git a/fortran/calc_uh.f90 b/fortran/calc_uh.f90 index 6eccb91..09eafb6 100644 --- a/fortran/calc_uh.f90 +++ b/fortran/calc_uh.f90 @@ -64,7 +64,7 @@ SUBROUTINE DCALCUH(nx, ny, nz, nzp1, zp, mapfct, dx, dy, uhmnhgt, uhmxhgt, us, & !$OMP PARALLEL - !$OMP DO COLLAPSE(3) + !$OMP DO COLLAPSE(3) SCHEDULE(runtime) DO k=2,nz-2 DO j=2,ny-1 DO i=2,nx-1 @@ -83,7 +83,7 @@ SUBROUTINE DCALCUH(nx, ny, nz, nzp1, zp, mapfct, dx, dy, uhmnhgt, uhmxhgt, us, & ! WRITE(6,'(a,f12.1,a,f12.1,a)') & ! 'Calculating UH from ',uhmnhgt,' to ',uhmxhgt,' m AGL' !$OMP DO COLLAPSE(2) PRIVATE(i, j, k, zbot, ztop, kbot, ktop, & - !$OMP wgtlw, wbot, wtop, wsum, wmean, sum, helbot, heltop) + !$OMP wgtlw, wbot, wtop, wsum, wmean, sum, helbot, heltop) SCHEDULE(runtime) DO j=2,ny-2 DO i=2,nx-2 zbot = zp(i,j,2) + uhmnhgt diff --git a/fortran/eqthecalc.f90 b/fortran/eqthecalc.f90 index 33cad88..65c069d 100644 --- a/fortran/eqthecalc.f90 +++ b/fortran/eqthecalc.f90 @@ -32,7 +32,8 @@ SUBROUTINE DEQTHECALC(qvp, tmk, prs, eth, miy, mjx, mkzh) REAL(KIND=8) :: tlcl INTEGER :: i, j, k - !$OMP PARALLEL DO COLLAPSE(3) PRIVATE(i, j, k, q, t, p, e, tlcl) + !$OMP PARALLEL DO COLLAPSE(3) PRIVATE(i, j, k, q, t, p, e, tlcl) & + !$OMP SCHEDULE(runtime) DO k = 1,mkzh DO j = 1,mjx DO i = 1,miy diff --git a/fortran/rip_cape.f90 b/fortran/rip_cape.f90 index a56583e..dfa6f20 100644 --- a/fortran/rip_cape.f90 +++ b/fortran/rip_cape.f90 @@ -226,7 +226,7 @@ SUBROUTINE DPFCALC(prs, sfp, pf, mix, mjy, mkzh, ter_follow) INTEGER :: i,j,k - !$OMP PARALLEL DO COLLAPSE(3) + !$OMP PARALLEL DO COLLAPSE(3) SCHEDULE(runtime) DO j = 1,mjy DO i = 1,mix DO k = 1,mkzh @@ -353,7 +353,7 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& ! calculated the pressure at full sigma levels (a set of pressure ! levels that bound the layers represented by the vertical grid points) - !$OMP PARALLEL DO COLLAPSE(3) + !$OMP PARALLEL DO COLLAPSE(3) SCHEDULE(runtime) DO j = 1,mjy DO i = 1,mix DO k = 1,mkzh @@ -382,7 +382,7 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& !$OMP facden, tmkenv, qvpenv, eslift, qvplift, buoy, benamin, & !$OMP benaccum, zrel, kmax, dz, elfound, & !$OMP kel, klfc, & - !$OMP i, j, k, kpar) + !$OMP i, j, k, kpar) SCHEDULE(runtime) DO j = 1,mjy DO i = 1,mix cape(i,j,1) = 0.D0 @@ -679,7 +679,7 @@ SUBROUTINE DCAPECALC2D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& ! kg/kg (should range from 0.000 to 0.025) ! - !$OMP PARALLEL DO COLLAPSE(3) + !$OMP PARALLEL DO COLLAPSE(3) SCHEDULE(runtime) DO j = 1,mjy DO i = 1,mix DO k = 1,mkzh @@ -712,7 +712,7 @@ SUBROUTINE DCAPECALC2D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& !$OMP benaccum, zrel, kmax, dz, elfound, & !$OMP kel, klfc, pavg, p2, p1, totthe, totqvp, totprs, & !$OMP i, j, k, kpar, kpar1, kpar2, qvppari, tmkpari,p, pup, pdn, q, th, & - !$OMP pp1, pp2, ethmax, eth_temp, klev) + !$OMP pp1, pp2, ethmax, eth_temp, klev) SCHEDULE(runtime) DO j = 1,mjy DO i = 1,mix cape(i,j,1) = 0.D0 diff --git a/fortran/wrf_cloud_fracf.f90 b/fortran/wrf_cloud_fracf.f90 index c925704..9930206 100644 --- a/fortran/wrf_cloud_fracf.f90 +++ b/fortran/wrf_cloud_fracf.f90 @@ -22,7 +22,8 @@ SUBROUTINE DCLOUDFRAC(pres, rh, lowc, midc, highc, nz, ns, ew) midc = 0 highc = 0 - !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i, j, k, kchi, kcmi, kclo) + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i, j, k, kchi, kcmi, kclo) & + !$OMP SCHEDULE(runtime) DO j = 1,ns DO i = 1,ew DO k = 1,nz-1 @@ -85,7 +86,8 @@ SUBROUTINE DCLOUDFRAC2(vert, rh, vert_inc_w_height, low_thresh, mid_thresh, & midc = 0 highc = 0 - !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i, j, k, kchi, kcmi, kclo) + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i, j, k, kchi, kcmi, kclo) & + !$OMP SCHEDULE(runtime) DO j = 1,ns DO i = 1,ew ! A value of -1 means 'not found'. This is needed to handle diff --git a/fortran/wrf_fctt.f90 b/fortran/wrf_fctt.f90 index a1c97da..efc59b6 100644 --- a/fortran/wrf_fctt.f90 +++ b/fortran/wrf_fctt.f90 @@ -26,7 +26,7 @@ SUBROUTINE wrfcttcalc(prs, tk, qci, qcw, qvp, ght, ter, ctt, haveqci, nz, ns, ew !$OMP PARALLEL ! Calculate the surface pressure - !$OMP DO COLLAPSE(2) + !$OMP DO COLLAPSE(2) SCHEDULE(runtime) DO j=1,ns DO i=1,ew ratmix = .001D0*qvp(i,j,1) @@ -40,7 +40,7 @@ SUBROUTINE wrfcttcalc(prs, tk, qci, qcw, qvp, ght, ter, ctt, haveqci, nz, ns, ew END DO !$OMP END DO - !$OMP DO COLLAPSE(3) + !$OMP DO COLLAPSE(3) SCHEDULE(runtime) DO k=1,nz-1 DO j=1,ns DO i=1,ew @@ -52,7 +52,7 @@ SUBROUTINE wrfcttcalc(prs, tk, qci, qcw, qvp, ght, ter, ctt, haveqci, nz, ns, ew !$OMP END DO !$OMP DO COLLAPSE(2) PRIVATE(i, j, k, ripk, opdepthd, opdepthu, & - !$OMP prsctt, dp, p1, p2, fac, arg1) + !$OMP prsctt, dp, p1, p2, fac, arg1) SCHEDULE(runtime) DO j=1,ns DO i=1,ew opdepthd = 0.D0 diff --git a/fortran/wrf_pvo.f90 b/fortran/wrf_pvo.f90 index cef3ed9..0a4e788 100644 --- a/fortran/wrf_pvo.f90 +++ b/fortran/wrf_pvo.f90 @@ -24,7 +24,7 @@ SUBROUTINE DCOMPUTEABSVORT(av, u, v, msfu, msfv, msft, cor, dx, dy, nx, ny, nz,& REAL(KIND=8) :: mm !$OMP PARALLEL DO COLLAPSE(3) PRIVATE(i, j, k, jp1, jm1, ip1, im1, & - !$OMP dsx, dsy, mm, dudy, dvdx, avort) + !$OMP dsx, dsy, mm, dudy, dvdx, avort) SCHEDULE(runtime) DO k = 1,nz DO j = 1,ny DO i = 1,nx @@ -82,7 +82,7 @@ SUBROUTINE DCOMPUTEPV(pv, u, v, theta, prs, msfu, msfv, msft, cor, dx, dy, nx, & !$OMP PARALLEL DO COLLAPSE(3) PRIVATE(i, j, k, kp1, km1, jp1, jm1, ip1, & !$OMP im1, dsx, dsy, mm, dudy, dvdx, avort, & - !$OMP dp, dudp, dvdp, dthdp, dthdx, dthdy) + !$OMP dp, dudp, dvdp, dthdp, dthdx, dthdy) SCHEDULE(runtime) DO k = 1,nz DO J = 1,ny DO i = 1,nx diff --git a/fortran/wrf_pw.f90 b/fortran/wrf_pw.f90 index 275deb4..bd80876 100644 --- a/fortran/wrf_pw.f90 +++ b/fortran/wrf_pw.f90 @@ -22,7 +22,7 @@ SUBROUTINE DCOMPUTEPW(p, tv, qv, ht, pw, nx, ny, nz, nzh) !$OMP PARALLEL DO k=1,nz - !$OMP DO COLLAPSE(2) + !$OMP DO COLLAPSE(2) SCHEDULE(runtime) DO j=1,ny DO i=1,nx pw(i,j) = pw(i,j) + ((p(i,j,k)/(RD*tv(i,j,k)))*qv(i,j,k)*(ht(i,j,k+1) - ht(i,j,k))) diff --git a/fortran/wrf_relhl.f90 b/fortran/wrf_relhl.f90 index 43d4950..b3e562f 100644 --- a/fortran/wrf_relhl.f90 +++ b/fortran/wrf_relhl.f90 @@ -57,7 +57,7 @@ SUBROUTINE DCALRELHL(u, v, ght, ter, top, sreh, miy, mjx, mkzh) !REAL(KIND=8), PARAMETER :: DTR=PI/180.d0, DPR=180.d0/PI !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i,j,k,k10,k3,ktop, cu, cv, x, & - !$OMP sum, dh, sdh, su, sv, ua, va, asp, adr, bsp, bdr) + !$OMP sum, dh, sdh, su, sv, ua, va, asp, adr, bsp, bdr) SCHEDULE(runtime) DO j=1, mjx DO i=1, miy sdh = 0.D0 diff --git a/fortran/wrf_rip_phys_routines.f90 b/fortran/wrf_rip_phys_routines.f90 index ad1306f..51af679 100644 --- a/fortran/wrf_rip_phys_routines.f90 +++ b/fortran/wrf_rip_phys_routines.f90 @@ -79,7 +79,8 @@ SUBROUTINE WETBULBCALC(prs, tmk, qvp, twb, nx, ny, nz, psafile, errstat, errmsg) !$OMP PARALLEL DO COLLAPSE(3) PRIVATE (i, j, k, jt, ip, q, t, p, e, tlcl, & !$OMP eth, fracip, fracip2, fracjt, fracjt2, l1, h1, mid1, rang1, l2, h2, & - !$OMP mid2, rang2, tonpsadiabat) REDUCTION(+:errcnt1, errcnt2) + !$OMP mid2, rang2, tonpsadiabat) REDUCTION(+:errcnt1, errcnt2) & + !$OMP SCHEDULE(runtime) DO k=1,nz DO j=1,ny DO i=1,nx @@ -256,7 +257,7 @@ SUBROUTINE OMGCALC(qvp, tmk, www, prs, omg, mx, my, mz) INTEGER :: i, j, k !REAL(KIND=8), PARAMETER :: GRAV=9.81, RGAS=287.04, EPS=0.622 - !$OMP PARALLEL DO COLLAPSE(3) + !$OMP PARALLEL DO COLLAPSE(3) SCHEDULE(runtime) DO k=1,mz DO j=1,my DO i=1,mx @@ -318,7 +319,7 @@ SUBROUTINE VIRTUAL_TEMP(temp, ratmix, tv, nx, ny, nz) INTEGER :: i,j,k !REAL(KIND=8),PARAMETER :: EPS = 0.622D0 - !$OMP PARALLEL DO COLLAPSE(3) + !$OMP PARALLEL DO COLLAPSE(3) SCHEDULE(runtime) DO k=1,nz DO j=1,ny DO i=1,nx diff --git a/fortran/wrf_user.f90 b/fortran/wrf_user.f90 index 618b864..1e133a1 100644 --- a/fortran/wrf_user.f90 +++ b/fortran/wrf_user.f90 @@ -16,7 +16,7 @@ SUBROUTINE DCOMPUTEPI(pi, pressure, nx, ny, nz) !REAL(KIND=8), PARAMETER :: P1000MB=100000.D0, R_D=287.D0, CP=7.D0*R_D/2.D0 - !$OMP PARALLEL DO COLLAPSE(3) + !$OMP PARALLEL DO COLLAPSE(3) SCHEDULE(runtime) DO k = 1,nz DO j = 1,ny DO i = 1,nx @@ -50,7 +50,7 @@ SUBROUTINE DCOMPUTETK(tk, pressure, theta, nx) !REAL(KIND=8), PARAMETER :: P1000MB=100000.D0, RD=287.D0, CP=7.D0*RD/2.D0 - !$OMP PARALLEL DO + !$OMP PARALLEL DO SCHEDULE(runtime) DO i = 1,nx !pi = (pressure(i)/P1000MB)**(RD/CP) !tk(i) = pi * theta(i) @@ -94,7 +94,7 @@ SUBROUTINE DINTERP3DZ(data3d, out2d, zdata, desiredloc, nx, ny, nz, missingval) END IF !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i,j,kp,dointerp,w1,w2) & - !$OMP FIRSTPRIVATE(ip,im) + !$OMP FIRSTPRIVATE(ip,im) SCHEDULE(runtime) DO i = 1,nx DO j = 1,ny ! Initialize to missing. Was initially hard-coded to -999999. @@ -200,7 +200,8 @@ SUBROUTINE DINTERP2DXY(v3d, v2d, xy, nx, ny, nz, nxy) INTEGER :: i, j, k, ij REAL(KIND=8) :: w11, w12, w21, w22, wx, wy - !$OMP PARALLEL DO PRIVATE(i,j,k,ij,w11,w12,w21,w22,wx,wy) + !$OMP PARALLEL DO PRIVATE(i,j,k,ij,w11,w12,w21,w22,wx,wy) & + !$OMP SCHEDULE(runtime) DO ij = 1,nxy i = MAX(1,MIN(nx-1,INT(xy(1,ij)+1))) j = MAX(1,MIN(ny-1,INT(xy(2,ij)+1))) @@ -252,7 +253,8 @@ SUBROUTINE DINTERP1D(v_in, v_out, z_in, z_out, vmsg, nz_in, nz_out) im = 0 END IF - !$OMP PARALLEL DO PRIVATE(kp, k, interp, height, w1, w2) FIRSTPRIVATE(ip, im) + !$OMP PARALLEL DO PRIVATE(kp, k, interp, height, w1, w2) & + !$OMP FIRSTPRIVATE(ip, im) SCHEDULE(runtime) DO k = 1,nz_out v_out(k) = vmsg @@ -344,7 +346,8 @@ SUBROUTINE DCOMPUTESEAPRS(nx, ny, nz, z, t, p, q, sea_level_pressure, & bad_j = -1 bad_sfp = -1 - !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i,j,k,found) REDUCTION(+:errcnt) + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i,j,k,found) REDUCTION(+:errcnt) & + !$OMP SCHEDULE(runtime) DO j = 1,ny DO i = 1,nx level(i,j) = -1 @@ -382,7 +385,8 @@ SUBROUTINE DCOMPUTESEAPRS(nx, ny, nz, z, t, p, q, sea_level_pressure, & ! Get temperature PCONST Pa above surface. Use this to extrapolate ! the temperature at the surface and down to sea level. - !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i,j,klo,khi) REDUCTION(+:errcnt) + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i,j,klo,khi) REDUCTION(+:errcnt) & + !$OMP SCHEDULE(runtime) DO j = 1,ny DO i = 1,nx @@ -426,7 +430,7 @@ SUBROUTINE DCOMPUTESEAPRS(nx, ny, nz, z, t, p, q, sea_level_pressure, & ! temperatures are *too* hot. IF (ridiculous_mm5_test) THEN - !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(l1,l2,l3) + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(l1,l2,l3) SCHEDULE(runtime) DO j = 1,ny DO i = 1,nx l1 = t_sea_level(i,j) < TC @@ -443,7 +447,7 @@ SUBROUTINE DCOMPUTESEAPRS(nx, ny, nz, z, t, p, q, sea_level_pressure, & END IF ! The grand finale: ta da! - !$OMP PARALLEL DO COLLAPSE(2) + !$OMP PARALLEL DO COLLAPSE(2) SCHEDULE(runtime) DO j = 1,ny DO i = 1,nx !z_half_lowest = z(i,j,1) @@ -492,7 +496,7 @@ SUBROUTINE DFILTER2D(a, b, nx, ny, it, missing) INTEGER :: i, j, iter DO iter=1,it - !$OMP PARALLEL DO COLLAPSE(2) + !$OMP PARALLEL DO COLLAPSE(2) SCHEDULE(runtime) DO j=1,ny DO i = 1,nx b(i,j) = a(i,j) @@ -500,7 +504,7 @@ SUBROUTINE DFILTER2D(a, b, nx, ny, it, missing) END DO !$OMP END PARALLEL DO - !$OMP PARALLEL DO COLLAPSE(2) + !$OMP PARALLEL DO COLLAPSE(2) SCHEDULE(runtime) DO j=2,ny-1 DO i=1,nx IF (b(i,j-1) .EQ. missing .OR. b(i,j) .EQ. missing .OR. & @@ -513,7 +517,7 @@ SUBROUTINE DFILTER2D(a, b, nx, ny, it, missing) END DO !$OMP END PARALLEL DO - !$OMP PARALLEL DO COLLAPSE(2) + !$OMP PARALLEL DO COLLAPSE(2) SCHEDULE(runtime) DO j=1,ny DO i=2,nx-1 IF (b(i-1,j) .EQ. missing .OR. b(i,j) .EQ. missing .OR. & @@ -570,7 +574,7 @@ SUBROUTINE FILTER2D(a, b, nx, ny, it, missing) !$OMP PARALLEL DO iter=1,it - !$OMP DO COLLAPSE(2) + !$OMP DO COLLAPSE(2) SCHEDULE(runtime) DO j=1,ny DO i = 1,nx b(i,j) = a(i,j) @@ -578,7 +582,7 @@ SUBROUTINE FILTER2D(a, b, nx, ny, it, missing) END DO !$OMP END DO - !$OMP DO COLLAPSE(2) + !$OMP DO COLLAPSE(2) SCHEDULE(runtime) DO j=2,ny-1 DO i=1,nx IF (b(i,j-1) .EQ. missing .OR. b(i,j) .EQ. missing .OR. & @@ -591,7 +595,7 @@ SUBROUTINE FILTER2D(a, b, nx, ny, it, missing) END DO !$OMP END DO - !$OMP DO COLLAPSE(2) + !$OMP DO COLLAPSE(2) SCHEDULE(runtime) DO j=1,ny DO i=2,nx-1 IF (b(i-1,j) .EQ. missing .OR. b(i,j) .EQ. missing .OR. & @@ -645,7 +649,7 @@ SUBROUTINE DCOMPUTERH(qv, p, t, rh, nx) INTEGER :: i REAL(KIND=8) :: qvs,es,pressure,temperature - !$OMP PARALLEL DO PRIVATE(qvs, es, pressure, temperature) + !$OMP PARALLEL DO PRIVATE(qvs, es, pressure, temperature) SCHEDULE(runtime) DO i = 1,nx pressure = p(i) temperature = t(i) @@ -760,7 +764,7 @@ SUBROUTINE DCOMPUTEUVMET(u, v, uvmet, longca,longcb,flong,flat, & !$OMP PARALLEL - !$OMP DO COLLAPSE(2) + !$OMP DO COLLAPSE(2) SCHEDULE(runtime) DO j = 1,ny DO i = 1,nx @@ -789,7 +793,7 @@ SUBROUTINE DCOMPUTEUVMET(u, v, uvmet, longca,longcb,flong,flat, & IF (istag .EQ. 0) THEN ! Not staggered IF (.NOT. is_msg_val) THEN ! No missing values used - !$OMP DO COLLAPSE(2) + !$OMP DO COLLAPSE(2) SCHEDULE(runtime) DO j = 1,ny DO i = 1,nx uvmet(i,j,1) = v(i,j)*longcb(i,j) + u(i,j)*longca(i,j) @@ -798,7 +802,7 @@ SUBROUTINE DCOMPUTEUVMET(u, v, uvmet, longca,longcb,flong,flat, & END DO !$OMP END DO ELSE ! Missing values used - !$OMP DO COLLAPSE(2) + !$OMP DO COLLAPSE(2) SCHEDULE(runtime) DO j = 1,ny DO i = 1,nx IF ((u(i,j) .NE. umsg .AND. v(i,j) .NE. vmsg)) THEN @@ -814,7 +818,7 @@ SUBROUTINE DCOMPUTEUVMET(u, v, uvmet, longca,longcb,flong,flat, & END IF ELSE ! Staggered IF (.NOT. is_msg_val) THEN ! No missing values used - !$OMP DO COLLAPSE(2) + !$OMP DO COLLAPSE(2) SCHEDULE(runtime) DO j = 1,ny DO i = 1,nx ! This is the more readable version. @@ -832,7 +836,7 @@ SUBROUTINE DCOMPUTEUVMET(u, v, uvmet, longca,longcb,flong,flat, & END DO !$OMP END DO ELSE ! Missing values used - !$OMP DO COLLAPSE(2) + !$OMP DO COLLAPSE(2) SCHEDULE(runtime) DO j = 1,ny DO i = 1,nx IF (u(i,j) .NE. umsg .AND. v(i,j) .NE. vmsg .AND. u(i+1,j) .NE. umsg .AND. v(i,j+1) .NE. vmsg) THEN @@ -887,7 +891,7 @@ SUBROUTINE DCOMPUTETD(td, pressure, qv_in, nx) INTEGER :: i - !$OMP PARALLEL DO PRIVATE(i,qv,tdc) + !$OMP PARALLEL DO PRIVATE(i,qv,tdc) SCHEDULE(runtime) DO i = 1,nx qv = MAX(qv_in(i), 0.D0) ! vapor pressure diff --git a/fortran/wrf_user_dbz.f90 b/fortran/wrf_user_dbz.f90 index d2c6098..2e5e17b 100644 --- a/fortran/wrf_user_dbz.f90 +++ b/fortran/wrf_user_dbz.f90 @@ -80,7 +80,7 @@ SUBROUTINE CALCDBZ(prs, tmk, qvp, qra, qsn, qgr, sn0, ivarint, iliqskin, dbz, nx !$OMP PARALLEL ! Force all Q arrays to be 0.0 or greater. - !$OMP DO COLLAPSE(3) + !$OMP DO COLLAPSE(3) SCHEDULE(runtime) DO k = 1,nz DO j = 1,ny DO i = 1,nx @@ -104,7 +104,7 @@ SUBROUTINE CALCDBZ(prs, tmk, qvp, qra, qsn, qgr, sn0, ivarint, iliqskin, dbz, nx ! Input pressure is Pa, but we need hPa in calculations IF (sn0 .EQ. 0) THEN - !$OMP DO COLLAPSE(3) + !$OMP DO COLLAPSE(3) SCHEDULE(runtime) DO k = 1,nz DO j = 1,ny DO i = 1,nx @@ -125,7 +125,7 @@ SUBROUTINE CALCDBZ(prs, tmk, qvp, qra, qsn, qgr, sn0, ivarint, iliqskin, dbz, nx !$OMP DO COLLAPSE(3) PRIVATE(i, j, k, temp_c, virtual_t, gonv, ronv, sonv, & !$OMP factorb_g, factorb_s, rhoair, z_e) & - !$OMP FIRSTPRIVATE(factor_r, factor_s, factor_g) + !$OMP FIRSTPRIVATE(factor_r, factor_s, factor_g) SCHEDULE(runtime) DO k = 1,nz DO j = 1,ny DO i = 1,nx diff --git a/fortran/wrf_vinterp.f90 b/fortran/wrf_vinterp.f90 index d5c18c1..2dc7c17 100644 --- a/fortran/wrf_vinterp.f90 +++ b/fortran/wrf_vinterp.f90 @@ -22,7 +22,7 @@ SUBROUTINE wrf_monotonic(out, in, lvprs, cor, idir, delta, ew, ns, nz, icorsw) INTEGER :: i, j, k, k300 - !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i, j, k, k300) + !$OMP PARALLEL DO COLLAPSE(2) PRIVATE(i, j, k, k300) SCHEDULE(runtime) DO j=1,ns DO i=1,ew k300 = -1 @@ -205,7 +205,8 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& !$OMP vclhsl, vctophsl, diff, isign, plhsl, zlhsl, ezlhsl, tlhsl, & !$OMP zsurf, qvapor, psurf, psurfsm, ezsurf, plev, ezlev, zlev, & !$OMP ptarget, dpmin, kupper, pbot, zbot, pratio, tbotextrap, & - !$OMP vt, tlev, gammam, e, tlcl) REDUCTION (+:log_errcnt, interp_errcnt) + !$OMP vt, tlev, gammam, e, tlcl) REDUCTION (+:log_errcnt, interp_errcnt) & + !$OMP SCHEDULE(runtime) DO j=1,ns DO i=1,ew tempout(i,j) = rmsg @@ -421,7 +422,7 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& RETURN END IF - !$OMP PARALLEL DO COLLAPSE(2) + !$OMP PARALLEL DO COLLAPSE(2) SCHEDULE(runtime) DO j = 1,ns DO i = 1,ew dataout(i,j,nreqlvs) = tempout(i,j) diff --git a/fortran/wrf_wind.f90 b/fortran/wrf_wind.f90 index 88d022e..1d93d20 100644 --- a/fortran/wrf_wind.f90 +++ b/fortran/wrf_wind.f90 @@ -13,7 +13,7 @@ SUBROUTINE DCOMPUTEWSPD(wspd, u, v, nx, ny) INTEGER i, j - !$OMP PARALLEL DO COLLAPSE(2) + !$OMP PARALLEL DO COLLAPSE(2) SCHEDULE(runtime) DO j = 1,ny DO i = 1,nx wspd(i,j) = SQRT(u(i,j)*u(i,j) + v(i,j)*v(i,j)) @@ -40,7 +40,7 @@ SUBROUTINE DCOMPUTEWDIR(wdir, u, v, nx, ny) INTEGER i, j - !$OMP PARALLEL DO COLLAPSE(2) + !$OMP PARALLEL DO COLLAPSE(2) SCHEDULE(runtime) DO j = 1,ny DO i = 1,nx wdir(i,j) = MOD(270.0 - ATAN2(v(i,j), u(i,j)) * DEG_PER_RAD, 360.) From 78f34b4e693a47d885861973cd0e4126ecc6f8db Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Fri, 15 Dec 2017 15:21:38 -0700 Subject: [PATCH 30/43] Cleaned up the default implementation of omp.f90 and the pyf file. --- fortran/omp.f90 | 232 +---------------------------------------- fortran/wrffortran.pyf | 11 +- 2 files changed, 10 insertions(+), 233 deletions(-) diff --git a/fortran/omp.f90 b/fortran/omp.f90 index 3da26d4..81805ff 100644 --- a/fortran/omp.f90 +++ b/fortran/omp.f90 @@ -1,9 +1,5 @@ -# 1 "ompgen.F90" -# 1 "" -# 1 "" -# 1 "ompgen.F90" + MODULE omp_constants -# 52 "ompgen.F90" INTEGER, PARAMETER :: fomp_sched_kind = 4 INTEGER, PARAMETER :: fomp_lock_kind = 4 INTEGER, PARAMETER :: fomp_nest_lock_kind = 8 @@ -11,209 +7,135 @@ MODULE omp_constants INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_dynamic = 2 INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_guided = 3 INTEGER(KIND=fomp_sched_kind), PARAMETER :: fomp_sched_auto = 4 - - END MODULE omp_constants SUBROUTINE fomp_set_num_threads(num_threads) - - - IMPLICIT NONE !f2py threadsafe INTEGER, INTENT(IN) :: num_threads - - - - END SUBROUTINE fomp_set_num_threads FUNCTION fomp_get_num_threads() - - - IMPLICIT NONE !f2py threadsafe INTEGER :: fomp_get_num_threads - - - fomp_get_num_threads = -1 - END FUNCTION fomp_get_num_threads FUNCTION fomp_get_max_threads() - - - IMPLICIT NONE !f2py threadsafe INTEGER :: fomp_get_max_threads - - - fomp_get_max_threads = -1 - END FUNCTION fomp_get_max_threads FUNCTION fomp_get_thread_num() - - - IMPLICIT NONE !f2py threadsafe INTEGER :: fomp_get_thread_num - - - fomp_get_thread_num = -1 - END FUNCTION fomp_get_thread_num FUNCTION fomp_get_num_procs() - - - IMPLICIT NONE !f2py threadsafe INTEGER :: fomp_get_num_procs - - - fomp_get_num_procs = -1 - END FUNCTION fomp_get_num_procs FUNCTION fomp_in_parallel() - - - IMPLICIT NONE !f2py threadsafe LOGICAL :: fomp_in_parallel - - - fomp_in_parallel = .FALSE. - END FUNCTION fomp_in_parallel SUBROUTINE fomp_set_dynamic(dynamic_threads) - - - IMPLICIT NONE !f2py threadsafe LOGICAL, INTENT(IN) :: dynamic_threads - - - - END SUBROUTINE fomp_set_dynamic FUNCTION fomp_get_dynamic() - - - IMPLICIT NONE !f2py threadsafe LOGICAL :: fomp_get_dynamic - - - fomp_get_dynamic = .FALSE. - END FUNCTION fomp_get_dynamic SUBROUTINE fomp_set_nested(nested) - - - IMPLICIT NONE !f2py threadsafe LOGICAL, INTENT(IN) :: nested - - - - END SUBROUTINE fomp_set_nested FUNCTION fomp_get_nested() - - - IMPLICIT NONE !f2py threadsafe LOGICAL :: fomp_get_nested - - - fomp_get_nested = .FALSE. - END FUNCTION fomp_get_nested SUBROUTINE fomp_set_schedule(kind, modifier) - - USE omp_constants, ONLY : fomp_sched_kind IMPLICIT NONE @@ -223,17 +145,11 @@ SUBROUTINE fomp_set_schedule(kind, modifier) INTEGER(KIND=fomp_sched_kind), INTENT(IN) :: kind INTEGER, INTENT(IN) :: modifier - - - - END SUBROUTINE fomp_set_schedule SUBROUTINE fomp_get_schedule(kind, modifier) - - USE omp_constants, ONLY : fomp_sched_kind IMPLICIT NONE @@ -243,103 +159,64 @@ SUBROUTINE fomp_get_schedule(kind, modifier) INTEGER(KIND=fomp_sched_kind), INTENT(OUT) :: kind INTEGER, INTENT(OUT) :: modifier - - - kind = -1 modifier = -1 - END SUBROUTINE fomp_get_schedule FUNCTION fomp_get_thread_limit() - - - IMPLICIT NONE !f2py threadsafe INTEGER :: fomp_get_thread_limit - - - fomp_get_thread_limit = -1 - END FUNCTION fomp_get_thread_limit SUBROUTINE fomp_set_max_active_levels(max_levels) - - - IMPLICIT NONE !f2py threadsafe - - - INTEGER, INTENT(IN) :: max_levels - - - - - END SUBROUTINE fomp_set_max_active_levels FUNCTION fomp_get_max_active_levels() - - - IMPLICIT NONE !f2py threadsafe INTEGER :: fomp_get_max_active_levels - - - fomp_get_max_active_levels = -1 - END FUNCTION fomp_get_max_active_levels FUNCTION fomp_get_level() - - - IMPLICIT NONE !f2py threadsafe INTEGER :: fomp_get_level - - - fomp_get_level = -1 - END FUNCTION fomp_get_level FUNCTION fomp_get_ancestor_thread_num(level) - - - IMPLICIT NONE !f2py threadsafe @@ -347,20 +224,13 @@ FUNCTION fomp_get_ancestor_thread_num(level) INTEGER, INTENT(IN) :: level INTEGER :: fomp_get_ancestor_thread_num - - - fomp_get_ancestor_thread_num = -1 - END FUNCTION fomp_get_ancestor_thread_num FUNCTION fomp_get_team_size(level) - - - IMPLICIT NONE !f2py threadsafe @@ -368,59 +238,39 @@ FUNCTION fomp_get_team_size(level) INTEGER, INTENT(IN) :: level INTEGER :: fomp_get_team_size - - - fomp_get_team_size = -1 - END FUNCTION fomp_get_team_size FUNCTION fomp_get_active_level() - - - IMPLICIT NONE !f2py threadsafe INTEGER :: fomp_get_active_level - - - fomp_get_active_level = -1 - END FUNCTION fomp_get_active_level FUNCTION fomp_in_final() - - - IMPLICIT NONE !f2py threadsafe LOGICAL :: fomp_in_final - - - fomp_in_final = .FALSE. - END FUNCTION fomp_in_final SUBROUTINE fomp_init_lock(svar) - - USE omp_constants, ONLY : fomp_lock_kind IMPLICIT NONE @@ -429,19 +279,13 @@ SUBROUTINE fomp_init_lock(svar) INTEGER(KIND=fomp_lock_kind), INTENT(OUT) :: svar - - - svar = -1 - END SUBROUTINE fomp_init_lock SUBROUTINE fomp_init_nest_lock(nvar) - - USE omp_constants, ONLY : fomp_nest_lock_kind IMPLICIT NONE @@ -450,19 +294,13 @@ SUBROUTINE fomp_init_nest_lock(nvar) INTEGER(KIND=fomp_nest_lock_kind), INTENT(OUT) :: nvar - - - nvar = -1 - END SUBROUTINE fomp_init_nest_lock SUBROUTINE fomp_destroy_lock(svar) - - USE omp_constants, ONLY : fomp_lock_kind IMPLICIT NONE @@ -471,17 +309,11 @@ SUBROUTINE fomp_destroy_lock(svar) INTEGER(KIND=fomp_lock_kind), INTENT(INOUT) :: svar - - - - END SUBROUTINE fomp_destroy_lock SUBROUTINE fomp_destroy_nest_lock(nvar) - - USE omp_constants, ONLY : fomp_nest_lock_kind IMPLICIT NONE @@ -490,17 +322,11 @@ SUBROUTINE fomp_destroy_nest_lock(nvar) INTEGER(KIND=fomp_nest_lock_kind), INTENT(INOUT) :: nvar - - - - END SUBROUTINE fomp_destroy_nest_lock SUBROUTINE fomp_set_lock(svar) - - USE omp_constants, ONLY : fomp_lock_kind IMPLICIT NONE @@ -509,17 +335,11 @@ SUBROUTINE fomp_set_lock(svar) INTEGER(KIND=fomp_lock_kind), INTENT(INOUT) :: svar - - - - END SUBROUTINE fomp_set_lock SUBROUTINE fomp_set_nest_lock(nvar) - - USE omp_constants, ONLY : fomp_nest_lock_kind IMPLICIT NONE @@ -528,17 +348,11 @@ SUBROUTINE fomp_set_nest_lock(nvar) INTEGER(KIND=fomp_nest_lock_kind), INTENT(INOUT) :: nvar - - - - END SUBROUTINE fomp_set_nest_lock SUBROUTINE fomp_unset_lock(svar) - - USE omp_constants, ONLY : fomp_lock_kind IMPLICIT NONE @@ -547,17 +361,11 @@ SUBROUTINE fomp_unset_lock(svar) INTEGER(KIND=fomp_lock_kind), INTENT(INOUT) :: svar - - - - END SUBROUTINE fomp_unset_lock SUBROUTINE fomp_unset_nest_lock(nvar) - - USE omp_constants, ONLY : fomp_nest_lock_kind IMPLICIT NONE @@ -566,17 +374,11 @@ SUBROUTINE fomp_unset_nest_lock(nvar) INTEGER(KIND=fomp_nest_lock_kind), INTENT(INOUT) :: nvar - - - - END SUBROUTINE fomp_unset_nest_lock FUNCTION fomp_test_lock(svar) - - USE omp_constants, ONLY : fomp_lock_kind IMPLICIT NONE @@ -586,21 +388,13 @@ FUNCTION fomp_test_lock(svar) INTEGER(KIND=fomp_lock_kind), INTENT(INOUT) :: svar LOGICAL :: fomp_test_lock - - - fomp_test_lock = .FALSE. - - - END FUNCTION fomp_test_lock FUNCTION fomp_test_nest_lock(nvar) - - USE omp_constants, ONLY : fomp_nest_lock_kind IMPLICIT NONE @@ -610,58 +404,34 @@ FUNCTION fomp_test_nest_lock(nvar) INTEGER(KIND=fomp_nest_lock_kind), INTENT(INOUT) :: nvar INTEGER :: fomp_test_nest_lock - - - fomp_test_nest_lock = -1 - - - END FUNCTION fomp_test_nest_lock FUNCTION fomp_get_wtime() - - - IMPLICIT NONE !f2py threadsafe REAL (KIND=8) :: fomp_get_wtime - - - fomp_get_wtime = -1 - - - END FUNCTION fomp_get_wtime FUNCTION fomp_get_wtick() - - - IMPLICIT NONE !f2py threadsafe REAL (KIND=8) :: fomp_get_wtick - - - fomp_get_wtick = -1 - - - END FUNCTION fomp_get_wtick diff --git a/fortran/wrffortran.pyf b/fortran/wrffortran.pyf index 8359fb7..b74da12 100644 --- a/fortran/wrffortran.pyf +++ b/fortran/wrffortran.pyf @@ -295,7 +295,10 @@ python module _wrffortran ! in integer, optional,check(shape(vert,0)==ew),depend(vert) :: ew=shape(vert,0) end subroutine dcloudfrac2 module wrf_constants ! in :_wrffortran:wrf_constants.f90 + real(kind=4), parameter,optional :: default_fill_float=9.96920996839e+36 + integer(kind=8), parameter,optional :: default_fill_int64=int(-9223372036854775806d0, kind=8) real(kind=8), parameter,optional :: wrf_earth_radius=6370000.d0 + character(len=1), parameter,optional :: default_fill_char=achar(0) real(kind=8), parameter,optional :: rhowat=1000.d0 real(kind=8), parameter,optional :: t_base=300.0d0 real(kind=8), parameter,optional :: cp=1004.5d0 @@ -305,6 +308,7 @@ python module _wrffortran ! in real(kind=8), parameter,optional :: p1000mb=100000.d0 real(kind=8), parameter,optional :: rv=461.6d0 real(kind=8), parameter,optional,depend(pi) :: rad_per_deg=pi/180.d0 + real(kind=8), parameter,optional :: default_fill_double=9.96920996839e+36 real(kind=8), parameter,optional :: rd=287.d0 real(kind=8), parameter,optional :: abscoef=.145d0 real(kind=8), parameter,optional :: celkel=273.15d0 @@ -312,16 +316,18 @@ python module _wrffortran ! in real(kind=8), parameter,optional :: eslcon2=29.65d0 real(kind=8), parameter,optional :: eslcon1=17.67d0 real(kind=8), parameter,optional :: pi=3.1415926535897932384626433d0 + integer(kind=1), parameter,optional :: default_fill_int8=-127 real(kind=8), parameter,optional :: tlclc2=3.5d0 real(kind=8), parameter,optional :: tlclc3=4.805d0 real(kind=8), parameter,optional :: rho_g=400.d0 real(kind=8), parameter,optional :: tlclc1=2840.d0 real(kind=8), parameter,optional :: tlclc4=55.d0 + integer(kind=2), parameter,optional :: default_fill_int16=-32767 real(kind=8), parameter,optional,depend(pi) :: deg_per_rad=180.d0/pi real(kind=8), parameter,optional :: cpmd=.887d0 real(kind=8), parameter,optional,depend(rd,g) :: sclht=rd*256.d0/g real(kind=8), parameter,optional :: ussalr=0.0065d0 - real(kind=8), parameter,optional :: default_fill=9.9692099683868690d36 + real(kind=8), parameter,optional :: default_fill=9.96920996839e+36 real(kind=8), parameter,optional :: rho_s=100.d0 real(kind=8), parameter,optional,depend(rhowat) :: rho_r=1000.0 real(kind=8), parameter,optional :: alpha=0.224d0 @@ -331,9 +337,10 @@ python module _wrffortran ! in real(kind=8), parameter,optional :: rgasmd=.608d0 real(kind=8), parameter,optional :: g=9.81d0 integer, optional :: errlen=512 - real(kind=8), parameter,optional :: eps=0.622d0 + integer(kind=4), parameter,optional :: default_fill_int32=-2147483647 real(kind=8), parameter,optional :: gamma_seven=720.d0 real(kind=8), parameter,optional,depend(cpmd,rgasmd) :: gammamd=-0.279 + real(kind=8), parameter,optional :: eps=0.622d0 integer, optional :: algerr=64 real(kind=8), parameter,optional,depend(cp,rd) :: gamma=0.285714285714 real(kind=8), parameter,optional,depend(expon,rd,ussalr,g) :: exponi=5.25864379523 From 38f49d2303c8916fbafc0655f5abe54fef6d78b2 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Fri, 15 Dec 2017 15:57:43 -0700 Subject: [PATCH 31/43] Added dummy code to prevent compiler warnings. Removed unused variables. --- fortran/calc_uh.f90 | 2 +- fortran/omp.f90 | 15 +++++++++++++++ fortran/ompgen.F90 | 32 ++++++++++++++++++++++++++++---- fortran/rip_cape.f90 | 11 +++++------ fortran/wrf_cloud_fracf.f90 | 2 +- fortran/wrf_user.f90 | 5 +++-- fortran/wrf_vinterp.f90 | 1 - 7 files changed, 53 insertions(+), 15 deletions(-) diff --git a/fortran/calc_uh.f90 b/fortran/calc_uh.f90 index 09eafb6..72acfe6 100644 --- a/fortran/calc_uh.f90 +++ b/fortran/calc_uh.f90 @@ -47,7 +47,7 @@ SUBROUTINE DCALCUH(nx, ny, nz, nzp1, zp, mapfct, dx, dy, uhmnhgt, uhmxhgt, us, & ! Misc local variables INTEGER :: i, j, k, kbot, ktop - REAL(KIND=8) :: twodx, twody, wgtlw, sum, wmean, wsum, wavg + REAL(KIND=8) :: twodx, twody, wgtlw, sum, wmean, wsum !,wavg REAL(KIND=8) :: helbot, heltop, wbot, wtop REAL(KIND=8) :: zbot, ztop diff --git a/fortran/omp.f90 b/fortran/omp.f90 index 81805ff..bea1974 100644 --- a/fortran/omp.f90 +++ b/fortran/omp.f90 @@ -17,6 +17,7 @@ SUBROUTINE fomp_set_num_threads(num_threads) !f2py threadsafe INTEGER, INTENT(IN) :: num_threads + IF (.FALSE.) PRINT *, num_threads END SUBROUTINE fomp_set_num_threads @@ -93,6 +94,7 @@ SUBROUTINE fomp_set_dynamic(dynamic_threads) !f2py threadsafe LOGICAL, INTENT(IN) :: dynamic_threads + IF (.FALSE.) PRINT *, dynamic_threads END SUBROUTINE fomp_set_dynamic @@ -117,6 +119,7 @@ SUBROUTINE fomp_set_nested(nested) !f2py threadsafe LOGICAL, INTENT(IN) :: nested + IF (.FALSE.) PRINT *, nested END SUBROUTINE fomp_set_nested @@ -144,6 +147,7 @@ SUBROUTINE fomp_set_schedule(kind, modifier) INTEGER(KIND=fomp_sched_kind), INTENT(IN) :: kind INTEGER, INTENT(IN) :: modifier + IF (.FALSE.) PRINT *, kind, modifier END SUBROUTINE fomp_set_schedule @@ -185,6 +189,7 @@ SUBROUTINE fomp_set_max_active_levels(max_levels) !f2py threadsafe INTEGER, INTENT(IN) :: max_levels + IF (.FALSE.) PRINT *, max_levels END SUBROUTINE fomp_set_max_active_levels @@ -223,6 +228,7 @@ FUNCTION fomp_get_ancestor_thread_num(level) INTEGER, INTENT(IN) :: level INTEGER :: fomp_get_ancestor_thread_num + IF (.FALSE.) PRINT *, level fomp_get_ancestor_thread_num = -1 @@ -237,6 +243,7 @@ FUNCTION fomp_get_team_size(level) INTEGER, INTENT(IN) :: level INTEGER :: fomp_get_team_size + IF (.FALSE.) PRINT *, level fomp_get_team_size = -1 @@ -308,6 +315,7 @@ SUBROUTINE fomp_destroy_lock(svar) !f2py threadsafe INTEGER(KIND=fomp_lock_kind), INTENT(INOUT) :: svar + IF (.FALSE.) PRINT *, svar END SUBROUTINE fomp_destroy_lock @@ -321,6 +329,7 @@ SUBROUTINE fomp_destroy_nest_lock(nvar) !f2py threadsafe INTEGER(KIND=fomp_nest_lock_kind), INTENT(INOUT) :: nvar + IF (.FALSE.) PRINT *, nvar END SUBROUTINE fomp_destroy_nest_lock @@ -334,6 +343,7 @@ SUBROUTINE fomp_set_lock(svar) !f2py threadsafe INTEGER(KIND=fomp_lock_kind), INTENT(INOUT) :: svar + IF (.FALSE.) PRINT *, svar END SUBROUTINE fomp_set_lock @@ -347,6 +357,7 @@ SUBROUTINE fomp_set_nest_lock(nvar) !f2py threadsafe INTEGER(KIND=fomp_nest_lock_kind), INTENT(INOUT) :: nvar + IF (.FALSE.) PRINT *, nvar END SUBROUTINE fomp_set_nest_lock @@ -360,6 +371,7 @@ SUBROUTINE fomp_unset_lock(svar) !f2py threadsafe INTEGER(KIND=fomp_lock_kind), INTENT(INOUT) :: svar + IF (.FALSE.) PRINT *, svar END SUBROUTINE fomp_unset_lock @@ -373,6 +385,7 @@ SUBROUTINE fomp_unset_nest_lock(nvar) !f2py threadsafe INTEGER(KIND=fomp_nest_lock_kind), INTENT(INOUT) :: nvar + IF (.FALSE.) PRINT *, nvar END SUBROUTINE fomp_unset_nest_lock @@ -387,6 +400,7 @@ FUNCTION fomp_test_lock(svar) INTEGER(KIND=fomp_lock_kind), INTENT(INOUT) :: svar LOGICAL :: fomp_test_lock + IF (.FALSE.) PRINT *, svar fomp_test_lock = .FALSE. @@ -403,6 +417,7 @@ FUNCTION fomp_test_nest_lock(nvar) INTEGER(KIND=fomp_nest_lock_kind), INTENT(INOUT) :: nvar INTEGER :: fomp_test_nest_lock + IF (.FALSE.) PRINT *, nvar fomp_test_nest_lock = -1 diff --git a/fortran/ompgen.F90 b/fortran/ompgen.F90 index ed3d9bd..dea4246 100644 --- a/fortran/ompgen.F90 +++ b/fortran/ompgen.F90 @@ -74,8 +74,11 @@ SUBROUTINE fomp_set_num_threads(num_threads) #ifdef _OPENMP CALL omp_set_num_threads(num_threads) +#else + IF (.FALSE.) PRINT *, num_threads #endif + END SUBROUTINE fomp_set_num_threads @@ -192,6 +195,8 @@ SUBROUTINE fomp_set_dynamic(dynamic_threads) #ifdef _OPENMP CALL omp_set_dynamic(dynamic_threads) +#else + IF (.FALSE.) PRINT *, dynamic_threads #endif END SUBROUTINE fomp_set_dynamic @@ -230,6 +235,8 @@ SUBROUTINE fomp_set_nested(nested) #ifdef _OPENMP CALL omp_set_nested(nested) +#else + IF (.FALSE.) PRINT *, nested #endif END SUBROUTINE fomp_set_nested @@ -270,6 +277,8 @@ SUBROUTINE fomp_set_schedule(kind, modifier) #ifdef _OPENMP CALL omp_set_schedule(kind, modifier) +#else + IF (.FALSE.) PRINT *, kind, modifier #endif END SUBROUTINE fomp_set_schedule @@ -327,14 +336,12 @@ SUBROUTINE fomp_set_max_active_levels(max_levels) !f2py threadsafe -#ifdef _OPENMP - INTEGER, INTENT(IN) :: max_levels -#else INTEGER, INTENT(IN) :: max_levels -#endif #ifdef _OPENMP CALL omp_set_max_active_levels(max_levels) +#else + IF (.FALSE.) PRINT *, max_levels #endif END SUBROUTINE fomp_set_max_active_levels @@ -395,6 +402,7 @@ FUNCTION fomp_get_ancestor_thread_num(level) #ifdef _OPENMP fomp_get_ancestor_thread_num = omp_get_ancestor_thread_num(level) #else + IF (.FALSE.) PRINT *, level fomp_get_ancestor_thread_num = -1 #endif @@ -416,6 +424,7 @@ FUNCTION fomp_get_team_size(level) #ifdef _OPENMP fomp_get_team_size = omp_get_team_size(level) #else + IF (.FALSE.) PRINT *, level fomp_get_team_size = -1 #endif @@ -518,8 +527,11 @@ SUBROUTINE fomp_destroy_lock(svar) #ifdef _OPENMP CALL omp_destroy_lock(svar) +#else + IF (.FALSE.) PRINT *, svar #endif + END SUBROUTINE fomp_destroy_lock @@ -537,6 +549,8 @@ SUBROUTINE fomp_destroy_nest_lock(nvar) #ifdef _OPENMP CALL omp_destroy_nest_lock(nvar) +#else + IF (.FALSE.) PRINT *, nvar #endif END SUBROUTINE fomp_destroy_nest_lock @@ -556,6 +570,8 @@ SUBROUTINE fomp_set_lock(svar) #ifdef _OPENMP CALL omp_set_lock(svar) +#else + IF (.FALSE.) PRINT *, svar #endif END SUBROUTINE fomp_set_lock @@ -575,6 +591,8 @@ SUBROUTINE fomp_set_nest_lock(nvar) #ifdef _OPENMP CALL omp_set_nest_lock(nvar) +#else + IF (.FALSE.) PRINT *, nvar #endif END SUBROUTINE fomp_set_nest_lock @@ -594,6 +612,8 @@ SUBROUTINE fomp_unset_lock(svar) #ifdef _OPENMP CALL omp_unset_lock(svar) +#else + IF (.FALSE.) PRINT *, svar #endif END SUBROUTINE fomp_unset_lock @@ -613,6 +633,8 @@ SUBROUTINE fomp_unset_nest_lock(nvar) #ifdef _OPENMP CALL omp_unset_nest_lock(nvar) +#else + IF (.FALSE.) PRINT *, nvar #endif END SUBROUTINE fomp_unset_nest_lock @@ -634,6 +656,7 @@ FUNCTION fomp_test_lock(svar) #ifdef _OPENMP fomp_test_lock = omp_test_lock(svar) #else + IF (.FALSE.) PRINT *, svar fomp_test_lock = .FALSE. #endif @@ -658,6 +681,7 @@ FUNCTION fomp_test_nest_lock(nvar) #ifdef _OPENMP fomp_test_nest_lock = omp_test_nest_lock(nvar) #else + IF (.FALSE.) PRINT *, nvar fomp_test_nest_lock = -1 #endif diff --git a/fortran/rip_cape.f90 b/fortran/rip_cape.f90 index dfa6f20..dc5b79b 100644 --- a/fortran/rip_cape.f90 +++ b/fortran/rip_cape.f90 @@ -274,7 +274,7 @@ END SUBROUTINE DPFCALC SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& cmsg,mix,mjy,mkzh,ter_follow,& psafile, errstat, errmsg) - USE wrf_constants, ONLY : ALGERR, CELKEL, G, EZERO, ESLCON1, ESLCON2, & + USE wrf_constants, ONLY : CELKEL, G, EZERO, ESLCON1, ESLCON2, & EPS, RD, CP, GAMMA, CPMD, RGASMD, GAMMAMD, TLCLC1, & TLCLC2, TLCLC3, TLCLC4, THTECON1, THTECON2, THTECON3 @@ -312,7 +312,6 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& REAL(KIND=8), DIMENSION(150) :: psadithte, psadiprs REAL(KIND=8), DIMENSION(150,150) :: psaditmk LOGICAL :: elfound - REAL :: t1,t2 REAL(KIND=8), DIMENSION(mkzh,mix,mjy) :: prs_new REAL(KIND=8), DIMENSION(mkzh,mix,mjy) :: tmk_new @@ -325,6 +324,7 @@ SUBROUTINE DCAPECALC3D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& klev = 0 klcl = 0 kel = 0 + IF (.FALSE.) PRINT *,ter ! the comments were taken from a mark stoelinga email, 23 apr 2007, @@ -599,7 +599,7 @@ END SUBROUTINE DCAPECALC3D SUBROUTINE DCAPECALC2D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& cmsg,mix,mjy,mkzh,ter_follow,& psafile, errstat, errmsg) - USE wrf_constants, ONLY : ALGERR, CELKEL, G, EZERO, ESLCON1, ESLCON2, & + USE wrf_constants, ONLY : CELKEL, G, EZERO, ESLCON1, ESLCON2, & EPS, RD, CP, GAMMA, CPMD, RGASMD, GAMMAMD, TLCLC1, & TLCLC2, TLCLC3, TLCLC4, THTECON1, THTECON2, THTECON3 @@ -628,10 +628,10 @@ SUBROUTINE DCAPECALC2D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& ! local variables INTEGER :: i, j, k, ilcl, kel, kk, klcl, klev, klfc, kmax, kpar, kpar1, kpar2 - REAL(KIND=8) :: ethmax, q, p, e, tlcl, zlcl + REAL(KIND=8) :: ethmax, p, e, tlcl, zlcl REAL(KIND=8) :: pavg, tvirtual, p1, p2, pp1, pp2, th, totthe, totqvp, totprs REAL(KIND=8) :: cpm, deltap, ethpari, gammam, qvppari, tmkpari - REAL(KIND=8) :: facden, qvplift, tmklift, tvenv, tvlift, ghtlift, fac1, fac2 + REAL(KIND=8) :: facden, qvplift, tmklift, tvenv, tvlift, ghtlift REAL(KIND=8) :: eslift, tmkenv, qvpenv, tonpsadiabat REAL(KIND=8) :: benamin, dz, pup, pdn REAL(KIND=8), DIMENSION(150) :: buoy, zrel, benaccum @@ -639,7 +639,6 @@ SUBROUTINE DCAPECALC2D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& REAL(KIND=8), DIMENSION(150) :: psadithte, psadiprs REAL(KIND=8), DIMENSION(150,150) :: psaditmk LOGICAL :: elfound - INTEGER :: nthreads REAL(KIND=8), DIMENSION(mkzh) :: eth_temp REAL(KIND=8), DIMENSION(mkzh,mix,mjy) :: prs_new REAL(KIND=8), DIMENSION(mkzh,mix,mjy) :: tmk_new diff --git a/fortran/wrf_cloud_fracf.f90 b/fortran/wrf_cloud_fracf.f90 index 9930206..92ae745 100644 --- a/fortran/wrf_cloud_fracf.f90 +++ b/fortran/wrf_cloud_fracf.f90 @@ -78,7 +78,7 @@ SUBROUTINE DCLOUDFRAC2(vert, rh, vert_inc_w_height, low_thresh, mid_thresh, & ! NCLEND - INTEGER i, j, k, kstart, kend + INTEGER i, j, k INTEGER kchi, kcmi, kclo ! Initialize the output diff --git a/fortran/wrf_user.f90 b/fortran/wrf_user.f90 index 1e133a1..1bfc82c 100644 --- a/fortran/wrf_user.f90 +++ b/fortran/wrf_user.f90 @@ -327,11 +327,12 @@ SUBROUTINE DCOMPUTESEAPRS(nx, ny, nz, z, t, p, q, sea_level_pressure, & INTEGER :: i, j, k INTEGER :: klo, khi - INTEGER :: errcnt, bad_i, bad_j, bad_sfp + INTEGER :: errcnt, bad_i, bad_j + REAL(KIND=8) :: bad_sfp REAL(KIND=8) :: plo, phi, tlo, thi, zlo, zhi REAL(KIND=8) :: p_at_pconst, t_at_pconst, z_at_pconst - REAL(KIND=8) :: z_half_lowest + !REAL(KIND=8) :: z_half_lowest LOGICAL :: l1, l2, l3, found diff --git a/fortran/wrf_vinterp.f90 b/fortran/wrf_vinterp.f90 index 2dc7c17..c34efd7 100644 --- a/fortran/wrf_vinterp.f90 +++ b/fortran/wrf_vinterp.f90 @@ -169,7 +169,6 @@ SUBROUTINE wrf_vintrp(datain, dataout, pres, tk, qvp, ght, terrain,& REAL(KIND=8) :: pbot, zbot, tbotextrap, e REAL(KIND=8) :: tlcl, gammam CHARACTER(LEN=1) :: cvcord - INTEGER :: thd ! Removes the warnings for uninitialized variables cvcord = '' From e22de8f4a4ccdd71d2069a7c5e4a3b1c0f545747 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Fri, 15 Dec 2017 16:07:19 -0700 Subject: [PATCH 32/43] Updated ignore file to ignore the pyf and default omp.f90 files, along with other junk --- .gitignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.gitignore b/.gitignore index 868dff8..042b9bd 100644 --- a/.gitignore +++ b/.gitignore @@ -46,4 +46,10 @@ doc/build doc/source/internal_api/generated doc/source/user_api/generated test/ipynb/.ipynb_checkpoints +fortran/omp.f90 +fortran/wrffortran.pyf +fortran/*.mod +build +.settings +src/wrf_python.egg-info From 8b21b10860c7146e43ac6328f2d6f66095598dc4 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Mon, 18 Dec 2017 13:55:52 -0700 Subject: [PATCH 33/43] Renamed the internal 'get_' modules to have their names start with 'g_' to avoid naming issues with the raw computational routines. Closes #33. Closes #32. --- doc/source/internal_api/index.rst | 80 ++++++++++++------------ fortran/rip_cape.f90 | 2 +- src/wrf/api.py | 2 +- src/wrf/{cape.py => g_cape.py} | 0 src/wrf/{cloudfrac.py => g_cloudfrac.py} | 2 +- src/wrf/{ctt.py => g_ctt.py} | 0 src/wrf/{dbz.py => g_dbz.py} | 0 src/wrf/{dewpoint.py => g_dewpoint.py} | 0 src/wrf/{geoht.py => g_geoht.py} | 0 src/wrf/{helicity.py => g_helicity.py} | 0 src/wrf/{latlon.py => g_latlon.py} | 0 src/wrf/{omega.py => g_omega.py} | 0 src/wrf/{precip.py => g_precip.py} | 0 src/wrf/{pressure.py => g_pressure.py} | 0 src/wrf/{pw.py => g_pw.py} | 0 src/wrf/{rh.py => g_rh.py} | 0 src/wrf/{slp.py => g_slp.py} | 0 src/wrf/{temp.py => g_temp.py} | 0 src/wrf/{terrain.py => g_terrain.py} | 0 src/wrf/{times.py => g_times.py} | 0 src/wrf/{uvmet.py => g_uvmet.py} | 2 +- src/wrf/{vorticity.py => g_vorticity.py} | 0 src/wrf/{wind.py => g_wind.py} | 0 src/wrf/interp.py | 8 +-- src/wrf/routines.py | 38 +++++------ 25 files changed, 67 insertions(+), 67 deletions(-) rename src/wrf/{cape.py => g_cape.py} (100%) rename src/wrf/{cloudfrac.py => g_cloudfrac.py} (99%) rename src/wrf/{ctt.py => g_ctt.py} (100%) rename src/wrf/{dbz.py => g_dbz.py} (100%) rename src/wrf/{dewpoint.py => g_dewpoint.py} (100%) rename src/wrf/{geoht.py => g_geoht.py} (100%) rename src/wrf/{helicity.py => g_helicity.py} (100%) rename src/wrf/{latlon.py => g_latlon.py} (100%) rename src/wrf/{omega.py => g_omega.py} (100%) rename src/wrf/{precip.py => g_precip.py} (100%) rename src/wrf/{pressure.py => g_pressure.py} (100%) rename src/wrf/{pw.py => g_pw.py} (100%) rename src/wrf/{rh.py => g_rh.py} (100%) rename src/wrf/{slp.py => g_slp.py} (100%) rename src/wrf/{temp.py => g_temp.py} (100%) rename src/wrf/{terrain.py => g_terrain.py} (100%) rename src/wrf/{times.py => g_times.py} (100%) rename src/wrf/{uvmet.py => g_uvmet.py} (99%) rename src/wrf/{vorticity.py => g_vorticity.py} (100%) rename src/wrf/{wind.py => g_wind.py} (100%) diff --git a/doc/source/internal_api/index.rst b/doc/source/internal_api/index.rst index 11fa312..a41775b 100644 --- a/doc/source/internal_api/index.rst +++ b/doc/source/internal_api/index.rst @@ -13,46 +13,46 @@ The routines below are called internally by :meth:`wrf.getvar`. :nosignatures: :toctree: ./generated/ - wrf.cape.get_2dcape - wrf.cape.get_3dcape - wrf.cloudfrac.get_cloudfrac - wrf.ctt.get_ctt - wrf.dbz.get_dbz - wrf.dbz.get_max_dbz - wrf.dewpoint.get_dp - wrf.dewpoint.get_dp_2m - wrf.geoht.get_geopt - wrf.geoht.get_height - wrf.helicity.get_srh - wrf.helicity.get_uh - wrf.omega.get_omega - wrf.pressure.get_pressure - wrf.pressure.get_pressure_hpa - wrf.pw.get_pw - wrf.rh.get_rh - wrf.rh.get_rh_2m - wrf.slp.get_slp - wrf.temp.get_theta - wrf.temp.get_temp - wrf.temp.get_eth - wrf.temp.get_tv - wrf.temp.get_tw - wrf.temp.get_tk - wrf.temp.get_tc - wrf.terrain.get_terrain - wrf.times.get_times - wrf.times.get_xtimes - wrf.uvmet.get_uvmet - wrf.uvmet.get_uvmet10 - wrf.uvmet.get_uvmet_wspd_wdir - wrf.uvmet.get_uvmet10_wspd_wdir - wrf.vorticity.get_avo - wrf.vorticity.get_pvo - wrf.wind.get_u_destag - wrf.wind.get_v_destag - wrf.wind.get_w_destag - wrf.wind.get_destag_wspd_wdir - wrf.wind.get_destag_wspd_wdir10 + wrf.g_cape.get_2dcape + wrf.g_cape.get_3dcape + wrf.g_cloudfrac.get_cloudfrac + wrf.g_ctt.get_ctt + wrf.g_dbz.get_dbz + wrf.g_dbz.get_max_dbz + wrf.g_dewpoint.get_dp + wrf.g_dewpoint.get_dp_2m + wrf.g_geoht.get_geopt + wrf.g_geoht.get_height + wrf.g_helicity.get_srh + wrf.g_helicity.get_uh + wrf.g_omega.get_omega + wrf.g_pressure.get_pressure + wrf.g_pressure.get_pressure_hpa + wrf.g_pw.get_pw + wrf.g_rh.get_rh + wrf.g_rh.get_rh_2m + wrf.g_slp.get_slp + wrf.g_temp.get_theta + wrf.g_temp.get_temp + wrf.g_temp.get_eth + wrf.g_temp.get_tv + wrf.g_temp.get_tw + wrf.g_temp.get_tk + wrf.g_temp.get_tc + wrf.g_terrain.get_terrain + wrf.g_times.get_times + wrf.g_times.get_xtimes + wrf.g_uvmet.get_uvmet + wrf.g_uvmet.get_uvmet10 + wrf.g_uvmet.get_uvmet_wspd_wdir + wrf.g_uvmet.get_uvmet10_wspd_wdir + wrf.g_vorticity.get_avo + wrf.g_vorticity.get_pvo + wrf.g_wind.get_u_destag + wrf.g_wind.get_v_destag + wrf.g_wind.get_w_destag + wrf.g_wind.get_destag_wspd_wdir + wrf.g_wind.get_destag_wspd_wdir10 ------------------------- diff --git a/fortran/rip_cape.f90 b/fortran/rip_cape.f90 index dc5b79b..be05b79 100644 --- a/fortran/rip_cape.f90 +++ b/fortran/rip_cape.f90 @@ -710,7 +710,7 @@ SUBROUTINE DCAPECALC2D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& !$OMP facden, tmkenv, qvpenv, eslift, qvplift, buoy, benamin, & !$OMP benaccum, zrel, kmax, dz, elfound, & !$OMP kel, klfc, pavg, p2, p1, totthe, totqvp, totprs, & - !$OMP i, j, k, kpar, kpar1, kpar2, qvppari, tmkpari,p, pup, pdn, q, th, & + !$OMP i, j, k, kpar, kpar1, kpar2, qvppari, tmkpari,p, pup, pdn, th, & !$OMP pp1, pp2, ethmax, eth_temp, klev) SCHEDULE(runtime) DO j = 1,mjy DO i = 1,mix diff --git a/src/wrf/api.py b/src/wrf/api.py index 9333b2d..0542972 100644 --- a/src/wrf/api.py +++ b/src/wrf/api.py @@ -29,7 +29,7 @@ from .extension import (DiagnosticError, omp_set_num_threads, omp_test_lock, omp_test_nest_lock, omp_get_wtime, omp_get_wtick) from .interp import (interplevel, vertcross, interpline, vinterp) -from .latlon import (xy_to_ll, ll_to_xy, xy_to_ll_proj, ll_to_xy_proj) +from .g_latlon import (xy_to_ll, ll_to_xy, xy_to_ll_proj, ll_to_xy_proj) from .py3compat import (viewitems, viewkeys, viewvalues, isstr, py2round, py3range, ucode) from .util import (to_np, extract_global_attrs, is_standard_wrf_var, diff --git a/src/wrf/cape.py b/src/wrf/g_cape.py similarity index 100% rename from src/wrf/cape.py rename to src/wrf/g_cape.py diff --git a/src/wrf/cloudfrac.py b/src/wrf/g_cloudfrac.py similarity index 99% rename from src/wrf/cloudfrac.py rename to src/wrf/g_cloudfrac.py index f093146..76c4356 100644 --- a/src/wrf/cloudfrac.py +++ b/src/wrf/g_cloudfrac.py @@ -8,7 +8,7 @@ from .constants import Constants, default_fill from .extension import _tk, _rh, _cloudfrac from .metadecorators import set_cloudfrac_metadata from .util import extract_vars -from .geoht import _get_geoht +from .g_geoht import _get_geoht @set_cloudfrac_metadata() diff --git a/src/wrf/ctt.py b/src/wrf/g_ctt.py similarity index 100% rename from src/wrf/ctt.py rename to src/wrf/g_ctt.py diff --git a/src/wrf/dbz.py b/src/wrf/g_dbz.py similarity index 100% rename from src/wrf/dbz.py rename to src/wrf/g_dbz.py diff --git a/src/wrf/dewpoint.py b/src/wrf/g_dewpoint.py similarity index 100% rename from src/wrf/dewpoint.py rename to src/wrf/g_dewpoint.py diff --git a/src/wrf/geoht.py b/src/wrf/g_geoht.py similarity index 100% rename from src/wrf/geoht.py rename to src/wrf/g_geoht.py diff --git a/src/wrf/helicity.py b/src/wrf/g_helicity.py similarity index 100% rename from src/wrf/helicity.py rename to src/wrf/g_helicity.py diff --git a/src/wrf/latlon.py b/src/wrf/g_latlon.py similarity index 100% rename from src/wrf/latlon.py rename to src/wrf/g_latlon.py diff --git a/src/wrf/omega.py b/src/wrf/g_omega.py similarity index 100% rename from src/wrf/omega.py rename to src/wrf/g_omega.py diff --git a/src/wrf/precip.py b/src/wrf/g_precip.py similarity index 100% rename from src/wrf/precip.py rename to src/wrf/g_precip.py diff --git a/src/wrf/pressure.py b/src/wrf/g_pressure.py similarity index 100% rename from src/wrf/pressure.py rename to src/wrf/g_pressure.py diff --git a/src/wrf/pw.py b/src/wrf/g_pw.py similarity index 100% rename from src/wrf/pw.py rename to src/wrf/g_pw.py diff --git a/src/wrf/rh.py b/src/wrf/g_rh.py similarity index 100% rename from src/wrf/rh.py rename to src/wrf/g_rh.py diff --git a/src/wrf/slp.py b/src/wrf/g_slp.py similarity index 100% rename from src/wrf/slp.py rename to src/wrf/g_slp.py diff --git a/src/wrf/temp.py b/src/wrf/g_temp.py similarity index 100% rename from src/wrf/temp.py rename to src/wrf/g_temp.py diff --git a/src/wrf/terrain.py b/src/wrf/g_terrain.py similarity index 100% rename from src/wrf/terrain.py rename to src/wrf/g_terrain.py diff --git a/src/wrf/times.py b/src/wrf/g_times.py similarity index 100% rename from src/wrf/times.py rename to src/wrf/g_times.py diff --git a/src/wrf/uvmet.py b/src/wrf/g_uvmet.py similarity index 99% rename from src/wrf/uvmet.py rename to src/wrf/g_uvmet.py index 6dba5e6..5ef19a3 100755 --- a/src/wrf/uvmet.py +++ b/src/wrf/g_uvmet.py @@ -9,7 +9,7 @@ import numpy as np from .extension import _uvmet from .destag import destagger from .constants import Constants -from .wind import _calc_wspd_wdir +from .g_wind import _calc_wspd_wdir from .decorators import convert_units from .metadecorators import set_wind_metadata from .util import extract_vars, extract_global_attrs, either diff --git a/src/wrf/vorticity.py b/src/wrf/g_vorticity.py similarity index 100% rename from src/wrf/vorticity.py rename to src/wrf/g_vorticity.py diff --git a/src/wrf/wind.py b/src/wrf/g_wind.py similarity index 100% rename from src/wrf/wind.py rename to src/wrf/g_wind.py diff --git a/src/wrf/interp.py b/src/wrf/interp.py index bf9e34b..403abd6 100755 --- a/src/wrf/interp.py +++ b/src/wrf/interp.py @@ -12,10 +12,10 @@ from .util import extract_vars, is_staggered, get_id, to_np from .py3compat import py3range from .interputils import get_xy, get_xy_z_params, to_xy_coords from .constants import Constants, default_fill, ConversionFactors -from .terrain import get_terrain -from .geoht import get_height -from .temp import get_theta, get_temp, get_eth -from .pressure import get_pressure +from wrf.g_terrain import get_terrain +from wrf.g_geoht import get_height +from wrf.g_temp import get_theta, get_temp, get_eth +from wrf.g_pressure import get_pressure # Note: Extension decorator is good enough to handle left dims diff --git a/src/wrf/routines.py b/src/wrf/routines.py index 4aed3d8..3267598 100644 --- a/src/wrf/routines.py +++ b/src/wrf/routines.py @@ -3,27 +3,27 @@ from __future__ import (absolute_import, division, print_function, from .util import (get_iterable, is_standard_wrf_var, extract_vars, viewkeys, get_id) -from .cape import get_2dcape, get_3dcape -from .ctt import get_ctt -from .dbz import get_dbz, get_max_dbz -from .dewpoint import get_dp, get_dp_2m -from .geoht import get_geopt, get_height -from .helicity import get_srh, get_uh -from .latlon import get_lat, get_lon -from .omega import get_omega -from .pressure import get_pressure, get_pressure_hpa -from .pw import get_pw -from .rh import get_rh, get_rh_2m -from .slp import get_slp -from .temp import get_tc, get_eth, get_temp, get_theta, get_tk, get_tv, get_tw -from .terrain import get_terrain -from .uvmet import (get_uvmet, get_uvmet10, get_uvmet10_wspd_wdir, +from .g_cape import get_2dcape, get_3dcape +from .g_ctt import get_ctt +from .g_dbz import get_dbz, get_max_dbz +from .g_dewpoint import get_dp, get_dp_2m +from .g_geoht import get_geopt, get_height +from .g_helicity import get_srh, get_uh +from .g_latlon import get_lat, get_lon +from .g_omega import get_omega +from .g_pressure import get_pressure, get_pressure_hpa +from .g_pw import get_pw +from .g_rh import get_rh, get_rh_2m +from .g_slp import get_slp +from .g_temp import get_tc, get_eth, get_temp, get_theta, get_tk, get_tv, get_tw +from .g_terrain import get_terrain +from .g_uvmet import (get_uvmet, get_uvmet10, get_uvmet10_wspd_wdir, get_uvmet_wspd_wdir) -from .vorticity import get_avo, get_pvo -from .wind import (get_destag_wspd_wdir, get_destag_wspd_wdir10, +from .g_vorticity import get_avo, get_pvo +from .g_wind import (get_destag_wspd_wdir, get_destag_wspd_wdir10, get_u_destag, get_v_destag, get_w_destag) -from .times import get_times -from .cloudfrac import get_cloudfrac +from .g_times import get_times +from .g_cloudfrac import get_cloudfrac # func is the function to call. kargs are required arguments that should From 1cc072b2a9b87734eed43f6ea08084bfbcde8fd6 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Mon, 18 Dec 2017 16:38:58 -0700 Subject: [PATCH 34/43] Changed default cfrac to use height_agl as the type. Since NCL never advertised this function, and it never really worked correctly before, breaking backwards compatibility is less of an issue. --- doc/source/_templates/product_table.txt | 482 ++++++++++++------------ src/wrf/g_cloudfrac.py | 20 +- test/utests.py | 13 + 3 files changed, 265 insertions(+), 250 deletions(-) diff --git a/doc/source/_templates/product_table.txt b/doc/source/_templates/product_table.txt index 8e92724..8dfb388 100644 --- a/doc/source/_templates/product_table.txt +++ b/doc/source/_templates/product_table.txt @@ -1,240 +1,244 @@ -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| Variable Name | Description | Available Units | Additional Keyword Arguments | -+====================+===============================================================+=============================+===============================================================================================+ -| avo | Absolute Vorticity | 10-5 s-1 | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| eth/theta_e | Equivalent Potential Temperature | K | **units** (str) : Set to desired units. Default is *'K'*. | -| | | | | -| | | degC | | -| | | | | -| | | degF | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| cape_2d | 2D cape (mcape/mcin/lcl/lfc) | J kg-1 ; J kg-1 ; m ; m | **missing** (float): Fill value for output only | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| cape_3d | 3D cape and cin | J kg-1 | **missing** (float): Fill value for output only | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| ctt | Cloud Top Temperature | degC | **units** (str) : Set to desired units. Default is *'degC'*. | -| | | | | -| | | K | | -| | | | | -| | | degF | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| cloudfrac | Cloud Fraction | % | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| dbz | Reflectivity | dBZ | **do_variant** (boolean): Set to True to enable variant calculation. Default is *False*. | -| | | | | -| | | | **do_liqskin** (boolean): Set to True to enable liquid skin calculation. Default is *False*. | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| mdbz | Maximum Reflectivity | dBZ | **do_variant** (boolean): Set to True to enable variant calculation. Default is *False*. | -| | | | | -| | | | **do_liqskin** (boolean): Set to True to enable liquid skin calculation. Default is *False*. | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| geopt/geopotential | Full Model Geopotential | m2 s-2 | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| helicity | Storm Relative Helicity | m2 s-2 | **top** (float): The top level for the calculation in meters. Default is *3000.0*. | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| lat | Latitude | decimal degrees | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| lon | Longitude | decimal degrees | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| omg/omega | Omega | Pa s-1 | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| p/pres | Full Model Pressure | Pa | **units** (str) : Set to desired units. Default is *'Pa'*. | -| | | | | -| | (in specified units) | hPa | | -| | | | | -| | | mb | | -| | | | | -| | | torr | | -| | | | | -| | | mmhg | | -| | | | | -| | | atm | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| pressure | Full Model Pressure (hPa) | hPa | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| pvo | Potential Vorticity | PVU | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| pw | Precipitable Water | kg m-2 | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| rh | Relative Humidity | % | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| rh2 | 2m Relative Humidity | % | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| slp | Sea Level Pressure | hPa | **units** (str) : Set to desired units. Default is *'hPa'*. | -| | | | | -| | | hPa | | -| | | | | -| | | mb | | -| | | | | -| | | torr | | -| | | | | -| | | mmhg | | -| | | | | -| | | atm | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| ter | Model Terrain Height | m | **units** (str) : Set to desired units. Default is *'m'*. | -| | | | | -| | | km | | -| | | | | -| | | dm | | -| | | | | -| | | ft | | -| | | | | -| | | mi | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| td2 | 2m Dew Point Temperature | degC | **units** (str) : Set to desired units. Default is *'degC'*. | -| | | | | -| | | K | | -| | | | | -| | | degF | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| td | Dew Point Temperature | degC | **units** (str) : Set to desired units. Default is *'degC'*. | -| | | | | -| | | K | | -| | | | | -| | | degF | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| tc | Temperature in Celsius | degC | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| th/theta | Potential Temperature | K | **units** (str) : Set to desired units. Default is *'K'*. | -| | | | | -| | | degC | | -| | | | | -| | | degF | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| temp | Temperature (in specified units) | K | **units** (str) : Set to desired units. Default is *'K'*. | -| | | | | -| | | degC | | -| | | | | -| | | degF | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| tk | Temperature in Kelvin | K | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| times | Times in the File or Sequence | | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| xtimes | XTIME Coordinate | minutes since | | -| | | | | -| | (if applicable) | start of | | -| | | | | -| | | model run | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| tv | Virtual Temperature | K | **units** (str) : Set to desired units. Default is *'K'*. | -| | | | | -| | | degC | | -| | | | | -| | | degF | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| twb | Wet Bulb Temperature | K | **units** (str) : Set to desired units. Default is *'K'*. | -| | | | | -| | | degC | | -| | | | | -| | | degF | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| updraft_helicity | Updraft Helicity | m2 s-2 | **bottom** (float): The bottom level for the calculation in meters. Default is *2000.0*. | -| | | | | -| | | | **top** (float): The top level for the calculation in meters. Default is *5000.0*. | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| ua | U-component of Wind on Mass Points | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | -| | | | | -| | | km h-1 | | -| | | | | -| | | mi h-1 | | -| | | | | -| | | kt | | -| | | | | -| | | ft s-1 | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| va | V-component of Wind on Mass Points | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | -| | | | | -| | | km h-1 | | -| | | | | -| | | mi h-1 | | -| | | | | -| | | kt | | -| | | | | -| | | ft s-1 | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| wa | W-component of Wind on Mass Points | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | -| | | | | -| | | km h-1 | | -| | | | | -| | | mi h-1 | | -| | | | | -| | | kt | | -| | | | | -| | | ft s-1 | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| uvmet10 | 10 m U and V Components of Wind | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | -| | | | | -| | Rotated to Earth Coordinates | km h-1 | | -| | | | | -| | | mi h-1 | | -| | | | | -| | | kt | | -| | | | | -| | | ft s-1 | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| uvmet | U and V Components of Wind | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | -| | | | | -| | Rotated to Earth Coordinates | km h-1 | | -| | | | | -| | | mi h-1 | | -| | | | | -| | | kt | | -| | | | | -| | | ft s-1 | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| wspd_wdir | Wind Speed and Direction (wind_from_direction) | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | -| | | | | -| | in Grid Coordinates | km h-1 | | -| | | | | -| | | mi h-1 | | -| | | | | -| | | kt | | -| | | | | -| | | ft s-1 | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| wspd_wdir10 | 10m Wind Speed and Direction (wind_from_direction) | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | -| | | | | -| | in Grid Coordinates | km h-1 | | -| | | | | -| | | mi h-1 | | -| | | | | -| | | kt | | -| | | | | -| | | ft s-1 | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| uvmet_wspd_wdir | Wind Speed and Direction (wind_from_direction) | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | -| | | | | -| | Rotated to Earth Coordinates | km h-1 | | -| | | | | -| | | mi h-1 | | -| | | | | -| | | kt | | -| | | | | -| | | ft s-1 | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| uvmet10_wspd_wdir | 10m Wind Speed and Direction (wind_from_direction) | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | -| | | | | -| | Rotated to Earth Coordinates | km h-1 | | -| | | | | -| | | mi h-1 | | -| | | | | -| | | kt | | -| | | | | -| | | ft s-1 | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ -| z/height | Full Model Height on Mass Levels | m | **msl** (boolean): Set to False to return AGL values. True is for MSL. Default is *True*. | -| | | | | -| | | km | **units** (str) : Set to desired units. Default is *'m'*. | -| | | | | -| | | dm | | -| | | | | -| | | ft | | -| | | | | -| | | mi | | -+--------------------+---------------------------------------------------------------+-----------------------------+-----------------------------------------------------------------------------------------------+ - - ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| Variable Name | Description | Available Units | Additional Keyword Arguments | ++====================+===============================================================+=============================+=========================================================================================================================================================+ +| avo | Absolute Vorticity | 10-5 s-1 | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| eth/theta_e | Equivalent Potential Temperature | K | **units** (str) : Set to desired units. Default is *'K'*. | +| | | | | +| | | degC | | +| | | | | +| | | degF | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| cape_2d | 2D cape (mcape/mcin/lcl/lfc) | J kg-1 ; J kg-1 ; m ; m | **missing** (float): Fill value for output only | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| cape_3d | 3D cape and cin | J kg-1 | **missing** (float): Fill value for output only | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ctt | Cloud Top Temperature | degC | **units** (str) : Set to desired units. Default is *'degC'*. | +| | | | | +| | | K | | +| | | | | +| | | degF | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| cloudfrac | Cloud Fraction | % | **vert_type** (str): The vertical coordinate type for the cloud thresholds. Must be 'height_agl', 'height_msl', or 'pres'. Default is 'height_agl'. | +| | | | | +| | | | **low_thresh** (float): The low cloud threshold (meters for 'height_agl' and 'height_msl', pascals for 'pres'). Default is 300 m (97000 Pa) | +| | | | | +| | | | **mid_thresh** (float): The mid cloud threshold (meters for 'height_agl' and 'height_msl', pascals for 'pres'). Default is 2000 m (80000 Pa) | +| | | | | +| | | | **high_thresh** (float): The high cloud threshold (meters for 'height_agl' and 'height_msl', pascals for 'pres'). Default is 6000 m (45000 Pa) | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| dbz | Reflectivity | dBZ | **do_variant** (boolean): Set to True to enable variant calculation. Default is *False*. | +| | | | | +| | | | **do_liqskin** (boolean): Set to True to enable liquid skin calculation. Default is *False*. | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| mdbz | Maximum Reflectivity | dBZ | **do_variant** (boolean): Set to True to enable variant calculation. Default is *False*. | +| | | | | +| | | | **do_liqskin** (boolean): Set to True to enable liquid skin calculation. Default is *False*. | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| geopt/geopotential | Full Model Geopotential | m2 s-2 | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| helicity | Storm Relative Helicity | m2 s-2 | **top** (float): The top level for the calculation in meters. Default is *3000.0*. | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| lat | Latitude | decimal degrees | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| lon | Longitude | decimal degrees | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| omg/omega | Omega | Pa s-1 | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| p/pres | Full Model Pressure | Pa | **units** (str) : Set to desired units. Default is *'Pa'*. | +| | | | | +| | (in specified units) | hPa | | +| | | | | +| | | mb | | +| | | | | +| | | torr | | +| | | | | +| | | mmhg | | +| | | | | +| | | atm | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| pressure | Full Model Pressure (hPa) | hPa | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| pvo | Potential Vorticity | PVU | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| pw | Precipitable Water | kg m-2 | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| rh | Relative Humidity | % | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| rh2 | 2m Relative Humidity | % | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| slp | Sea Level Pressure | hPa | **units** (str) : Set to desired units. Default is *'hPa'*. | +| | | | | +| | | hPa | | +| | | | | +| | | mb | | +| | | | | +| | | torr | | +| | | | | +| | | mmhg | | +| | | | | +| | | atm | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ter | Model Terrain Height | m | **units** (str) : Set to desired units. Default is *'m'*. | +| | | | | +| | | km | | +| | | | | +| | | dm | | +| | | | | +| | | ft | | +| | | | | +| | | mi | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| td2 | 2m Dew Point Temperature | degC | **units** (str) : Set to desired units. Default is *'degC'*. | +| | | | | +| | | K | | +| | | | | +| | | degF | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| td | Dew Point Temperature | degC | **units** (str) : Set to desired units. Default is *'degC'*. | +| | | | | +| | | K | | +| | | | | +| | | degF | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| tc | Temperature in Celsius | degC | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| th/theta | Potential Temperature | K | **units** (str) : Set to desired units. Default is *'K'*. | +| | | | | +| | | degC | | +| | | | | +| | | degF | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| temp | Temperature (in specified units) | K | **units** (str) : Set to desired units. Default is *'K'*. | +| | | | | +| | | degC | | +| | | | | +| | | degF | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| tk | Temperature in Kelvin | K | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| times | Times in the File or Sequence | | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| xtimes | XTIME Coordinate | minutes since | | +| | | | | +| | (if applicable) | start of | | +| | | | | +| | | model run | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| tv | Virtual Temperature | K | **units** (str) : Set to desired units. Default is *'K'*. | +| | | | | +| | | degC | | +| | | | | +| | | degF | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| twb | Wet Bulb Temperature | K | **units** (str) : Set to desired units. Default is *'K'*. | +| | | | | +| | | degC | | +| | | | | +| | | degF | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| updraft_helicity | Updraft Helicity | m2 s-2 | **bottom** (float): The bottom level for the calculation in meters. Default is *2000.0*. | +| | | | | +| | | | **top** (float): The top level for the calculation in meters. Default is *5000.0*. | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| ua | U-component of Wind on Mass Points | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | +| | | | | +| | | km h-1 | | +| | | | | +| | | mi h-1 | | +| | | | | +| | | kt | | +| | | | | +| | | ft s-1 | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| va | V-component of Wind on Mass Points | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | +| | | | | +| | | km h-1 | | +| | | | | +| | | mi h-1 | | +| | | | | +| | | kt | | +| | | | | +| | | ft s-1 | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| wa | W-component of Wind on Mass Points | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | +| | | | | +| | | km h-1 | | +| | | | | +| | | mi h-1 | | +| | | | | +| | | kt | | +| | | | | +| | | ft s-1 | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| uvmet10 | 10 m U and V Components of Wind | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | +| | | | | +| | Rotated to Earth Coordinates | km h-1 | | +| | | | | +| | | mi h-1 | | +| | | | | +| | | kt | | +| | | | | +| | | ft s-1 | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| uvmet | U and V Components of Wind | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | +| | | | | +| | Rotated to Earth Coordinates | km h-1 | | +| | | | | +| | | mi h-1 | | +| | | | | +| | | kt | | +| | | | | +| | | ft s-1 | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| wspd_wdir | Wind Speed and Direction (wind_from_direction) | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | +| | | | | +| | in Grid Coordinates | km h-1 | | +| | | | | +| | | mi h-1 | | +| | | | | +| | | kt | | +| | | | | +| | | ft s-1 | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| wspd_wdir10 | 10m Wind Speed and Direction (wind_from_direction) | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | +| | | | | +| | in Grid Coordinates | km h-1 | | +| | | | | +| | | mi h-1 | | +| | | | | +| | | kt | | +| | | | | +| | | ft s-1 | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| uvmet_wspd_wdir | Wind Speed and Direction (wind_from_direction) | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | +| | | | | +| | Rotated to Earth Coordinates | km h-1 | | +| | | | | +| | | mi h-1 | | +| | | | | +| | | kt | | +| | | | | +| | | ft s-1 | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| uvmet10_wspd_wdir | 10m Wind Speed and Direction (wind_from_direction) | m s-1 | **units** (str) : Set to desired units. Default is *'m s-1'*. | +| | | | | +| | Rotated to Earth Coordinates | km h-1 | | +| | | | | +| | | mi h-1 | | +| | | | | +| | | kt | | +| | | | | +| | | ft s-1 | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| z/height | Full Model Height on Mass Levels | m | **msl** (boolean): Set to False to return AGL values. True is for MSL. Default is *True*. | +| | | | | +| | | km | **units** (str) : Set to desired units. Default is *'m'*. | +| | | | | +| | | dm | | +| | | | | +| | | ft | | +| | | | | +| | | mi | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ diff --git a/src/wrf/g_cloudfrac.py b/src/wrf/g_cloudfrac.py index 76c4356..10123e4 100644 --- a/src/wrf/g_cloudfrac.py +++ b/src/wrf/g_cloudfrac.py @@ -14,7 +14,7 @@ from .g_geoht import _get_geoht @set_cloudfrac_metadata() def get_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, - vert_type="pres", low_thresh=None, mid_thresh=None, + vert_type="height_agl", low_thresh=None, mid_thresh=None, high_thresh=None, missing=default_fill(np.float64)): """Return the cloud fraction for low, mid, and high level clouds. @@ -24,13 +24,6 @@ def get_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True, - return_val[0,...] will contain LOW level cloud fraction - return_val[1,...] will contain MID level cloud fraction - return_val[2,...] will contain HIGH level cloud fraction - - For backwards compatibility, the default vertical coordinate type is - pressure, with default cloud levels defined as: - - 97000 Pa <= low_cloud < 80000 Pa - 80000 Pa <= mid_cloud < 45000 Pa - 45000 Pa <= high_cloud If the vertical coordinate type is 'height_agl' or 'height_msl', the default cloud levels are defined as: @@ -38,6 +31,12 @@ def get_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True, 300 m <= low_cloud < 2000 m 2000 m <= mid_cloud < 6000 m 6000 m <= high_cloud + + For 'pres', the default cloud levels are defined as: + + 97000 Pa <= low_cloud < 80000 Pa + 80000 Pa <= mid_cloud < 45000 Pa + 45000 Pa <= high_cloud Note that the default low cloud levels are chosen to exclude clouds near the surface (fog). If you want fog included, set @@ -95,9 +94,8 @@ def get_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True, purposes only. Default is None. vert_type (:obj:`str`, optional): The type of vertical coordinate used - to determine cloud type thresholds. Must be 'pres', 'height_msl', - or 'height_agl'. For backwards compatibility, the default - is 'pres'. + to determine cloud type thresholds. Must be 'height_agl', + 'height_msl', or 'pres'. The default is 'height_agl'. low_thresh (:obj:`float`, optional): The lower bound for what is considered a low cloud. If *vert_type* is 'pres', the default is diff --git a/test/utests.py b/test/utests.py index 51f0a97..5f3ede9 100644 --- a/test/utests.py +++ b/test/utests.py @@ -138,6 +138,13 @@ def make_test(varname, wrf_in, referent, multi=False, repeat=3, pynio=False): tol = 1/100. atol = .1 # Note: NCL uses 273.16 as conversion for some reason nt.assert_allclose(to_np(my_vals), ref_vals, tol, atol) + elif (varname == "cfrac"): + # Change the vert_type to height_agl when NCL gets updated. + my_vals = getvar(in_wrfnc, "cfrac", timeidx=timeidx, + vert_type="pres") + tol = 1/100. + atol = .1 # Note: NCL uses 273.16 as conversion for some reason + nt.assert_allclose(to_np(my_vals), ref_vals, tol, atol) elif (varname == "pw"): my_vals = getvar(in_wrfnc, "pw", timeidx=timeidx) tol = .5/100.0 @@ -611,6 +618,12 @@ class WRFLatLonTest(ut.TestCase): if __name__ == "__main__": + from wrf import (omp_set_num_threads, omp_set_schedule, omp_get_schedule, + omp_set_dynamic, Constants) + omp_set_num_threads(6) + omp_set_schedule(Constants.OMP_SCHED_STATIC, 0) + omp_set_dynamic(False) + ignore_vars = [] # Not testable yet wrf_vars = ["avo", "eth", "cape_2d", "cape_3d", "ctt", "dbz", "mdbz", "geopt", "helicity", "lat", "lon", "omg", "p", "pressure", From aa683a54cd2e3c19a9213a6dbf693d5d1c88c612 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Mon, 18 Dec 2017 16:39:55 -0700 Subject: [PATCH 35/43] Updated documentation for cfrac --- src/wrf/computation.py | 29 +++++------------------------ 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/src/wrf/computation.py b/src/wrf/computation.py index bce2c75..3973709 100644 --- a/src/wrf/computation.py +++ b/src/wrf/computation.py @@ -973,33 +973,14 @@ def cloudfrac(vert, relh, vert_inc_w_height, low_thresh, mid_thresh, - return_val[0,...] will contain LOW level cloud fraction - return_val[1,...] will contain MID level cloud fraction - return_val[2,...] will contain HIGH level cloud fraction - - For backwards compatibility, the default vertical coordinate type is - pressure, with default cloud levels defined as: - - 97000 Pa <= low_cloud < 80000 Pa - 80000 Pa <= mid_cloud < 45000 Pa - 45000 Pa <= high_cloud - - If the vertical coordinate type is 'height_agl' or 'height_msl', the - default cloud levels are defined as: - 300 m <= low_cloud < 2000 m - 2000 m <= mid_cloud < 6000 m - 6000 m <= high_cloud - - Note that the default low cloud levels are chosen to - exclude clouds near the surface (fog). If you want fog included, set - *low_thresh* to ~99500 Pa if *vert_type* is set to 'pres', or 15 m if using - 'height_msl' or 'height_agl'. Keep in mind that the lowest mass grid points - are slightly above the ground, and in order to find clouds, the - *low_thresh* needs to be set to values that are slightly greater than - (less than) the lowest height (pressure) values. + The *low_thresh*, *mid_thresh*, and *high_threshold* paramters specify the + low, mid, and high cloud levels in the same units as *vert*. - When using 'pres' or 'height_agl' for *vert_type*, there is a possibility + In mountainous regions, there is a possibility that the lowest WRF level will be higher than the low_cloud or mid_cloud - threshold, particularly for mountainous regions. When this happens, a - fill value will be used in the output. + threshold. When this happens, a fill value will be used in the output at + that location. This is the raw computational algorithm and does not extract any variables from WRF output files. Use :meth:`wrf.getvar` to both extract and compute From 23520ac34fc7f7d98ce8a7495566b0a8245529f3 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Tue, 19 Dec 2017 15:01:45 -0700 Subject: [PATCH 36/43] Now throws a warning if latlon is set to True, but there is no coordinate metadata for vertcross and interpline. Fixes #38. --- src/wrf/interp.py | 20 +++++++++++++++++++- src/wrf/metadecorators.py | 13 +++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/wrf/interp.py b/src/wrf/interp.py index 403abd6..fcd3a8e 100755 --- a/src/wrf/interp.py +++ b/src/wrf/interp.py @@ -197,7 +197,16 @@ def vertcross(field3d, vert, levels=None, missing=default_fill(np.float64), latlon (:obj:`bool`, optional): Set to True to also interpolate the two-dimensional latitude and longitude coordinates along the same horizontal line and include this information in the metadata - (if enabled). This can be helpful for plotting. Default is False. + (if enabled). This can be helpful for plotting. Default is False. + + Note: + + Currently, *field3d* must be of type :class:`xarray.DataArray` + and contain coordinate information in order to generate the + latitude and longitude coordinates along the line if + *latlon* is set to True. Otherwise, a warning will be issued, + and the latitude and longitude information will not be + present. cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) that can be used to supply pre-extracted NetCDF variables to the @@ -365,6 +374,15 @@ def interpline(field2d, pivot_point=None, horizontal line and include this information in the metadata (if enabled). This can be helpful for plotting. Default is False. + Note: + + Currently, *field2d* must be of type :class:`xarray.DataArray` + and contain coordinate information in order to generate the + latitude and longitude coordinates along the line if + *latlon* is set to True. Otherwise, a warning will be issued, + and the latitude and longitude information will not be + present. + cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) that can be used to supply pre-extracted NetCDF variables to the computational routines. It is primarily used for internal diff --git a/src/wrf/metadecorators.py b/src/wrf/metadecorators.py index 0fe6b35..c397ba1 100644 --- a/src/wrf/metadecorators.py +++ b/src/wrf/metadecorators.py @@ -1,5 +1,6 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) +import warnings import wrapt from collections import OrderedDict @@ -1022,6 +1023,8 @@ def _set_cross_meta(wrapped, instance, args, kwargs): outcoords["xy_loc"] = (loc_dimnames, latlon_loc) else: + warnings.warn("'latlon' is set to True, but 'field3d' " + " contains no coordinate information") outcoords["xy_loc"] = ("cross_line_idx", np.asarray(tuple( CoordPair(xy[i,0], xy[i,1]) for i in py3range(xy.shape[-2])))) @@ -1034,6 +1037,10 @@ def _set_cross_meta(wrapped, instance, args, kwargs): outcoords["vertical"] = z_var2d[:] else: + if inc_latlon: + warnings.warn("'latlon' is set to True, but 'field3d' is " + "not of type xarray.DataArray and contains no " + "coordinate information") outname = "field3d_cross" outattrs = OrderedDict() @@ -1229,6 +1236,8 @@ def _set_line_meta(wrapped, instance, args, kwargs): outcoords["xy_loc"] = (loc_dimnames, latlon_loc) else: + warnings.warn("'latlon' is set to True, but 'field2d' " + "contains no coordinate information") outcoords["xy_loc"] = ("line_idx", np.asarray(tuple( CoordPair(xy[i,0], xy[i,1]) for i in py3range(xy.shape[-2])))) @@ -1239,6 +1248,10 @@ def _set_line_meta(wrapped, instance, args, kwargs): for i in py3range(xy.shape[-2])))) else: + if inc_latlon: + warnings.warn("'latlon' is set to True, but 'field2d' is " + "not of type xarray.DataArray and contains no " + "coordinate information") outname = "field2d_line" outattrs = OrderedDict() From 6a54d31c2e1d01f43d1337ca8455e46c71e2b81e Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Tue, 19 Dec 2017 16:45:42 -0700 Subject: [PATCH 37/43] Added a fortran routine to determine if OpenMP has been enabled. Added a python wrapper, and updated the default omp.f90 and wrffortran.pyf. --- fortran/omp.f90 | 13 +++++++++++++ fortran/ompgen.F90 | 17 +++++++++++++++++ fortran/wrffortran.pyf | 8 ++++++-- src/wrf/api.py | 4 ++-- src/wrf/config.py | 16 +++++++++++++++- src/wrf/constants.py | 2 +- 6 files changed, 54 insertions(+), 6 deletions(-) diff --git a/fortran/omp.f90 b/fortran/omp.f90 index bea1974..dce6b1d 100644 --- a/fortran/omp.f90 +++ b/fortran/omp.f90 @@ -10,6 +10,19 @@ MODULE omp_constants END MODULE omp_constants +FUNCTION fomp_enabled() + + IMPLICIT NONE + + !f2py threadsafe + + LOGICAL :: fomp_enabled + + fomp_enabled = .FALSE. + +END FUNCTION fomp_enabled + + SUBROUTINE fomp_set_num_threads(num_threads) IMPLICIT NONE diff --git a/fortran/ompgen.F90 b/fortran/ompgen.F90 index dea4246..6ae48e1 100644 --- a/fortran/ompgen.F90 +++ b/fortran/ompgen.F90 @@ -61,6 +61,23 @@ MODULE omp_constants END MODULE omp_constants +FUNCTION fomp_enabled() + + IMPLICIT NONE + + !f2py threadsafe + + LOGICAL :: fomp_enabled + +#ifdef _OPENMP + fomp_enabled = .TRUE. +#else + fomp_enabled = .FALSE. +#endif + +END FUNCTION fomp_enabled + + SUBROUTINE fomp_set_num_threads(num_threads) #ifdef _OPENMP USE omp_lib diff --git a/fortran/wrffortran.pyf b/fortran/wrffortran.pyf index b74da12..7211ff6 100644 --- a/fortran/wrffortran.pyf +++ b/fortran/wrffortran.pyf @@ -42,6 +42,10 @@ python module _wrffortran ! in integer, parameter,optional :: fomp_lock_kind=4 integer(kind=4), parameter,optional :: fomp_sched_auto=4 end module omp_constants + function fomp_enabled() ! in :_wrffortran:omp.f90 + threadsafe + logical :: fomp_enabled + end function fomp_enabled subroutine fomp_set_num_threads(num_threads) ! in :_wrffortran:omp.f90 threadsafe integer intent(in) :: num_threads @@ -229,7 +233,7 @@ python module _wrffortran ! in end subroutine dpfcalc subroutine dcapecalc3d(prs,tmk,qvp,ght,ter,sfp,cape,cin,cmsg,mix,mjy,mkzh,ter_follow,psafile,errstat,errmsg) ! in :_wrffortran:rip_cape.f90 threadsafe - use wrf_constants, only: tlclc2,gamma,tlclc1,rgasmd,tlclc4,g,tlclc3,thtecon3,eps,rd,cpmd,celkel,gammamd,eslcon2,eslcon1,cp,thtecon1,algerr,ezero,thtecon2 + use wrf_constants, only: tlclc2,gamma,tlclc1,rgasmd,tlclc4,g,tlclc3,thtecon3,eps,rd,cpmd,celkel,gammamd,eslcon2,eslcon1,cp,thtecon1,ezero,thtecon2 real(kind=8) dimension(mix,mjy,mkzh),intent(in) :: prs real(kind=8) dimension(mix,mjy,mkzh),intent(in),depend(mix,mjy,mkzh) :: tmk real(kind=8) dimension(mix,mjy,mkzh),intent(in),depend(mix,mjy,mkzh) :: qvp @@ -249,7 +253,7 @@ python module _wrffortran ! in end subroutine dcapecalc3d subroutine dcapecalc2d(prs,tmk,qvp,ght,ter,sfp,cape,cin,cmsg,mix,mjy,mkzh,ter_follow,psafile,errstat,errmsg) ! in :_wrffortran:rip_cape.f90 threadsafe - use wrf_constants, only: tlclc2,gamma,tlclc1,rgasmd,tlclc4,g,tlclc3,thtecon3,eps,rd,cpmd,celkel,gammamd,eslcon2,eslcon1,cp,thtecon1,algerr,ezero,thtecon2 + use wrf_constants, only: tlclc2,gamma,tlclc1,rgasmd,tlclc4,g,tlclc3,thtecon3,eps,rd,cpmd,celkel,gammamd,eslcon2,eslcon1,cp,thtecon1,ezero,thtecon2 real(kind=8) dimension(mix,mjy,mkzh),intent(in) :: prs real(kind=8) dimension(mix,mjy,mkzh),intent(in),depend(mix,mjy,mkzh) :: tmk real(kind=8) dimension(mix,mjy,mkzh),intent(in),depend(mix,mjy,mkzh) :: qvp diff --git a/src/wrf/api.py b/src/wrf/api.py index 0542972..c774505 100644 --- a/src/wrf/api.py +++ b/src/wrf/api.py @@ -2,7 +2,7 @@ from .config import (xarray_enabled, disable_xarray, enable_xarray, cartopy_enabled, disable_cartopy, enable_cartopy, basemap_enabled, disable_basemap, enable_basemap, pyngl_enabled, enable_pyngl, disable_pyngl, - set_cache_size, get_cache_size) + set_cache_size, get_cache_size, omp_enabled) from .constants import (ALL_TIMES, Constants, ConversionFactors, ProjectionTypes, default_fill) from .destag import destagger @@ -58,7 +58,7 @@ __all__ += ["xarray_enabled", "disable_xarray", "enable_xarray", "cartopy_enabled", "disable_cartopy", "enable_cartopy", "basemap_enabled", "disable_basemap", "enable_basemap", "pyngl_enabled", "enable_pyngl", "disable_pyngl", - "set_cache_size", "get_cache_size"] + "set_cache_size", "get_cache_size", "omp_enabled"] __all__ += ["ALL_TIMES", "Constants", "ConversionFactors", "ProjectionTypes", "default_fill"] __all__ += ["destagger"] diff --git a/src/wrf/config.py b/src/wrf/config.py index 556f632..adf9705 100644 --- a/src/wrf/config.py +++ b/src/wrf/config.py @@ -4,6 +4,8 @@ from __future__ import (absolute_import, division, print_function, from threading import local import wrapt +from ._wrffortran import fomp_enabled + _local_config = local() def _init_local(): @@ -198,5 +200,17 @@ def get_cache_size(): global _local_config return int(_local_config.cache_size) - + +def omp_enabled(): + """Return True if OpenMP is enabled. + + OpenMP is only enabled if compiled with OpenMP features. + + Returns: + + :obj:`bool`: True if OpenMP is enabled, otherwise False. + + """ + + return True if fomp_enabled() else False diff --git a/src/wrf/constants.py b/src/wrf/constants.py index 96dcc02..ab6da60 100755 --- a/src/wrf/constants.py +++ b/src/wrf/constants.py @@ -6,7 +6,7 @@ import struct import numpy as np from .py3compat import viewitems -from wrf._wrffortran import wrf_constants, omp_constants +from ._wrffortran import wrf_constants, omp_constants #: Indicates that all times should be used in a diagnostic routine. ALL_TIMES = None From 94d1de79ffa97d6aa9c4d509795b0d4ad8d8c3b3 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Wed, 20 Dec 2017 16:35:08 -0700 Subject: [PATCH 38/43] Better support for scipy.io.netcdf. Fixes #41. --- src/wrf/util.py | 55 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/src/wrf/util.py b/src/wrf/util.py index cba14aa..e245456 100644 --- a/src/wrf/util.py +++ b/src/wrf/util.py @@ -745,8 +745,12 @@ def is_moving_domain(wrfin, varname=None, latvar=either("XLAT", "XLAT_M"), # to be a shortcut in the netcdf files. if varname is not None: try: - coord_names = getattr(first_wrfnc.variables[varname], - "coordinates").split() + coord_str = getattr(first_wrfnc.variables[varname], "coordinates") + # scipy.io.netcdf stores attributes as bytes rather than str + if isinstance(coord_str, str): + coord_names = coord_str.split() + else: + coord_names = coord_str.decode().split() except AttributeError: # Variable doesn't have a coordinates attribute, use the # arguments @@ -896,7 +900,17 @@ def extract_dim(wrfin, dim): d = wrfin.dimensions[dim] if not isinstance(d, int): - return len(d) #netCDF4 + try: + return len(d) #netCDF4 + except TypeError: #scipy.io.netcdf + # Scipy can't handled unlimited dimensions, so now we have to + # figure it out + try: + s = wrfin.variables["P"].shape + return s[-4] + except: + raise ValueError("unsupported NetCDF reader") + return d # PyNIO @@ -1160,7 +1174,10 @@ def _get_coord_names(wrfin, varname): lat_coord = "XLAT" lon_coord = "XLONG" else: - coord_names = coord_attr.split() + if isinstance(coord_attr, str): + coord_names = coord_attr.split() + else: + coord_names = coord_attr.decode().split() lon_coord = coord_names[0] lat_coord = coord_names[1] @@ -1231,7 +1248,24 @@ def _build_data_array(wrfnc, varname, timeidx, is_moving_domain, is_multifile, else: data = data[np.newaxis] - attrs = OrderedDict(var.__dict__) + attrs = OrderedDict() + for key, val in viewitems(var.__dict__): + # scipy.io adds these but don't want them + if key in ("data", "_shape", "_size", "_typecode", "_attributes", + "maskandscale", "dimensions"): + continue + + _key = key if isinstance(key, str) else key.decode() + if isstr(val): + _val = val + else: + if isinstance(val, bytes): + _val = val.decode() # scipy.io.netcdf + else: + _val = val + + attrs[_key] = _val + dimnames = var.dimensions[-data.ndim:] lat_coord = lon_coord = time_coord = None @@ -2999,7 +3033,16 @@ def get_filepath(obj): try: path = obj.file.path except: - raise ValueError("file contains no path information") + # Let's make up a filename from the first file time + found = False + times = extract_times(obj, None, meta=False, do_xtime=False) + for t in times: + path = "wrfout_{}".format(str(t)) + found = True + break + + if not found: + raise ValueError("file contains no path information") return path From 3441467ecb26d83185f8f467dd5867b27042d25b Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Fri, 22 Dec 2017 14:13:21 -0700 Subject: [PATCH 39/43] Added a product for the vertical staggered grid. Fixes #20. --- doc/source/_templates/product_table.txt | 16 ++- src/wrf/g_geoht.py | 158 +++++++++++++++++++++++- src/wrf/metadecorators.py | 28 ++++- src/wrf/routines.py | 8 +- 4 files changed, 197 insertions(+), 13 deletions(-) diff --git a/doc/source/_templates/product_table.txt b/doc/source/_templates/product_table.txt index 8dfb388..c49c3ac 100644 --- a/doc/source/_templates/product_table.txt +++ b/doc/source/_templates/product_table.txt @@ -35,7 +35,9 @@ | | | | | | | | | **do_liqskin** (boolean): Set to True to enable liquid skin calculation. Default is *False*. | +--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ -| geopt/geopotential | Full Model Geopotential | m2 s-2 | | +| geopt/geopotential | Geopotential for the Mass Grid | m2 s-2 | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| geopt_stag | Geopotential for the Vertically Staggered Grid | m2 s-2 | | +--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ | helicity | Storm Relative Helicity | m2 s-2 | **top** (float): The top level for the calculation in meters. Default is *3000.0*. | +--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ @@ -231,7 +233,17 @@ | | | | | | | | ft s-1 | | +--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ -| z/height | Full Model Height on Mass Levels | m | **msl** (boolean): Set to False to return AGL values. True is for MSL. Default is *True*. | +| z/height | Model Height for Mass Grid | m | **msl** (boolean): Set to False to return AGL values. True is for MSL. Default is *True*. | +| | | | | +| | | km | **units** (str) : Set to desired units. Default is *'m'*. | +| | | | | +| | | dm | | +| | | | | +| | | ft | | +| | | | | +| | | mi | | ++--------------------+---------------------------------------------------------------+-----------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------+ +| zstag | Model Height for Vertically Staggered Grid | m | **msl** (boolean): Set to False to return AGL values. True is for MSL. Default is *True*. | | | | | | | | | km | **units** (str) : Set to desired units. Default is *'m'*. | | | | | | diff --git a/src/wrf/g_geoht.py b/src/wrf/g_geoht.py index 98d9ceb..d9c7624 100755 --- a/src/wrf/g_geoht.py +++ b/src/wrf/g_geoht.py @@ -1,6 +1,8 @@ from __future__ import (absolute_import, division, print_function, unicode_literals) +import warnings + from .constants import Constants from .destag import destagger from .decorators import convert_units @@ -9,7 +11,7 @@ from .util import extract_vars, either def _get_geoht(wrfin, timeidx, method="cat", squeeze=True, cache=None, meta=True, _key=None, - height=True, msl=True): + height=True, msl=True, stag=False): """Return the geopotential or geopotential height. If *height* is False, then geopotential is returned in units of @@ -67,6 +69,9 @@ def _get_geoht(wrfin, timeidx, method="cat", squeeze=True, as Mean Sea Level (MSL). Set to False to return the geopotential height as Above Ground Level (AGL) by subtracting the terrain height. Default is True. + + stag (:obj:`bool`, optional): Set to True to use the vertical + staggered grid, rather than the mass grid. Default is False. Returns: :class:`xarray.DataArray` or :class:`numpy.ndarray`: The @@ -86,14 +91,21 @@ def _get_geoht(wrfin, timeidx, method="cat", squeeze=True, phb = ph_vars["PHB"] hgt = ph_vars["HGT"] geopt = ph + phb - geopt_unstag = destagger(geopt, -3) + if not stag: + geopt_unstag = destagger(geopt, -3) + else: + geopt_unstag = geopt else: ght_vars = extract_vars(wrfin, timeidx, ("GHT", "HGT_M"), method, squeeze, cache, meta=False, _key=_key) geopt_unstag = ght_vars["GHT"] * Constants.G hgt = ght_vars["HGT_M"] - + + if stag: + warnings.warn("file contains no vertically staggered geopotential " + "height variable, returning unstaggered result " + "instead" ) if height: if msl: return geopt_unstag / Constants.G @@ -110,7 +122,7 @@ def _get_geoht(wrfin, timeidx, method="cat", squeeze=True, return geopt_unstag -@set_height_metadata(geopt=True) +@set_height_metadata(geopt=True, stag=False) def get_geopt(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None): """Return the geopotential. @@ -171,7 +183,7 @@ def get_geopt(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, False, True) -@set_height_metadata(geopt=False) +@set_height_metadata(geopt=False, stag=False) @convert_units("height", "m") def get_height(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, meta=True, _key=None, @@ -245,3 +257,139 @@ def get_height(wrfin, timeidx=0, method="cat", squeeze=True, return _get_geoht(wrfin, timeidx, method, squeeze, cache, meta, _key, True, msl) + +@set_height_metadata(geopt=True, stag=True) +def get_stag_geopt(wrfin, timeidx=0, method="cat", squeeze=True, cache=None, + meta=True, _key=None): + """Return the geopotential for the vertically staggered grid. + + The geopotential is returned in units of [m2 s-2]. + + This functions extracts the necessary variables from the NetCDF file + object in order to perform the calculation. + + Args: + + wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ + iterable): WRF-ARW NetCDF + data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` + or an iterable sequence of the aforementioned types. + + timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The + desired time index. This value can be a positive integer, + negative integer, or + :data:`wrf.ALL_TIMES` (an alias for None) to return + all times in the file or sequence. The default is 0. + + method (:obj:`str`, optional): The aggregation method to use for + sequences. Must be either 'cat' or 'join'. + 'cat' combines the data along the Time dimension. + 'join' creates a new dimension for the file index. + The default is 'cat'. + + squeeze (:obj:`bool`, optional): Set to False to prevent dimensions + with a size of 1 from being automatically removed from the shape + of the output. Default is True. + + cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) + that can be used to supply pre-extracted NetCDF variables to the + computational routines. It is primarily used for internal + purposes, but can also be used to improve performance by + eliminating the need to repeatedly extract the same variables + used in multiple diagnostics calculations, particularly when using + large sequences of files. + Default is None. + + meta (:obj:`bool`, optional): Set to False to disable metadata and + return :class:`numpy.ndarray` instead of + :class:`xarray.DataArray`. Default is True. + + _key (:obj:`int`, optional): A caching key. This is used for internal + purposes only. Default is None. + + Returns: + :class:`xarray.DataArray` or :class:`numpy.ndarray`: The + geopotential. + If xarray is enabled and the *meta* parameter is True, then the result + will be a :class:`xarray.DataArray` object. Otherwise, the result will + be a :class:`numpy.ndarray` object with no metadata. + + """ + return _get_geoht(wrfin, timeidx, method, squeeze, cache, meta, _key, + False, True, stag=True) + + +@set_height_metadata(geopt=False, stag=True) +@convert_units("height", "m") +def get_stag_height(wrfin, timeidx=0, method="cat", squeeze=True, + cache=None, meta=True, _key=None, + msl=True, units="m"): + """Return the geopotential height for the vertically staggered grid. + + If *msl* is True, then geopotential height is returned as Mean Sea Level + (MSL). If *msl* is False, then geopotential height is returned as + Above Ground Level (AGL) by subtracting the terrain height. + + This functions extracts the necessary variables from the NetCDF file + object in order to perform the calculation. + + Args: + + wrfin (:class:`netCDF4.Dataset`, :class:`Nio.NioFile`, or an \ + iterable): WRF-ARW NetCDF + data as a :class:`netCDF4.Dataset`, :class:`Nio.NioFile` + or an iterable sequence of the aforementioned types. + + timeidx (:obj:`int` or :data:`wrf.ALL_TIMES`, optional): The + desired time index. This value can be a positive integer, + negative integer, or + :data:`wrf.ALL_TIMES` (an alias for None) to return + all times in the file or sequence. The default is 0. + + method (:obj:`str`, optional): The aggregation method to use for + sequences. Must be either 'cat' or 'join'. + 'cat' combines the data along the Time dimension. + 'join' creates a new dimension for the file index. + The default is 'cat'. + + squeeze (:obj:`bool`, optional): Set to False to prevent dimensions + with a size of 1 from being automatically removed from the shape + of the output. Default is True. + + cache (:obj:`dict`, optional): A dictionary of (varname, ndarray) + that can be used to supply pre-extracted NetCDF variables to the + computational routines. It is primarily used for internal + purposes, but can also be used to improve performance by + eliminating the need to repeatedly extract the same variables + used in multiple diagnostics calculations, particularly when using + large sequences of files. + Default is None. + + meta (:obj:`bool`, optional): Set to False to disable metadata and + return :class:`numpy.ndarray` instead of + :class:`xarray.DataArray`. Default is True. + + _key (:obj:`int`, optional): A caching key. This is used for internal + purposes only. Default is None. + + msl (:obj:`bool`, optional): Set to True to return geopotential height + as Mean Sea Level (MSL). Set to False to return the + geopotential height as Above Ground Level (AGL) by subtracting + the terrain height. Default is True. + + units (:obj:`str`): The desired units. Refer to the :meth:`getvar` + product table for a list of available units for 'z'. Default + is 'm'. + + Returns: + :class:`xarray.DataArray` or :class:`numpy.ndarray`: The + geopotential height. + If xarray is enabled and the *meta* parameter is True, then the result + will be a :class:`xarray.DataArray` object. Otherwise, the result will + be a :class:`numpy.ndarray` object with no metadata. + + """ + + return _get_geoht(wrfin, timeidx, method, squeeze, cache, meta, _key, + True, msl, stag=True) + \ No newline at end of file diff --git a/src/wrf/metadecorators.py b/src/wrf/metadecorators.py index c397ba1..0549220 100644 --- a/src/wrf/metadecorators.py +++ b/src/wrf/metadecorators.py @@ -645,7 +645,7 @@ def set_latlon_metadata(xy=False): return func_wrapper -def set_height_metadata(geopt=False): +def set_height_metadata(geopt=False, stag=False): """A decorator that sets the metadata for a wrapped height function's output. @@ -660,6 +660,9 @@ def set_height_metadata(geopt=False): returns geopotential. Set to True if the wrapped function returns geopotential height. Default is False. + stag (:obj:`bool`, optional): Set to True to use the vertical + staggered grid, rather than the mass grid. Default is False. + Returns: :class:`xarray.DataArray` or :class:`numpy.ndarray`: The wrapped @@ -695,9 +698,17 @@ def set_height_metadata(geopt=False): if cache is None: cache = {} + is_met_em = False # For height, either copy the met_em GHT variable or copy and modify # pressure (which has the same dims as destaggered height) - ht_metadata_varname = either("P", "GHT")(wrfin) + if not stag: + ht_metadata_varname = either("P", "GHT")(wrfin) + else: + ht_metadata_varname = either("PH", "GHT")(wrfin) + + if ht_metadata_varname == "GHT": + is_met_em = True + ht_var = extract_vars(wrfin, timeidx, ht_metadata_varname, method, squeeze, cache, meta=True, _key=_key) @@ -723,12 +734,21 @@ def set_height_metadata(geopt=False): if geopt: outname = "geopt" outattrs["units"] = "m2 s-2" - outattrs["description"] = "full model geopotential" + if not stag or is_met_em: + outattrs["description"] = "geopotential (mass grid)" + else: + outattrs["description"] = ("geopotential (vertically " + "staggered grid)") else: outname = "height" if msl else "height_agl" outattrs["units"] = units height_type = "MSL" if msl else "AGL" - outattrs["description"] = "model height ({})".format(height_type) + if not stag or is_met_em: + outattrs["description"] = ("model height - [{}] " + "(mass grid)".format(height_type)) + else: + outattrs["description"] = ("model height - [{}] (vertically " + "staggered grid)".format(height_type)) return DataArray(result, name=outname, diff --git a/src/wrf/routines.py b/src/wrf/routines.py index 3267598..5442956 100644 --- a/src/wrf/routines.py +++ b/src/wrf/routines.py @@ -7,7 +7,7 @@ from .g_cape import get_2dcape, get_3dcape from .g_ctt import get_ctt from .g_dbz import get_dbz, get_max_dbz from .g_dewpoint import get_dp, get_dp_2m -from .g_geoht import get_geopt, get_height +from .g_geoht import get_geopt, get_height, get_stag_geopt, get_stag_height from .g_helicity import get_srh, get_uh from .g_latlon import get_lat, get_lon from .g_omega import get_omega @@ -68,7 +68,9 @@ _FUNC_MAP = {"cape2d" : get_2dcape, "uvmet_wspd_wdir" : get_uvmet_wspd_wdir, "uvmet10_wspd_wdir" : get_uvmet10_wspd_wdir, "ctt" : get_ctt, - "cloudfrac" : get_cloudfrac + "cloudfrac" : get_cloudfrac, + "geopt_stag" : get_stag_geopt, + "zstag" : get_stag_height } _VALID_KARGS = {"cape2d" : ["missing"], @@ -113,6 +115,8 @@ _VALID_KARGS = {"cape2d" : ["missing"], "ctt" : [], "cloudfrac" : ["vert_type", "low_thresh", "mid_thresh", "high_thresh"], + "geopt_stag" : [], + "zstag" : ["msl", "units"], "default" : [] } From 48f406c2638c22267c9c3c3b7e16d8568a4652cb Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Tue, 9 Jan 2018 11:45:45 -0700 Subject: [PATCH 40/43] Fixed issue with coordinate cache key name conflict. Also added documentation for OpenMP and performance tips. The tutorial section has been moved to better support announcements in the future. Renamed the OpenMP scheduler constants so that they are placed in to the wrf namespace rather than wrf.Constants. --- doc/source/basic_usage.rst | 314 ++++++++++++++++++ doc/source/index.rst | 2 +- doc/source/tutorial.rst | 25 ++ doc/source/tutorials/tutorial_03_2018.rst | 76 +++++ .../wrf_workshop_2017.rst} | 0 doc/source/user_api/index.rst | 79 ++++- src/wrf/api.py | 7 +- src/wrf/config.py | 11 +- src/wrf/constants.py | 8 +- src/wrf/extension.py | 5 +- src/wrf/util.py | 8 +- 11 files changed, 519 insertions(+), 16 deletions(-) create mode 100644 doc/source/tutorial.rst create mode 100644 doc/source/tutorials/tutorial_03_2018.rst rename doc/source/{workshop.rst => tutorials/wrf_workshop_2017.rst} (100%) diff --git a/doc/source/basic_usage.rst b/doc/source/basic_usage.rst index 3bd5eba..9a201c7 100644 --- a/doc/source/basic_usage.rst +++ b/doc/source/basic_usage.rst @@ -1,6 +1,34 @@ How To Use ============ +Introduction +--------------- + +The API for wrf-python can be summarized as a variable extraction/computation +routine, several interpolation routines, and some plotting utilities. +The API is kept as simple as possible to help minimize the +programming burden on new users, students, and scientists. In the future, we +plan to extend xarray for programmers desiring a more object oriented API, +but this remains a work in progress. + +The five most commonly used routines can be summarized as: + +- **wrf.getvar**: The routine that extracts WRF NetCDF variables or + computes diagnostic variables. This is the routine you will use most often. + +- **wrf.interplevel**: Interpolates a three-dimensional field to a horizontal + plane at a specified level using simple (fast) linear interpolation. + +- **wrf.vertcross**: Interpolates a three-dimensional field to a vertical plane + through a user-specified horizontal line (i.e. a cross section). + +- **wrf.interpline**: Interpolates a two-dimensional field to a user-specified + line. + +- **wrf.vinterp**: Interpolates a three-dimensional field to user-specified + 'surface' levels (e.g. theta-e levels). This is a smarter, but slower, + version of wrf.interplevel. + Basic Usage ---------------- @@ -1614,3 +1642,289 @@ Result: ] + +Using OpenMP +------------------------- + +Beginning in version 1.1, the Fortran computational routines in wrf-python make +use of OpenMP directives. OpenMP enables the calculations to use multiple CPU +cores, which can improve performance. In order to use OpenMP features, +wrf-python has to be compiled with OpenMP enabled (most pre-built binary +installations will have this enabled). + +The Fortran computational routines have all been built using runtime +scheduling, instead of compile time scheduling, so that the user can choose the +scheduler type within their Python application. By default, the scheduling +type is set to :data:`wrf.OMP_SCHED_STATIC` using only 1 CPU core, so +wrf-python will behave similarly to the non-OpenMP built versions. For the most +part, the difference between the scheduling types is minimal, with the exception +being the :data:`wrf.OMP_SCHED_DYNAMIC` scheduler that is much slower due to +the additional overhead associated with it. For new users, using the default +scheduler should be sufficient. + + +Verifying that OpenMP is Enabled +************************************* + +To take advantage of the performance improvements offered by OpenMP, wrf-python +needs to have been compiled with OpenMP features enabled. The example below +shows how you can determine if OpenMP is enabled in your build of wrf-python. + +.. code-block:: python + + from __future__ import print_function + + from wrf import omp_enabled + + print(omp_enabled()) + + +Result: + +.. code-block:: none + + True + + +Determining the Number of Available Processors +*************************************************** + +The example below shows how you can get the maximum number of processors +that are available on your system. + +.. code-block:: python + + from __future__ import print_function + + from wrf import omp_get_num_procs + + print(omp_get_num_procs()) + + +Result: + +.. code-block:: none + + 8 + + +Specifying the Number of Threads +************************************* + +To enable multicore support via OpenMP, specifying the maximum number +of OpenMP threads (i.e. CPU cores) is the only step that you need to take. + +In the example below, :meth:`wrf.omp_set_num_threads` is used to set the +maximum number of threads to use, and :meth:`wrf.omp_get_max_threads` is +get to get (and print) the maximum number of threads used. + +.. note:: + + Although there is an OpenMP library named :meth:`wrf.omp_get_num_threads`, + this method will always returns 1 when called from the sequential part of + the program. Use :meth:`wrf.omp_get_max_threads` to return the value set by + :meth:`wrf.omp_set_num_threads`. + +.. code-block:: python + + from __future__ import print_function + + from wrf import omp_set_num_threads, omp_get_max_threads + + omp_set_num_threads(4) + + print (omp_get_max_threads()) + + +Result: + +.. code-block:: none + + 4 + +Setting a Different Scheduler Type +************************************** + +When an OpenMP directive is encountered in the Fortran code, a scheduler is +used to determine how the work is divided among the threads. All of the +Fortran routines are compiled to use a 'runtime' scheduler, which indicates +that the scheduler type (from the four listed below) is to be chosen at +runtime (i.e. inside a Python script) + +By default, the scheduler chosen is the :data:`wrf.OMP_SCHED_STATIC` scheduler, +which should be sufficient for most users. However, OpenMP and wrf-python +include the following options for the scheduler type: + +- :data:`wrf.OMP_SCHED_STATIC` +- :data:`wrf.OMP_SCHED_DYNAMIC` +- :data:`wrf.OMP_SCHED_GUIDED` +- :data:`wrf.OMP_SCHED_AUTO` + +Refer to the +`OpenMP Specification `_. +for more information about these scheduler types. In local testing, +:data:`wrf.OMP_SCHED_GUIDED` produced the best results, but +differences between :data:`wrf.OMP_SCHED_STATIC`, +:data:`wrf.OMP_SCHED_GUIDED`, and +:data:`wrf.OMP_SCHED_AUTO` were minor. However, +:data:`wrf.OMP_SCHED_DYNAMIC` produced noticeably slower results +due to the overhead of using a dynamic scheduler. + +When setting a scheduler type, the :meth:`wrf.omp_set_schedule` takes two +arguments. The first is the scheduler type (one from the list above), and the +second optional argument is a modifier, which is usually referred as the chunk +size. If the modifier/chunk_size is set to 0, then the OpenMP default +implementation is used. For :data:`wrf.OMP_SCHED_AUTO`, the +modifier is ignored. + +If you are new to OpenMP and all this sounds confusing, don't worry about +setting a scheduler type. The default static scheduler will be good enough. + +In the example below, the scheduler type is set to +:data:`wrf.OMP_SCHED_GUIDED` and uses the default chunk size of 0. The +scheduler type is then read back using :meth:`wrf.omp_get_schedule` +and printed. + +.. code-block:: python + + from __future__ import print_function + + from wrf import omp_set_schedule, omp_get_schedule, OMP_SCHED_GUIDED + + omp_set_schedule(OMP_SCHED_GUIDED, 0) + + sched, modifier = omp_get_schedule() + + print(sched, modifier) + + +Result: + +.. code-block:: none + + 3 1 + +Notice that the printed scheduler type (*sched* variable) is set to a +value of 3, which is the actual integer constant value for the +:data:`wrf.OMP_SCHED_GUIDED` scheduler type. The *modifier* is returned as a +value of 1, which is different than the 0 that was supplied to the +:meth:`wrf.omp_set_schedule` routine. This is because the 0 tells OpenMP to use +its own default value for the scheduler, which is 1 for this type of scheduler. + + +Performance Note +****************** + +If you have enabled multicore support with OpenMP, you may have noticed that +the routines do not scale linearly with the number of CPU cores added. One main +reason is that the computational routines are already fairly efficient and +vectorize well, so for many grid sizes, the time it takes to extract the +variables is on par with the time required to compute the diagnostic with a +single CPU core. Adding more CPU cores will decrease the time needed to do the +computation, but total performance will still be limited by the time it takes +to extract the variables from the NetCDF file. For local testing, diminishing +returns were seen after 4 CPU cores, but this will largely depend on the +hardware used and grid size for your WRF run. + + +Performance Tips +-------------------- + +Memory Issues and :data:`wrf.ALL_TIMES` +****************************************** + +The use of :data:`wrf.ALL_TIMES` for the timeidx to :meth:`wrf.getvar` is +convenient for computing diagnostic variables across multiple files/times, but +there is something that users should be aware of. When :data:`wrf.ALL_TIMES` is +set as the *timeidx* argument, all arrays used in the computation are extracted +for all times before the computation is started. This can cause serious memory +issues on smaller hardware systems like laptops. + +In this example, the user wants to use a data set that is 289 x 39 x 300 x 300 +and compute z for the entire data set. The user is using a laptop with +16 GB of memory. + +.. code:: python + + from netCDF4 import Dataset + from wrf import getvar, ALL_TIMES + + file_list = [Dataset("/path/to/file1"), Dataset("/path/to/file2"),...] + z = getvar(file_list, "z", ALL_TIMES) + +Five hours later, the computation finished. What happened? + +In wrf-python, all of the computational routines use 8-byte REAL variables so +that both the 4-byte and 8-byte version of WRF output can be used. The +calculation for z extracts three variables (P, PHB, and HGT) and returns a +fourth array (RESULT). The RESULT will get cut in half to 4-byte REALs +after the computation, but need 8-byte REAL when the result is computed. + +Let's look at the approximate amount memory needed: + +P: 289 x 39 x 300 x 300 x 8 = 8,115,120,000 bytes (~8 GB!) +PHB: 289 x 39 x 300 x 300 x 8 = 8,115,120,000 bytes (~8 GB!) +HGT: 289 x 300 x 300 x 8 = 208,080,000 (~208 MB) +RESULT: 289 x 39 x 300 x 300 x 8 = 8,115,120,000 bytes (~8 GB!) + +Yikes! So, in order to do this calculation using :data:`wrf.ALL_TIMES` as +the timeidx, over 24.2 GB are needed for this one calculation. When the laptop +runs out of memory, it begins using the hard drive for swap memory, which runs +hundreds of times slower than real memory. + +To fix this situation, it is better to allocate the output array yourself and +run the calculation for each time step in a loop. The required memory +requirements change to: + +(Only need to store the result in a 4-byte REAL) +FINAL_RESULT: 289 x 39 x 300 x 300 x 4 = 4,057560,000 bytes (~4 GB) + +(The numbers below are for each loop iteration) +P: 39 x 300 x 300 x 8 = 28,080,000 bytes (~28 MB) +PHB: 39 x 300 x 300 x 8 = 28,080,000 bytes (~28 MB) +HGT: 300 x 300 x 8 = 720,000 bytes (720 KB) +RESULT: 39 x 300 x 300 x 8 = 28,080,000 bytes (~28 MB) + +Since the memory for the computation is deleted after each +loop iteration, the total memory usage drops to approximately 4.1 GB. + +The moral of the story is that you need to make sure that your system has +enough memory to extract everything it needs up front if you want to use +:data:`wrf.ALL_TIMES`, otherwise it is better to "loop-and-fill" yourself. + +Here is an example of the loop-and-fill technique: + +.. code:: python + + from __future__ import print_function, division + + import numpy as np + from netCDF4 import Dataset + from wrf import getvar, ALL_TIMES + + filename_list = ["/path/to/file1", "/path/to/file2",...] + + # Result shape (hardcoded for this example) + result_shape = (289, 39, 300, 300) + + # Only need 4-byte floats + z_final = np.empty(result_shape, np.float32) + + # Modify this number if using more than 1 time per file + times_per_file = 1 + + for timeidx in xrange(result_shape[0]): + # Compute the file index and the time index inside the file + fileidx = timeidx // times_per_file + file_timeidx = timeidx % times_per_file + + f = Dataset(filename_list[fileidx]) + z = getvar(f, "z", file_timeidx) + + z_final[timeidx,:] = z[:] + f.close() + + + + + diff --git a/doc/source/index.rst b/doc/source/index.rst index c0ae7c1..e0ca1e0 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -46,7 +46,7 @@ Documentation ./faq ./support ./license - ./workshop + ./tutorial_03_2018 Indices and tables diff --git a/doc/source/tutorial.rst b/doc/source/tutorial.rst new file mode 100644 index 0000000..29b68b5 --- /dev/null +++ b/doc/source/tutorial.rst @@ -0,0 +1,25 @@ +Tutorials +============= + +NCAR occasionally provides tutorials for wrf-python at various times +throughout the year. + +Below are the links to the upcoming and past tutorials. + +Upcoming Tutorials +--------------------- + +.. toctree:: + :maxdepth: 1 + + tutorials/tutorial_03_2018.rst + + +Past Tutorials +------------------ + +.. toctree:: + :maxdepth: 1 + + tutorials/wrf_workshop_2017.rst + diff --git a/doc/source/tutorials/tutorial_03_2018.rst b/doc/source/tutorials/tutorial_03_2018.rst new file mode 100644 index 0000000..3f5ec39 --- /dev/null +++ b/doc/source/tutorials/tutorial_03_2018.rst @@ -0,0 +1,76 @@ +WRF Tutorial 2018 +===================== + +NCAR will be providing a four hour tutorial for wrf-python on Wednesday, March +7, 2018. The tutorial is free, but seating is limited to only 16 students, so +registration is required. + +The tutorial will take place at NCAR's corporate training center in Boulder, +Colorado. + +`Corporate Technical Training Center `_ +3085 Center Green Drive, Building CG-2, Room #3024 +Boulder, Colorado + +Overview +-------------- + +This tutorial provides an introduction to wrf-python. The tutorial is friendly +for new users of wrf-python, but this is not an introduction to the Python +programming language (see Prerequisites below). Due to limited seating, if you +do not have any previous experience with Python, please do not register +for this tutorial. + +Students are encouraged to bring their own data sets, but data will be provided +if this is not an option. Students will be provided a jupyter notebook workbook +which can be modified to accommodate their data. + +Topics include: + +- How to install wrf-python via conda +- A brief introduction to jupyter notebook +- Overview of WRF data files +- WRF-Python basics +- Plotting with cartopy +- Overview of OpenMP features and other performance tips +- Open lab for students + + +Registration +--------------- + +The registration form is here: + +`Registration Form `_ + +Registration consists of a brief survey, which will help give the instructors +a brief overview of your background and will help tailor the tutorial to +your expectations. + + +Prerequisites +--------------- + +This tutorial assumes that you have basic knowledge of how to type commands +in to a command terminal using your preferred operating system. You +should know some basic directory commands like *cd*, *mkdir*, *cp*, *mv*. + +This tutorial assumes that you have prior experience programming in Python. +Below is a list of some Python concepts that you will see in the examples, +but don't worry if you aren't familiar with everything. + +- Opening a Python interpreter and entering commands. +- Importing packages via the import statement. +- Familiarity with some of the basic Python types: str, list, tuple, dict, bool, float, int, None. +- Creating a list, tuple, or dict with "[ ]", "( )", "{ }" syntax (e.g. my_list = [1,2,3,4,5]). +- Accessing dict/list/tuple items with the "x[ ]" syntax (e.g. my_list_item = my_list[0]). +- Slicing str/list/tuple with the ":" syntax (e.g. my_slice = my_list[1:3]). +- Using object methods and attributes with the "x.y" syntax (e.g. my_list.append(6)). +- Calling functions (e.g. result = some_function(x, y)) +- Familiarity with numpy would be helpful, as only a very brief introduction + is provided. +- Familiarity with matplotlib would be helpful, as only a very brief + introduction is provided. + + + diff --git a/doc/source/workshop.rst b/doc/source/tutorials/wrf_workshop_2017.rst similarity index 100% rename from doc/source/workshop.rst rename to doc/source/tutorials/wrf_workshop_2017.rst diff --git a/doc/source/user_api/index.rst b/doc/source/user_api/index.rst index 17b72e0..587d429 100644 --- a/doc/source/user_api/index.rst +++ b/doc/source/user_api/index.rst @@ -74,7 +74,6 @@ the array object to a compiled extension. :toctree: ./generated/ wrf.to_np - Variable Extraction Routines ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -110,7 +109,7 @@ The routines below are used to assist with plotting. wrf.get_pyngl wrf.cartopy_xlim wrf.cartopy_ylim - + Raw Diagnostic Routines ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -154,6 +153,82 @@ sure they are removed before calling these routines. wrf.omega wrf.pw +OpenMP Runtime Library Routines +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The routines below are the OpenMP runtime libraries that have been wrapped +for wrf-python. The entire library (OpenMP 3.x) has been wrapped, but many of +the routines are only useful inside of an OpenMP thread, so they aren't useful +from inside the Python interpreter. Also, the Fortran code in wrf-python is +fairly simple in terms of threading, so features like nested threads aren't +used. The documentation below is split in to the useful OpenMP functions and +the less useful functions. + +The documentation for each routine was taken directly from the +`OpenMP Specification `_. +Read the specification for more details about these routines. + +Useful OpenMP Routines +***************************** + +The routines below are useful when called from within a Python program. These +routines handle setting the number of threads, setting up the scheduler, +and timing. + +It is also important to note that the OpenMP directives within the Fortran +code all specify a runtime scheduler. This means that the user can control +the type of scheduling to use from within their Python application by using the +routines below. + +.. autosummary:: + :nosignatures: + :toctree: ./generated/ + + wrf.omp_enabled + wrf.omp_set_num_threads + wrf.omp_get_max_threads + wrf.omp_get_num_procs + wrf.omp_set_dynamic + wrf.omp_get_dynamic + wrf.omp_set_schedule + wrf.omp_get_schedule + wrf.omp_get_thread_limit + wrf.omp_get_wtime + wrf.omp_get_wtick + +Less Useful OpenMP Routines +******************************* + +The routines below are less useful because wrf-python does not use nested +parallelism and some of the routines are only applicable when called from +within an OpenMP thread. + +.. autosummary:: + :nosignatures: + :toctree: ./generated/ + + wrf.omp_get_num_threads + wrf.omp_get_thread_num + wrf.omp_in_parallel + wrf.omp_set_nested + wrf.omp_get_nested + wrf.omp_set_max_active_levels + wrf.omp_get_max_active_levels + wrf.omp_get_level + wrf.omp_get_ancestor_thread_num + wrf.omp_get_team_size + wrf.omp_get_active_level + wrf.omp_in_final + wrf.omp_init_lock + wrf.omp_init_nest_lock + wrf.omp_destroy_lock + wrf.omp_destroy_nest_lock + wrf.omp_set_lock + wrf.omp_set_nest_lock + wrf.omp_unset_lock + wrf.omp_unset_nest_lock + wrf.omp_test_lock + wrf.omp_test_nest_lock Configuration Routines ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/wrf/api.py b/src/wrf/api.py index c774505..660c9d6 100644 --- a/src/wrf/api.py +++ b/src/wrf/api.py @@ -4,7 +4,9 @@ from .config import (xarray_enabled, disable_xarray, enable_xarray, pyngl_enabled, enable_pyngl, disable_pyngl, set_cache_size, get_cache_size, omp_enabled) from .constants import (ALL_TIMES, Constants, ConversionFactors, - ProjectionTypes, default_fill) + ProjectionTypes, default_fill, + OMP_SCHED_STATIC, OMP_SCHED_DYNAMIC, + OMP_SCHED_GUIDED, OMP_SCHED_AUTO) from .destag import destagger from .routines import getvar from .computation import (xy, interp1d, interp2dxy, interpz3d, slp, tk, td, rh, @@ -60,7 +62,8 @@ __all__ += ["xarray_enabled", "disable_xarray", "enable_xarray", "pyngl_enabled", "enable_pyngl", "disable_pyngl", "set_cache_size", "get_cache_size", "omp_enabled"] __all__ += ["ALL_TIMES", "Constants", "ConversionFactors", "ProjectionTypes", - "default_fill"] + "default_fill", "OMP_SCHED_STATIC", "OMP_SCHED_DYNAMIC", + "OMP_SCHED_GUIDED", "OMP_SCHED_AUTO"] __all__ += ["destagger"] __all__ += ["getvar"] __all__ += ["xy", "interp1d", "interp2dxy", "interpz3d", "slp", "tk", "td", diff --git a/src/wrf/config.py b/src/wrf/config.py index adf9705..d22d5de 100644 --- a/src/wrf/config.py +++ b/src/wrf/config.py @@ -4,7 +4,9 @@ from __future__ import (absolute_import, division, print_function, from threading import local import wrapt -from ._wrffortran import fomp_enabled +from ._wrffortran import (fomp_enabled, fomp_set_num_threads, + fomp_set_schedule, fomp_set_dynamic, + omp_constants) _local_config = local() @@ -214,3 +216,10 @@ def omp_enabled(): return True if fomp_enabled() else False + +# Set OpenMP to use 1 thread, static scheduler, and no dynamic +# Note: Using the raw extension functions here to prevent possible +# circular import problems in the future. +fomp_set_num_threads(1) +fomp_set_schedule(omp_constants.fomp_sched_static, 0) +fomp_set_dynamic(False) diff --git a/src/wrf/constants.py b/src/wrf/constants.py index ab6da60..6a6dc90 100755 --- a/src/wrf/constants.py +++ b/src/wrf/constants.py @@ -17,10 +17,10 @@ class Constants(object): for key,val in viewitems(wrf_constants.__dict__): setattr(Constants, key.upper(), np.asscalar(val)) -setattr(Constants, "OMP_SCHED_STATIC", omp_constants.fomp_sched_static) -setattr(Constants, "OMP_SCHED_DYNAMIC", omp_constants.fomp_sched_dynamic) -setattr(Constants, "OMP_SCHED_GUIDED", omp_constants.fomp_sched_guided) -setattr(Constants, "OMP_SCHED_AUTO", omp_constants.fomp_sched_auto) +OMP_SCHED_STATIC = omp_constants.fomp_sched_static +OMP_SCHED_DYNAMIC = omp_constants.fomp_sched_dynamic +OMP_SCHED_GUIDED = omp_constants.fomp_sched_guided +OMP_SCHED_AUTO = omp_constants.fomp_sched_auto class ConversionFactors(object): diff --git a/src/wrf/extension.py b/src/wrf/extension.py index 669d4f2..5747f64 100755 --- a/src/wrf/extension.py +++ b/src/wrf/extension.py @@ -1159,7 +1159,7 @@ def omp_get_nested(): return fomp_get_nested() -def omp_set_schedule(kind, modifier): +def omp_set_schedule(kind, modifier=0): """Set the schedule that is applied when *runtime* is used as schedule kind, by setting the value of the run-sched-var ICV. @@ -1182,7 +1182,8 @@ def omp_set_schedule(kind, modifier): modifier(:obj:`int`): An implementation specific value, depending on the choice for *kind*. This parameter is alternatively named - chunk_size in some OpenMP documentation. + chunk_size in some OpenMP documentation. Default is 0, which + means the OpenMP implementation will use its default value. Returns: diff --git a/src/wrf/util.py b/src/wrf/util.py index e245456..480f4f8 100644 --- a/src/wrf/util.py +++ b/src/wrf/util.py @@ -1249,13 +1249,13 @@ def _build_data_array(wrfnc, varname, timeidx, is_moving_domain, is_multifile, data = data[np.newaxis] attrs = OrderedDict() - for key, val in viewitems(var.__dict__): + for dkey, val in viewitems(var.__dict__): # scipy.io adds these but don't want them - if key in ("data", "_shape", "_size", "_typecode", "_attributes", + if dkey in ("data", "_shape", "_size", "_typecode", "_attributes", "maskandscale", "dimensions"): continue - _key = key if isinstance(key, str) else key.decode() + _dkey = dkey if isinstance(dkey, str) else dkey.decode() if isstr(val): _val = val else: @@ -1264,7 +1264,7 @@ def _build_data_array(wrfnc, varname, timeidx, is_moving_domain, is_multifile, else: _val = val - attrs[_key] = _val + attrs[_dkey] = _val dimnames = var.dimensions[-data.ndim:] From 16cbaf50ce4be4994dbf7cee496ab440e50050d7 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Wed, 10 Jan 2018 12:16:54 -0700 Subject: [PATCH 41/43] Fixed a segfault issue when writing the LCL and LFC values --- fortran/rip_cape.f90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fortran/rip_cape.f90 b/fortran/rip_cape.f90 index be05b79..c92be3f 100644 --- a/fortran/rip_cape.f90 +++ b/fortran/rip_cape.f90 @@ -710,7 +710,7 @@ SUBROUTINE DCAPECALC2D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& !$OMP facden, tmkenv, qvpenv, eslift, qvplift, buoy, benamin, & !$OMP benaccum, zrel, kmax, dz, elfound, & !$OMP kel, klfc, pavg, p2, p1, totthe, totqvp, totprs, & - !$OMP i, j, k, kpar, kpar1, kpar2, qvppari, tmkpari,p, pup, pdn, th, & + !$OMP i, j, k, kpar, kpar1, kpar2, qvppari, tmkpari, p, pup, pdn, th, & !$OMP pp1, pp2, ethmax, eth_temp, klev) SCHEDULE(runtime) DO j = 1,mjy DO i = 1,mix @@ -964,9 +964,9 @@ SUBROUTINE DCAPECALC2D(prs,tmk,qvp,ght,ter,sfp,cape,cin,& cape(i,j,mkzh) = cape(i,j,kpar1) cin(i,j,mkzh) = cin(i,j,kpar1) ! meters agl - cin(i,j,mkzh-1) = zrel(klcl) + ght_new(kpar,i,j) - ter(i,j) + cin(i,j,mkzh-1) = zrel(klcl) + ght_new(kpar1,i,j) - ter(i,j) ! meters agl - cin(i,j,mkzh-2) = zrel(klfc) + ght_new(kpar,i,j) - ter(i,j) + cin(i,j,mkzh-2) = zrel(klfc) + ght_new(kpar1,i,j) - ter(i,j) END DO END DO From abee4fc27c098b35b144eb98bf4332fa9305cb45 Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Wed, 10 Jan 2018 16:33:27 -0700 Subject: [PATCH 42/43] Updated documentation. Added OpenMP, performance tips, how to use the variable cache. --- doc/source/basic_usage.rst | 179 +++++++++++++++++- src/wrf/extension.py | 32 ++-- src/wrf/util.py | 3 - src/wrf/version.py | 2 +- test/ipynb/Doc_Examples.ipynb | 332 +++++++++++++++++++++++++--------- test/varcache.py | 36 ++++ 6 files changed, 474 insertions(+), 110 deletions(-) create mode 100644 test/varcache.py diff --git a/doc/source/basic_usage.rst b/doc/source/basic_usage.rst index 9a201c7..421aea4 100644 --- a/doc/source/basic_usage.rst +++ b/doc/source/basic_usage.rst @@ -1923,8 +1923,183 @@ Here is an example of the loop-and-fill technique: z_final[timeidx,:] = z[:] f.close() - - + +The *cache* Argument for :meth:`wrf.getvar` +********************************************* + +If you have read through the documentation, you may have noticed that the +:meth:`wrf.getvar` routine contains a *cache* argument. What is this for? + +Internally, if metadata is turned on, a variable is extracted from the NetCDF +file and its metadata is copied to form the result's metadata. Often this +variable is one of the computation's function arguments, so rather than +spend time extracting the variable again for the computation, it is placed +in a cache (dictionary) and passed on to the computational function. + +What isn't widely known is that this cache argument can also be supplied by +end users wishing to speed up their application. This can be useful in +situations where numerous calculations are being performed on the same +data set. For many algorithms, the cost to extract the arrays from the +NetCDF file is on par with the time to perform the calculation. If you are +computing numerous diagnostics, extracting the variables up front allows you +to only pay this extraction penalty once, rather than inside of each call +to :meth:`wrf.getvar`. + +The cache is nothing more than a dictionary where each key is the variable +name (e.g. "P") and the value is the :class:`xarray.DataArray` or +:class:`numpy.ndarray` variable. Creating the cache dictionary is easy, +since the :meth:`wrf.extract_vars` routine returns a dictionary for a +sequence of variables. + +.. note:: + + The *timeidx* parameter supplied to :meth:`extract_vars` + must be the same *timeidx* parameter that you plan to use for + :meth:`wrf.getvar`. Otherwise, it will crash with dimension mismatch errors. + +Some common variables that you can use to create an effective cache are: P, PB, +PH, PHB, T, QVAPOR, HGT, PSFC, U, V, W. + +Below is an example showing the same computations done with and without the +cache. The execution time is printed. The hardware used is a 2.8 GHz Intel Core +i7, which contains 4 CPU cores with 2 hyper threads (8 total threads). This +will be interpreted as 8 CPUs for OpenMP. + +.. code:: python + + from __future__ import print_function + + import time + from netCDF4 import Dataset + from wrf import getvar, ALL_TIMES, extract_vars + + # The first two files contain four times, the last file contains only one. + wrf_filenames = ["/path/to/wrfout_d02_2005-08-28_00:00:00", + "/path/to/wrfout_d02_2005-08-28_12:00:00", + "/path/to/wrfout_d02_2005-08-29_00:00:00"] + + wrfin = [Dataset(x) for x in wrf_filenames] + + start = time.time() + my_cache = extract_vars(wrfin, ALL_TIMES, ("P", "PSFC", "PB", "PH", "PHB", + "T", "QVAPOR", "HGT", "U", "V", + "W")) + end = time.time() + print ("Time taken to build cache: ", (end-start), "s") + + vars = ("avo", "eth", "cape_2d", "cape_3d", "ctt", "dbz", "mdbz", + "geopt", "helicity", "lat", "lon", "omg", "p", "pressure", + "pvo", "pw", "rh2", "rh", "slp", "ter", "td2", "td", "tc", + "theta", "tk", "tv", "twb", "updraft_helicity", "ua", "va", + "wa", "uvmet10", "uvmet", "z", "cfrac", "zstag", "geopt_stag") + + # No cache + start = time.time() + for var in vars: + v = getvar(wrfin, var, ALL_TIMES) + end = time.time() + no_cache_time = (end-start) + + print ("Time taken without variable cache: ", no_cache_time, "s") + + # With a cache + start = time.time() + for var in vars: + v = getvar(wrfin, var, ALL_TIMES, cache=my_cache) + end = time.time() + cache_time = (end-start) + + print ("Time taken with variable cache: ", cache_time, "s") + + improvement = ((no_cache_time-cache_time)/no_cache_time) * 100 + print ("The cache decreased computation time by: ", improvement, "%") + + +Result: +.. code:: none + Time taken to build cache: 0.28154706955 s + Time taken without variable cache: 11.0905270576 s + Time taken with variable cache: 8.25931215286 s + The cache decreased computation time by: 25.5282268378 % + +By removing the repeated extraction of common variables in the getvar routine, +for the single threaded case, the computation time has been reduced by +25.5% in the particular example. + +Things get more interesting when OpenMP is turned on, and set to use the +maximum number of processors (in this case 8 threads are used). + +.. code:: python + + from __future__ import print_function + + import time + from netCDF4 import Dataset + from wrf import (getvar, ALL_TIMES, extract_vars, + omp_set_num_threads, omp_get_num_procs) + + # The first two files contain four times, the last file contains only one. + wrf_filenames = ["/path/to/wrfout_d02_2005-08-28_00:00:00", + "/path/to/wrfout_d02_2005-08-28_12:00:00", + "/path/to/wrfout_d02_2005-08-29_00:00:00"] + + wrfin = [Dataset(x) for x in wrf_filenames] + + start = time.time() + my_cache = extract_vars(wrfin, ALL_TIMES, ("P", "PSFC", "PB", "PH", "PHB", + "T", "QVAPOR", "HGT", "U", "V", + "W")) + end = time.time() + print ("Time taken to build cache: ", (end-start), "s") + + omp_set_num_threads(omp_get_num_procs()) + + vars = ("avo", "eth", "cape_2d", "cape_3d", "ctt", "dbz", "mdbz", + "geopt", "helicity", "lat", "lon", "omg", "p", "pressure", + "pvo", "pw", "rh2", "rh", "slp", "ter", "td2", "td", "tc", + "theta", "tk", "tv", "twb", "updraft_helicity", "ua", "va", + "wa", "uvmet10", "uvmet", "z", "cfrac", "zstag", "geopt_stag") + + # No cache + start = time.time() + for var in vars: + v = getvar(wrfin, var, ALL_TIMES) + end = time.time() + no_cache_time = (end-start) + + print ("Time taken without variable cache: ", no_cache_time, "s") + + # With a cache + start = time.time() + for var in vars: + v = getvar(wrfin, var, ALL_TIMES, cache=my_cache) + end = time.time() + cache_time = (end-start) + + print ("Time taken with variable cache: ", cache_time, "s") + + improvement = ((no_cache_time-cache_time)/no_cache_time) * 100 + print ("The cache decreased computation time by: ", improvement, "%") + +Result: + +.. code:: none + + Time taken to build cache: 0.2700548172 s + Time taken without variable cache: 6.02652812004 s + Time taken with variable cache: 3.27777099609 s + The cache decreased computation time by: 45.6109565772 % + +In this example, 4 CPU cores (8 total threads) are used. When the cache is +used, the computation time drops by 45%, so almost half the time was spent +simply extracting variables from the NetCDF file. When compared to the +11.09 s needed to compute the single threaded case with no variable cache, the +computation time drops by roughly 70% (compared to 45% with 8 threads but +no cache). + +In summary, if you are computing a lot of diagnostic variables, consider using +the *cache* argument to improve performance, particularly if you want to +maximize your multithreaded performance with OpenMP. diff --git a/src/wrf/extension.py b/src/wrf/extension.py index 5747f64..e2e9e3c 100755 --- a/src/wrf/extension.py +++ b/src/wrf/extension.py @@ -617,7 +617,7 @@ def _cape(p_hpa, tk, qv, ht, ter, sfp, missing, i3dflag, ter_follow, cape_routine = dcapecalc3d else: cape_routine = dcapecalc2d - + # note that p_hpa, tk, qv, and ht have the vertical flipped result = cape_routine(p_hpa, tk, @@ -976,7 +976,7 @@ def omp_get_num_threads(): def omp_get_max_threads(): - """Return the maximum number of threads that can be used in a parallel + """Return the maximum number of threads that can be used in a parallel \ region. The omp_get_max_threads routine returns an upper bound on the number of @@ -997,7 +997,7 @@ def omp_get_max_threads(): def omp_get_thread_num(): - """Return the thread number, within the current team, of the + """Return the thread number, within the current team, of the \ calling thread. The omp_get_thread_num routine returns the thread number of the calling @@ -1041,7 +1041,7 @@ def omp_get_num_procs(): def omp_in_parallel(): - """Return 1 if the active-levels-var ICV is greater than zero; + """Return 1 if the active-levels-var ICV is greater than zero; \ otherwise, return 0. The effect of the omp_in_parallel routine is to return 1 if the current @@ -1063,8 +1063,8 @@ def omp_in_parallel(): def omp_set_dynamic(dynamic_threads): - """Enable or disable dynamic adjustment of the number of threads - available for the execution of subsequent parallel regions by setting the + """Enable or disable dynamic adjustment of the number of threads \ + available for the execution of subsequent parallel regions by setting the \ value of the dyn-var ICV. For implementations that support dynamic adjustment of the number of @@ -1092,7 +1092,7 @@ def omp_set_dynamic(dynamic_threads): def omp_get_dynamic(): - """Return the value of the dyn-var ICV, which determines whether + """Return the value of the dyn-var ICV, which determines whether \ dynamic adjustment of the number of threads is enabled or disabled. This routine returns 1 if dynamic adjustment of the number of threads @@ -1140,7 +1140,7 @@ def omp_set_nested(nested): def omp_get_nested(): - """Return the value of the nest-var ICV, which determines if nested + """Return the value of the nest-var ICV, which determines if nested \ parallelism is enabled or disabled This routine returns 1 if nested parallelism is enabled for the current @@ -1160,7 +1160,7 @@ def omp_get_nested(): def omp_set_schedule(kind, modifier=0): - """Set the schedule that is applied when *runtime* is used as + """Set the schedule that is applied when *runtime* is used as \ schedule kind, by setting the value of the run-sched-var ICV. The effect of this routine is to set the value of the run-sched-var ICV @@ -1221,8 +1221,8 @@ def omp_get_schedule(): def omp_get_thread_limit(): - """Return the maximum number of OpenMP threads available to participate in - the current contention group. + """Return the maximum number of OpenMP threads available to participate \ + in the current contention group. The omp_get_thread_limit routine returns the value of the thread-limit-var ICV. @@ -1241,7 +1241,7 @@ def omp_get_thread_limit(): def omp_set_max_active_levels(max_levels): - """Limit the number of nested active parallel regions on the device, + """Limit the number of nested active parallel regions on the device, \ by setting the max-active-levels-var ICV. The effect of this routine is to set the value of the max-active-levels-var @@ -1271,7 +1271,7 @@ def omp_set_max_active_levels(max_levels): def omp_get_max_active_levels(): - """Return the value of the max-active-levels-var ICV, which determines + """Return the value of the max-active-levels-var ICV, which determines \ the maximum number of nested active parallel regions on the device The omp_get_max_active_levels routine returns the value of the @@ -1311,7 +1311,7 @@ def omp_get_level(): def omp_get_ancestor_thread_num(level): - """Return, for a given nested level of the current thread, the thread + """Return, for a given nested level of the current thread, the thread \ number of the ancestor of the current thread. The omp_get_ancestor_thread_num routine returns the thread number of the @@ -1338,7 +1338,7 @@ def omp_get_ancestor_thread_num(level): def omp_get_team_size(level): - """Return, for a given nested level of the current thread, the size + """Return, for a given nested level of the current thread, the size \ of the thread team to which the ancestor or the current thread belongs The omp_get_team_size routine returns the size of the thread team to which @@ -1385,7 +1385,7 @@ def omp_get_active_level(): def omp_in_final(): - """Return 1 (True) if the routine is executed in a final task region; + """Return 1 (True) if the routine is executed in a final task region; \ otherwise, it returns 0 (False). Returns: diff --git a/src/wrf/util.py b/src/wrf/util.py index 480f4f8..22cc2d6 100644 --- a/src/wrf/util.py +++ b/src/wrf/util.py @@ -2232,9 +2232,6 @@ def extract_vars(wrfin, timeidx, varnames, method="cat", squeeze=True, negative integer, or :data:`wrf.ALL_TIMES` (an alias for None) to return all times in the file or sequence. The default is 0. - - is_moving (:obj:`bool`): A boolean type that indicates if the - sequence is a moving nest. method (:obj:`str`, optional): The aggregation method to use for sequences. Must be either 'cat' or 'join'. diff --git a/src/wrf/version.py b/src/wrf/version.py index c55bfe3..aebf1b8 100644 --- a/src/wrf/version.py +++ b/src/wrf/version.py @@ -1,2 +1,2 @@ -__version__ = "1.0.5" +__version__ = "1.1.0" diff --git a/test/ipynb/Doc_Examples.ipynb b/test/ipynb/Doc_Examples.ipynb index 1006a79..4b8d794 100644 --- a/test/ipynb/Doc_Examples.ipynb +++ b/test/ipynb/Doc_Examples.ipynb @@ -2,10 +2,7 @@ "cells": [ { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "# Cartopy Examples" ] @@ -13,11 +10,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "%matplotlib inline" @@ -27,9 +20,6 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false, - "deletable": true, - "editable": true, "scrolled": false }, "outputs": [], @@ -115,11 +105,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", @@ -203,9 +189,6 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false, - "deletable": true, - "editable": true, "scrolled": false }, "outputs": [], @@ -343,9 +326,6 @@ "cell_type": "code", "execution_count": null, "metadata": { - "collapsed": false, - "deletable": true, - "editable": true, "scrolled": false }, "outputs": [], @@ -411,11 +391,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", @@ -484,11 +460,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from __future__ import (absolute_import, division, print_function, unicode_literals)\n", @@ -568,11 +540,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from __future__ import print_function\n", @@ -606,11 +574,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from __future__ import print_function\n", @@ -631,10 +595,7 @@ }, { "cell_type": "markdown", - "metadata": { - "deletable": true, - "editable": true - }, + "metadata": {}, "source": [ "# Basemap Examples" ] @@ -642,11 +603,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "\n", @@ -704,11 +661,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", @@ -781,11 +734,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", @@ -921,11 +870,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from __future__ import print_function\n", @@ -960,11 +905,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from __future__ import print_function\n", @@ -986,11 +927,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "# SLP\n", @@ -1041,11 +978,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from __future__ import print_function\n", @@ -1079,11 +1012,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false, - "deletable": true, - "editable": true - }, + "metadata": {}, "outputs": [], "source": [ "from __future__ import print_function\n", @@ -1101,6 +1030,233 @@ "\n", "print (bounds)" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# OpenMP Routines" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import print_function\n", + "\n", + "from wrf import omp_enabled\n", + "\n", + "print(omp_enabled())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import print_function\n", + "\n", + "from wrf import omp_get_num_procs\n", + "\n", + "print(omp_get_num_procs())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import print_function\n", + "\n", + "from wrf import omp_set_num_threads, omp_get_max_threads\n", + "\n", + "omp_set_num_threads(4)\n", + "\n", + "print(omp_get_max_threads())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import print_function\n", + "\n", + "from wrf import omp_set_schedule, omp_get_schedule, OMP_SCHED_GUIDED\n", + "\n", + "omp_set_schedule(OMP_SCHED_GUIDED, 0)\n", + "\n", + "sched, modifier = omp_get_schedule()\n", + "\n", + "print(sched, modifier)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Loop and Fill Technique" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import print_function, division\n", + "\n", + "import numpy as np\n", + "from netCDF4 import Dataset\n", + "from wrf import getvar, ALL_TIMES\n", + "\n", + "filename_list = [\"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/wrfout_d02_2005-08-28_00:00:00\",\n", + " \"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/wrfout_d02_2005-08-28_12:00:00\", \n", + " \"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/wrfout_d02_2005-08-29_00:00:00\"]\n", + "\n", + "# Result shape (hardcoded for this example, modify as necessary)\n", + "result_shape = (9, 29, 96, 96)\n", + "\n", + "# Only need 4-byte floats\n", + "z_final = np.empty(result_shape, np.float32)\n", + "\n", + "# Modify this number if using more than 1 time per file\n", + "times_per_file = 4\n", + "\n", + "for timeidx in xrange(result_shape[0]):\n", + " # Compute the file index and the time index inside the file\n", + " fileidx = timeidx // times_per_file\n", + " file_timeidx = timeidx % times_per_file\n", + "\n", + " f = Dataset(filename_list[fileidx]) \n", + " z = getvar(f, \"z\", file_timeidx)\n", + "\n", + " z_final[timeidx,:] = z[:]\n", + " f.close()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Using the cache argument" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import print_function\n", + "\n", + "import time\n", + "from netCDF4 import Dataset\n", + "from wrf import getvar, ALL_TIMES, extract_vars\n", + "\n", + "wrf_filenames = [\"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/wrfout_d02_2005-08-28_00:00:00\",\n", + " \"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/wrfout_d02_2005-08-28_12:00:00\", \n", + " \"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/wrfout_d02_2005-08-29_00:00:00\"]\n", + "\n", + "wrfin = [Dataset(x) for x in wrf_filenames]\n", + "\n", + "start = time.time()\n", + "my_cache = extract_vars(wrfin, ALL_TIMES, (\"P\", \"PSFC\", \"PB\", \"PH\", \"PHB\", \"T\", \"QVAPOR\", \n", + " \"HGT\", \"U\", \"V\", \"W\"))\n", + "end = time.time()\n", + "print (\"Time taken to build cache: \", (end-start), \"s\")\n", + "\n", + "vars = (\"avo\", \"eth\", \"cape_2d\", \"cape_3d\", \"ctt\", \"dbz\", \"mdbz\", \n", + " \"geopt\", \"helicity\", \"lat\", \"lon\", \"omg\", \"p\", \"pressure\", \n", + " \"pvo\", \"pw\", \"rh2\", \"rh\", \"slp\", \"ter\", \"td2\", \"td\", \"tc\", \n", + " \"theta\", \"tk\", \"tv\", \"twb\", \"updraft_helicity\", \"ua\", \"va\", \n", + " \"wa\", \"uvmet10\", \"uvmet\", \"z\", \"cfrac\", \"zstag\", \"geopt_stag\")\n", + "\n", + "start = time.time()\n", + "for var in vars:\n", + " v = getvar(wrfin, var, ALL_TIMES)\n", + "end = time.time()\n", + "no_cache_time = (end-start)\n", + "\n", + "print (\"Time taken without variable cache: \", no_cache_time, \"s\")\n", + "\n", + "start = time.time()\n", + "for var in vars:\n", + " v = getvar(wrfin, var, ALL_TIMES, cache=my_cache)\n", + "end = time.time()\n", + "cache_time = (end-start)\n", + "\n", + "print (\"Time taken with variable cache: \", cache_time, \"s\")\n", + "\n", + "improvement = ((no_cache_time-cache_time)/no_cache_time) * 100 \n", + "print (\"The cache decreased computation time by: \", improvement, \"%\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Using the cache argument with OpenMP" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from __future__ import print_function\n", + "\n", + "import time\n", + "from netCDF4 import Dataset\n", + "from wrf import getvar, ALL_TIMES, extract_vars, omp_set_num_threads, omp_get_num_procs\n", + "\n", + "wrf_filenames = [\"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/wrfout_d02_2005-08-28_00:00:00\",\n", + " \"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/wrfout_d02_2005-08-28_12:00:00\", \n", + " \"/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/wrfout_d02_2005-08-29_00:00:00\"]\n", + "\n", + "wrfin = [Dataset(x) for x in wrf_filenames]\n", + "\n", + "start = time.time()\n", + "my_cache = extract_vars(wrfin, ALL_TIMES, (\"P\", \"PSFC\", \"PB\", \"PH\", \"PHB\", \"T\", \"QVAPOR\", \n", + " \"HGT\", \"U\", \"V\", \"W\"))\n", + "end = time.time()\n", + "print (\"Time taken to build cache: \", (end-start), \"s\")\n", + "\n", + "omp_set_num_threads(omp_get_num_procs())\n", + "\n", + "vars = (\"avo\", \"eth\", \"cape_2d\", \"cape_3d\", \"ctt\", \"dbz\", \"mdbz\", \n", + " \"geopt\", \"helicity\", \"lat\", \"lon\", \"omg\", \"p\", \"pressure\", \n", + " \"pvo\", \"pw\", \"rh2\", \"rh\", \"slp\", \"ter\", \"td2\", \"td\", \"tc\", \n", + " \"theta\", \"tk\", \"tv\", \"twb\", \"updraft_helicity\", \"ua\", \"va\", \n", + " \"wa\", \"uvmet10\", \"uvmet\", \"z\", \"cfrac\", \"zstag\", \"geopt_stag\")\n", + "\n", + "start = time.time()\n", + "for var in vars:\n", + " v = getvar(wrfin, var, ALL_TIMES)\n", + "end = time.time()\n", + "no_cache_time = (end-start)\n", + "\n", + "print (\"Time taken without variable cache: \", no_cache_time, \"s\")\n", + "\n", + "start = time.time()\n", + "for var in vars:\n", + " v = getvar(wrfin, var, ALL_TIMES, cache=my_cache)\n", + "end = time.time()\n", + "cache_time = (end-start)\n", + "\n", + "print (\"Time taken with variable cache: \", cache_time, \"s\")\n", + "\n", + "improvement = ((no_cache_time-cache_time)/no_cache_time) * 100 \n", + "print (\"The cache decreased computation time by: \", improvement, \"%\")\n", + "\n", + "omp_set_num_threads(1)" + ] } ], "metadata": { @@ -1119,7 +1275,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", - "version": "2.7.12" + "version": "2.7.13" } }, "nbformat": 4, diff --git a/test/varcache.py b/test/varcache.py new file mode 100644 index 0000000..1e0fe3d --- /dev/null +++ b/test/varcache.py @@ -0,0 +1,36 @@ +from __future__ import print_function + +import time +from netCDF4 import Dataset +from wrf import getvar, ALL_TIMES, extract_vars + +wrf_filenames = ["/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/wrfout_d02_2005-08-28_00:00:00", + "/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/wrfout_d02_2005-08-28_12:00:00", + "/Users/ladwig/Documents/wrf_files/wrf_vortex_multi/wrfout_d02_2005-08-29_00:00:00"] + +wrfin = [Dataset(x) for x in wrf_filenames] + +my_cache = extract_vars(wrfin, ALL_TIMES, ("P", "PB", "PH", "PHB", "T", "QVAPOR", "HGT", "U", "V", "W", "PSFC")) + +start = time.time() +for var in ("avo", "eth", "cape_2d", "cape_3d", "ctt", "dbz", "mdbz", + "geopt", "helicity", "lat", "lon", "omg", "p", "pressure", + "pvo", "pw", "rh2", "rh", "slp", "ter", "td2", "td", "tc", + "theta", "tk", "tv", "twb", "updraft_helicity", "ua", "va", + "wa", "uvmet10", "uvmet", "z", "cfrac", "zstag", "geopt_stag"): + v = getvar(wrfin, var, ALL_TIMES) +end = time.time() + +print ("Time taken without variable cache: ", (end-start)) + +start = time.time() +for var in ("avo", "eth", "cape_2d", "cape_3d", "ctt", "dbz", "mdbz", + "geopt", "helicity", "lat", "lon", "omg", "p", "pressure", + "pvo", "pw", "rh2", "rh", "slp", "ter", "td2", "td", "tc", + "theta", "tk", "tv", "twb", "updraft_helicity", "ua", "va", + "wa", "uvmet10", "uvmet", "z", "cfrac", "zstag", "geopt_stag"): + v = getvar(wrfin, var, ALL_TIMES, cache=my_cache) +end = time.time() + +print ("Time taken with variable cache: ", (end-start)) + From 497aeb508edd653f6c814ae69a4f8e69c244ba3a Mon Sep 17 00:00:00 2001 From: Bill Ladwig Date: Fri, 12 Jan 2018 14:58:30 -0700 Subject: [PATCH 43/43] Updated documentation Added sections for performance, what's new, FAQ, the citation. Fixed some typos. --- doc/source/basic_usage.rst | 174 +++++++++++----------- doc/source/citation.rst | 26 ++++ doc/source/conf.py | 17 ++- doc/source/faq.rst | 7 + doc/source/index.rst | 3 +- doc/source/new.rst | 42 ++++++ doc/source/tutorials/tutorial_03_2018.rst | 11 +- doc/source/user_api/index.rst | 9 +- src/wrf/extension.py | 4 +- src/wrf/g_cloudfrac.py | 16 +- test/mocktest.py | 44 ++++++ test/test_filevars.py | 27 +++- test/utests.py | 87 ++++++----- 13 files changed, 318 insertions(+), 149 deletions(-) create mode 100644 doc/source/citation.rst create mode 100644 test/mocktest.py diff --git a/doc/source/basic_usage.rst b/doc/source/basic_usage.rst index 421aea4..145eed4 100644 --- a/doc/source/basic_usage.rst +++ b/doc/source/basic_usage.rst @@ -4,30 +4,32 @@ How To Use Introduction --------------- -The API for wrf-python can be summarized as a variable extraction/computation -routine, several interpolation routines, and some plotting utilities. +The API for wrf-python can be summarized as a variable computation/extraction +routine, several interpolation routines, and a few plotting helper utilities. The API is kept as simple as possible to help minimize the -programming burden on new users, students, and scientists. In the future, we -plan to extend xarray for programmers desiring a more object oriented API, +learning curve for new programmers, students, and scientists. In the future, +we plan to extend xarray for programmers desiring a more object oriented API, but this remains a work in progress. The five most commonly used routines can be summarized as: -- **wrf.getvar**: The routine that extracts WRF NetCDF variables or - computes diagnostic variables. This is the routine you will use most often. +- :meth:`wrf.getvar` - Extracts WRF-ARW NetCDF variables and + computes diagnostic variables that WRF does not compute (e.g. storm + relative helicity). This is the routine that you will use most often. -- **wrf.interplevel**: Interpolates a three-dimensional field to a horizontal - plane at a specified level using simple (fast) linear interpolation. +- :meth:`wrf.interplevel` - Interpolates a three-dimensional field to a + horizontal plane at a specified level using simple (fast) linear + interpolation (e.g. 850 hPa temperature). -- **wrf.vertcross**: Interpolates a three-dimensional field to a vertical plane - through a user-specified horizontal line (i.e. a cross section). +- :meth:`wrf.vertcross` - Interpolates a three-dimensional field to a vertical + plane through a user-specified horizontal line (i.e. a cross section). -- **wrf.interpline**: Interpolates a two-dimensional field to a user-specified - line. +- :meth:`wrf.interpline` - Interpolates a two-dimensional field to a + user-specified line. -- **wrf.vinterp**: Interpolates a three-dimensional field to user-specified - 'surface' levels (e.g. theta-e levels). This is a smarter, but slower, - version of wrf.interplevel. +- :meth:`wrf.vinterp` - Interpolates a three-dimensional field to + user-specified 'surface' levels (e.g. theta-e levels). This is a smarter, + albeit slower, version of :meth:`wrf.interplevel`. Basic Usage ---------------- @@ -38,8 +40,8 @@ Computing Diagnostic Variables ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The primary use for the :meth:`wrf.getvar` function is to return diagnostic -variables that require a calculation, since WRF does not produce these variables -natively. These diagnostics include CAPE, storm relative helicity, +variables that require a calculation, since WRF does not produce these +variables natively. These diagnostics include CAPE, storm relative helicity, omega, sea level pressure, etc. A table of all available diagnostics can be found here: :ref:`diagnostic-table`. @@ -383,9 +385,9 @@ Result: pole_lon=0.0) -Note how the 'Time' dimension was replaced with the 'file' dimension, due to the -numpy's automatic squeezing of the single 'Time' dimension. To maintain the -'Time' dimension, set the *squeeze* parameter to False. +Note how the 'Time' dimension was replaced with the 'file' dimension, due to +numpy's automatic squeezing of the single element 'Time' dimension. To maintain +the 'Time' dimension, set the *squeeze* parameter to False. .. code-block:: python @@ -1642,6 +1644,7 @@ Result: ] +.. _using_omp: Using OpenMP ------------------------- @@ -1664,7 +1667,7 @@ scheduler should be sufficient. Verifying that OpenMP is Enabled -************************************* +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ To take advantage of the performance improvements offered by OpenMP, wrf-python needs to have been compiled with OpenMP features enabled. The example below @@ -1687,7 +1690,7 @@ Result: Determining the Number of Available Processors -*************************************************** +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The example below shows how you can get the maximum number of processors that are available on your system. @@ -1709,19 +1712,19 @@ Result: Specifying the Number of Threads -************************************* +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ To enable multicore support via OpenMP, specifying the maximum number of OpenMP threads (i.e. CPU cores) is the only step that you need to take. In the example below, :meth:`wrf.omp_set_num_threads` is used to set the -maximum number of threads to use, and :meth:`wrf.omp_get_max_threads` is -get to get (and print) the maximum number of threads used. +maximum number of threads to use, and :meth:`wrf.omp_get_max_threads` is used +to retrieve (and print) the maximum number of threads used. .. note:: - Although there is an OpenMP library named :meth:`wrf.omp_get_num_threads`, - this method will always returns 1 when called from the sequential part of + Although there is an OpenMP routine named :meth:`wrf.omp_get_num_threads`, + this routine will always return 1 when called from the sequential part of the program. Use :meth:`wrf.omp_get_max_threads` to return the value set by :meth:`wrf.omp_set_num_threads`. @@ -1743,7 +1746,7 @@ Result: 4 Setting a Different Scheduler Type -************************************** +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ When an OpenMP directive is encountered in the Fortran code, a scheduler is used to determine how the work is divided among the threads. All of the @@ -1811,40 +1814,27 @@ value of 1, which is different than the 0 that was supplied to the :meth:`wrf.omp_set_schedule` routine. This is because the 0 tells OpenMP to use its own default value for the scheduler, which is 1 for this type of scheduler. - -Performance Note -****************** - -If you have enabled multicore support with OpenMP, you may have noticed that -the routines do not scale linearly with the number of CPU cores added. One main -reason is that the computational routines are already fairly efficient and -vectorize well, so for many grid sizes, the time it takes to extract the -variables is on par with the time required to compute the diagnostic with a -single CPU core. Adding more CPU cores will decrease the time needed to do the -computation, but total performance will still be limited by the time it takes -to extract the variables from the NetCDF file. For local testing, diminishing -returns were seen after 4 CPU cores, but this will largely depend on the -hardware used and grid size for your WRF run. - +.. _performance: Performance Tips -------------------- -Memory Issues and :data:`wrf.ALL_TIMES` -****************************************** +Memory Issues with :data:`wrf.ALL_TIMES` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The use of :data:`wrf.ALL_TIMES` for the timeidx to :meth:`wrf.getvar` is -convenient for computing diagnostic variables across multiple files/times, but -there is something that users should be aware of. When :data:`wrf.ALL_TIMES` is -set as the *timeidx* argument, all arrays used in the computation are extracted -for all times before the computation is started. This can cause serious memory -issues on smaller hardware systems like laptops. +The use of :data:`wrf.ALL_TIMES` for the *timeidx* parameter to +:meth:`wrf.getvar` is convenient for computing diagnostic variables across +multiple files/times, but there is something that users should be aware of. +When :data:`wrf.ALL_TIMES` is set as the *timeidx* argument, all arrays used +in the computation are extracted for all times before the computation +is started. This can cause serious memory issues on smaller hardware systems +like laptops. In this example, the user wants to use a data set that is 289 x 39 x 300 x 300 and compute z for the entire data set. The user is using a laptop with -16 GB of memory. +8 GB of memory. -.. code:: python +.. code-block:: python from netCDF4 import Dataset from wrf import getvar, ALL_TIMES @@ -1858,32 +1848,40 @@ In wrf-python, all of the computational routines use 8-byte REAL variables so that both the 4-byte and 8-byte version of WRF output can be used. The calculation for z extracts three variables (P, PHB, and HGT) and returns a fourth array (RESULT). The RESULT will get cut in half to 4-byte REALs -after the computation, but need 8-byte REAL when the result is computed. +after the computation, but needs an 8-byte REAL when the result is computed. Let's look at the approximate amount memory needed: -P: 289 x 39 x 300 x 300 x 8 = 8,115,120,000 bytes (~8 GB!) -PHB: 289 x 39 x 300 x 300 x 8 = 8,115,120,000 bytes (~8 GB!) -HGT: 289 x 300 x 300 x 8 = 208,080,000 (~208 MB) -RESULT: 289 x 39 x 300 x 300 x 8 = 8,115,120,000 bytes (~8 GB!) +**P**: 289 x 39 x 300 x 300 x 8 = 8,115,120,000 bytes (~8 GB!) + +**PHB**: 289 x 39 x 300 x 300 x 8 = 8,115,120,000 bytes (~8 GB!) + +**HGT**: 289 x 300 x 300 x 8 = 208,080,000 (~208 MB) + +**RESULT**: 289 x 39 x 300 x 300 x 8 = 8,115,120,000 bytes (~8 GB!) Yikes! So, in order to do this calculation using :data:`wrf.ALL_TIMES` as -the timeidx, over 24.2 GB are needed for this one calculation. When the laptop -runs out of memory, it begins using the hard drive for swap memory, which runs -hundreds of times slower than real memory. +the *timeidx*, over 24.2 GB are needed for this one calculation. When the +laptop runs out of memory, it begins using the hard drive for swap memory, +which runs hundreds of times slower than real memory. To fix this situation, it is better to allocate the output array yourself and -run the calculation for each time step in a loop. The required memory -requirements change to: +run the calculation for each time step in a loop +("loop-and-fill"). The required memory requirements change to: -(Only need to store the result in a 4-byte REAL) -FINAL_RESULT: 289 x 39 x 300 x 300 x 4 = 4,057560,000 bytes (~4 GB) +(Note: only need to store the result in a 4-byte REAL) -(The numbers below are for each loop iteration) -P: 39 x 300 x 300 x 8 = 28,080,000 bytes (~28 MB) -PHB: 39 x 300 x 300 x 8 = 28,080,000 bytes (~28 MB) -HGT: 300 x 300 x 8 = 720,000 bytes (720 KB) -RESULT: 39 x 300 x 300 x 8 = 28,080,000 bytes (~28 MB) +**FINAL_RESULT**: 289 x 39 x 300 x 300 x 4 = 4,057560,000 bytes (~4 GB) + +(Note: the numbers below are for each loop iteration) + +**P**: 39 x 300 x 300 x 8 = 28,080,000 bytes (~28 MB) + +**PHB**: 39 x 300 x 300 x 8 = 28,080,000 bytes (~28 MB) + +**HGT**: 300 x 300 x 8 = 720,000 bytes (720 KB) + +**RESULT**: 39 x 300 x 300 x 8 = 28,080,000 bytes (~28 MB) Since the memory for the computation is deleted after each loop iteration, the total memory usage drops to approximately 4.1 GB. @@ -1892,9 +1890,9 @@ The moral of the story is that you need to make sure that your system has enough memory to extract everything it needs up front if you want to use :data:`wrf.ALL_TIMES`, otherwise it is better to "loop-and-fill" yourself. -Here is an example of the loop-and-fill technique: +Here is an example of the "loop-and-fill" technique: -.. code:: python +.. code-block:: python from __future__ import print_function, division @@ -1904,7 +1902,7 @@ Here is an example of the loop-and-fill technique: filename_list = ["/path/to/file1", "/path/to/file2",...] - # Result shape (hardcoded for this example) + # Result shape (hard coded for this example) result_shape = (289, 39, 300, 300) # Only need 4-byte floats @@ -1913,7 +1911,7 @@ Here is an example of the loop-and-fill technique: # Modify this number if using more than 1 time per file times_per_file = 1 - for timeidx in xrange(result_shape[0]): + for timeidx in range(result_shape[0]): # Compute the file index and the time index inside the file fileidx = timeidx // times_per_file file_timeidx = timeidx % times_per_file @@ -1926,7 +1924,7 @@ Here is an example of the loop-and-fill technique: The *cache* Argument for :meth:`wrf.getvar` -********************************************* +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you have read through the documentation, you may have noticed that the :meth:`wrf.getvar` routine contains a *cache* argument. What is this for? @@ -1940,10 +1938,10 @@ in a cache (dictionary) and passed on to the computational function. What isn't widely known is that this cache argument can also be supplied by end users wishing to speed up their application. This can be useful in situations where numerous calculations are being performed on the same -data set. For many algorithms, the cost to extract the arrays from the -NetCDF file is on par with the time to perform the calculation. If you are -computing numerous diagnostics, extracting the variables up front allows you -to only pay this extraction penalty once, rather than inside of each call +data set. For many algorithms, the time needed to extract the arrays from the +NetCDF file is on par with the time needed to perform the calculation. If you +are computing numerous diagnostics, extracting the variables up front allows +you to only pay this extraction penalty once, rather than inside of each call to :meth:`wrf.getvar`. The cache is nothing more than a dictionary where each key is the variable @@ -1961,12 +1959,12 @@ sequence of variables. Some common variables that you can use to create an effective cache are: P, PB, PH, PHB, T, QVAPOR, HGT, PSFC, U, V, W. -Below is an example showing the same computations done with and without the +Below is an example showing the same computation done with and without the cache. The execution time is printed. The hardware used is a 2.8 GHz Intel Core i7, which contains 4 CPU cores with 2 hyper threads (8 total threads). This will be interpreted as 8 CPUs for OpenMP. -.. code:: python +.. code-block:: python from __future__ import print_function @@ -2018,21 +2016,21 @@ will be interpreted as 8 CPUs for OpenMP. Result: -.. code:: none +.. code-block:: none Time taken to build cache: 0.28154706955 s Time taken without variable cache: 11.0905270576 s Time taken with variable cache: 8.25931215286 s The cache decreased computation time by: 25.5282268378 % -By removing the repeated extraction of common variables in the getvar routine, -for the single threaded case, the computation time has been reduced by -25.5% in the particular example. +By removing the repeated extraction of common variables in the +:meth:`wrf.getvar` routine, for the single threaded case, the computation +time has been reduced by 25.5% in this particular example. -Things get more interesting when OpenMP is turned on, and set to use the +Things get more interesting when OpenMP is turned on and set to use the maximum number of processors (in this case 8 threads are used). -.. code:: python +.. code-block:: python from __future__ import print_function @@ -2086,7 +2084,7 @@ maximum number of processors (in this case 8 threads are used). Result: -.. code:: none +.. code-block:: none Time taken to build cache: 0.2700548172 s Time taken without variable cache: 6.02652812004 s diff --git a/doc/source/citation.rst b/doc/source/citation.rst new file mode 100644 index 0000000..89c35c4 --- /dev/null +++ b/doc/source/citation.rst @@ -0,0 +1,26 @@ +.. _citation: + +Citation +================= + +WRF-Python has a Digital Object Identifier (DOI), which is a persistent +identifier for web-based resources. The wrf-python DOI, when used in URL form, +https://doi.org/10.5065/D6W094P1, provides a persistent link to the wrf-python +Github page. The benefit of DOIs is that they are widely accepted by academic +publishers as citable locators for scholarly objects. + +If you author a paper that involves data analysis with wrf-python, or +visualizations created with wrf-python, we would like to ask you to please +cite wrf-python. This helps us better understand the impact of the software on +the scientific community, which in turns helps us maintain support for the +effort. + +You can cite wrf-python using the following citation: + +.. code-block:: none + + Ladwig, W. (2017). wrf-python (Version x.x.x) [Software]. Boulder, Colorado: UCAR/NCAR. https://doi.org/10.5065/D6W094P1 + +.. note:: + + The version number x.x.x should be set to the version of wrf-python that you are using. \ No newline at end of file diff --git a/doc/source/conf.py b/doc/source/conf.py index 76aa4d0..b248434 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -23,14 +23,24 @@ except ImportError: class Mock(MagicMock): @classmethod def __getattr__(cls, name): - return Mock() + return MagicMock() MOCK_MODULES = ["numpy", "numpy.ma", "xarray", "cartopy", "pandas", "matplotlib", "netCDF4", "mpl_toolkits.basemap", "wrf._wrffortran"] sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES) -consts = {"DEFAULT_FILL" : 9.9692099683868690E36} +consts = {"DEFAULT_FILL" : 9.9692099683868690E36, + "DEFAULT_FILL_INT8" : -127, + "DEFAULT_FILL_INT16" : -32767, + "DEFAULT_FILL_INT32" : -2147483647, + "DEFAULT_FILL_INT64" : -9223372036854775806, + "DEFAULT_FILL_FLOAT" : 9.9692099683868690E36, + "DEFAULT_FILL_DOUBLE" : 9.9692099683868690E36, + "fomp_sched_static" : 1, + "fomp_sched_dynamic" : 2, + "fomp_sched_guided" : 3, + "fomp_sched_auto" : 4} class MockWrfConstants(object): def __init__(self): @@ -40,6 +50,8 @@ def mock_asscalar(val): return float(val) sys.modules["wrf._wrffortran"].wrf_constants = MockWrfConstants() +sys.modules["wrf._wrffortran"].omp_constants = MockWrfConstants() + sys.modules["numpy"].asscalar = mock_asscalar @@ -106,6 +118,7 @@ author = u'Bill Ladwig' # built documents. # # The short X.Y version. + import wrf version = wrf.__version__ # The full version, including alpha/beta/rc tags. diff --git a/doc/source/faq.rst b/doc/source/faq.rst index 74468d0..2c1cd28 100644 --- a/doc/source/faq.rst +++ b/doc/source/faq.rst @@ -61,6 +61,13 @@ In a future release of wrf-python, direct support for Dataset objects will be added and this will no longer be necessary. +Why is wrf-python taking hours to run? +--------------------------------------------- + +The most likely culprit is insufficient memory for the calculation you are +trying to perform. + +See :ref:`performance` for more information. diff --git a/doc/source/index.rst b/doc/source/index.rst index e0ca1e0..f43090a 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -45,8 +45,9 @@ Documentation ./api ./faq ./support + ./citation ./license - ./tutorial_03_2018 + ./tutorial Indices and tables diff --git a/doc/source/new.rst b/doc/source/new.rst index aec8cbe..e2ee510 100644 --- a/doc/source/new.rst +++ b/doc/source/new.rst @@ -4,6 +4,48 @@ What's New Releases ------------- +v1.1.0 +^^^^^^^^^^^^^^ + +- Release 1.1.0 +- Computational routines now support multiple cores using OpenMP. See + :ref:`using_omp` for details on how to use this new feature. +- The CAPE routines should be noticeably faster, even in the single threaded + case (thank you supreethms1809!). +- :meth:`wrf.getvar` now works correctly with non-gridded NetCDF variables +- The cloud fraction diagnostic has changed: + - Users can now select their own cloud threshold levels, and can choose + between a vertical coordinate defined as height (AGL), height (MSL), or + pressure. + - The default vertical coordinate type has been changed to be height (AGL). + This ensures that clouds appear over mountainous regions. If you need + the old behavior, set the *vert_type* argument to 'pressure'. + - Fixed a bug involving the cloud threshold search algorithm, where if the + surface was higher than the threshold for a cloud level, the algorithm + would use whatever was there before (uninitialized variable bug). This + caused some interesting visualization issues when plotted. Now, whenever + the surface is above a cloud level threshold, a fill value is used to + indicate that data is unavailable for that location. +- The cartopy object for LambertConformal should now work correctly in the + southern hemisphere. +- Fixed a bug with the PolarStereographic projection missing a geobounds + argument (thank you hanschen!). +- Renamed the modules containing the 'get_product' routines used + by :meth:`wrf.getvar` to avoid naming conflicts with the raw computational + routine names. Users should be using :meth:`wrf.getvar` instead of these + routines, but for those that imported the 'get_product' routines + directly, you will need to modify your code. +- Fixed a uniqueness issue with the internal coordinate cache that was causing + crashes when input data is changed to a different file in a jupyter notebook + cell. +- Added code to better support building wheels on Windows (thank you letmaik!) +- Improved support for scipy.io.netcdf objects. +- Added a new 'zstag' diagnostic that returns the height values for the + vertically staggered grid. +- A DOI is now available for wrf-python. Please cite wrf-python if you are + using it for your research. (See :ref:`citation`) + + v1.0.5 ^^^^^^^^^^^^^^ diff --git a/doc/source/tutorials/tutorial_03_2018.rst b/doc/source/tutorials/tutorial_03_2018.rst index 3f5ec39..dfaea94 100644 --- a/doc/source/tutorials/tutorial_03_2018.rst +++ b/doc/source/tutorials/tutorial_03_2018.rst @@ -1,5 +1,5 @@ -WRF Tutorial 2018 -===================== +WRF-Python Tutorial 2018 +========================= NCAR will be providing a four hour tutorial for wrf-python on Wednesday, March 7, 2018. The tutorial is free, but seating is limited to only 16 students, so @@ -15,9 +15,9 @@ Boulder, Colorado Overview -------------- -This tutorial provides an introduction to wrf-python. The tutorial is friendly -for new users of wrf-python, but this is not an introduction to the Python -programming language (see Prerequisites below). Due to limited seating, if you +This tutorial provides an introduction to wrf-python. The tutorial is beginner +friendly for new users of wrf-python, but this is not an introduction to the Python +programming language (see :ref:`prereq`). Due to limited seating, if you do not have any previous experience with Python, please do not register for this tutorial. @@ -47,6 +47,7 @@ Registration consists of a brief survey, which will help give the instructors a brief overview of your background and will help tailor the tutorial to your expectations. +.. _prereq: Prerequisites --------------- diff --git a/doc/source/user_api/index.rst b/doc/source/user_api/index.rst index 587d429..0832ca5 100644 --- a/doc/source/user_api/index.rst +++ b/doc/source/user_api/index.rst @@ -257,6 +257,7 @@ in one place. wrf.disable_pyngl wrf.set_cache_size wrf.get_cache_size + wrf.omp_enabled Miscellaneous Routines @@ -322,7 +323,7 @@ use a single point for an (x, y) or (lat, lon) location. wrf.CoordPair CoordPair Methods -~~~~~~~~~~~~~~~~~~~~~~~ +************************ .. autosummary:: :nosignatures: @@ -349,7 +350,7 @@ The classes below are used to hold the projection information in the 'projection' entry within a :attr:`xarray.DataArray.attrs` attribute. Projection Base Class -~~~~~~~~~~~~~~~~~~~~~~~~ +****************************** The base class for all map projection types. @@ -360,7 +361,7 @@ The base class for all map projection types. wrf.WrfProj Projection Base Class Methods -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*********************************** The class methods for all projection types. @@ -378,7 +379,7 @@ The class methods for all projection types. Projection Subclasses -~~~~~~~~~~~~~~~~~~~~~~~~ +***************************** See :class:`wrf.WrfProj` for methods and attributes. diff --git a/src/wrf/extension.py b/src/wrf/extension.py index e2e9e3c..3862c85 100755 --- a/src/wrf/extension.py +++ b/src/wrf/extension.py @@ -5,7 +5,7 @@ import numpy as np from .constants import Constants, default_fill -from ._wrffortran import (dcomputetk, dinterp3dz, dinterp2dxy, dinterp1d, +from wrf._wrffortran import (dcomputetk, dinterp3dz, dinterp2dxy, dinterp1d, dcomputeseaprs, dfilter2d, dcomputerh, dcomputeuvmet, dcomputetd, dcapecalc2d, dcapecalc3d, dcloudfrac2, wrfcttcalc, calcdbz, dcalrelhl, dcalcuh, dcomputepv, @@ -40,7 +40,7 @@ from .specialdec import (uvmet_left_iter, cape_left_iter, class DiagnosticError(Exception): """Raised when an error occurs in a diagnostic routine.""" def __init__(self, message=None): - """Initialize a :class:`wrf.DiagnosticError` objection. + """Initialize a :class:`wrf.DiagnosticError` object. Args: diff --git a/src/wrf/g_cloudfrac.py b/src/wrf/g_cloudfrac.py index 10123e4..2417dfa 100644 --- a/src/wrf/g_cloudfrac.py +++ b/src/wrf/g_cloudfrac.py @@ -32,7 +32,7 @@ def get_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True, 2000 m <= mid_cloud < 6000 m 6000 m <= high_cloud - For 'pres', the default cloud levels are defined as: + For 'pressure', the default cloud levels are defined as: 97000 Pa <= low_cloud < 80000 Pa 80000 Pa <= mid_cloud < 45000 Pa @@ -40,16 +40,16 @@ def get_cloudfrac(wrfin, timeidx=0, method="cat", squeeze=True, Note that the default low cloud levels are chosen to exclude clouds near the surface (fog). If you want fog included, set - *low_thresh* to ~99500 Pa if *vert_type* is set to 'pres', or 15 m if using - 'height_msl' or 'height_agl'. Keep in mind that the lowest mass grid points - are slightly above the ground, and in order to find clouds, the + *low_thresh* to ~99500 Pa if *vert_type* is set to 'pressure', or 15 m if + using 'height_msl' or 'height_agl'. Keep in mind that the lowest mass grid + points are slightly above the ground, and in order to find clouds, the *low_thresh* needs to be set to values that are slightly greater than (less than) the lowest height (pressure) values. - When using 'pres' or 'height_agl' for *vert_type*, there is a possibility - that the lowest WRF level will be higher than the low_cloud or mid_cloud - threshold, particularly for mountainous regions. When this happens, a - fill value will be used in the output. + When using 'pressure' or 'height_agl' for *vert_type*, there is a + possibility that the lowest WRF level will be higher than the low_cloud or + mid_cloud threshold, particularly for mountainous regions. When this + happens, a fill value will be used in the output. This functions extracts the necessary variables from the NetCDF file object in order to perform the calculation. diff --git a/test/mocktest.py b/test/mocktest.py new file mode 100644 index 0000000..211936f --- /dev/null +++ b/test/mocktest.py @@ -0,0 +1,44 @@ +import sys +import os + +try: + from unittest.mock import MagicMock +except ImportError: + from mock import Mock as MagicMock + +class Mock(MagicMock): + @classmethod + def __getattr__(cls, name): + return Mock() + +MOCK_MODULES = ["numpy", "numpy.ma", "xarray", "cartopy", + "pandas", "matplotlib", "netCDF4", "mpl_toolkits.basemap", + "wrf._wrffortran"] +sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES) + +consts = {"DEFAULT_FILL" : 9.9692099683868690E36, + "DEFAULT_FILL_INT8" : -127, + "DEFAULT_FILL_INT16" : -32767, + "DEFAULT_FILL_INT32" : -2147483647, + "DEFAULT_FILL_INT64" : -9223372036854775806, + "DEFAULT_FILL_FLOAT" : 9.9692099683868690E36, + "DEFAULT_FILL_DOUBLE" : 9.9692099683868690E36, + "fomp_sched_static" : 1, + "fomp_sched_dynamic" : 2, + "fomp_sched_guided" : 3, + "fomp_sched_auto" : 4} + +class MockWrfConstants(object): + def __init__(self): + self.__dict__ = consts + +def mock_asscalar(val): + return float(val) + +sys.modules["wrf._wrffortran"].wrf_constants = MockWrfConstants() +sys.modules["wrf._wrffortran"].omp_constants = MockWrfConstants() + +sys.modules["numpy"].asscalar = mock_asscalar + +import wrf +print (wrf.get_coord_pairs.__doc__) diff --git a/test/test_filevars.py b/test/test_filevars.py index 9684596..dc816c3 100644 --- a/test/test_filevars.py +++ b/test/test_filevars.py @@ -23,21 +23,46 @@ class WRFFileVarsTest(ut.TestCase): def make_test(ncfiles, varname): def test(self): + #import time + #very_start = time.time() + #start = time.time() t1 = getvar(ncfiles, varname, 0) + + #end = time.time() + #print ("t1: ", start-end) + #start = time.time() t2 = getvar(ncfiles, varname, 0, meta=False) + #end = time.time() + #print ("t2: ", start-end) + #start = time.time() t3 = getvar(ncfiles, varname, ALL_TIMES) + #end = time.time() + #print ("t3: ", start-end) + #start = time.time() t4 = getvar(ncfiles, varname, ALL_TIMES, meta=False) + #end = time.time() + #print ("t4: ", start-end) + #start = time.time() t5 = getvar(ncfiles, varname, ALL_TIMES, method="join") + #end = time.time() + #print ("t5: ", start-end) + #start = time.time() t6 = getvar(ncfiles, varname, ALL_TIMES, method="join", meta=False) + #end = time.time() + #print ("t6: ", start-end) + #start = time.time() + #print ("Total Time: ", (end-start)) return test if __name__ == "__main__": from netCDF4 import Dataset - ncfiles = [Dataset(x) for x in TEST_FILES] + #import scipy.io + #ncfiles = [scipy.io.netcdf.netcdf_file(x) for x in TEST_FILES] + file_vars = ncfiles[0].variables.keys() ignore_vars = [] diff --git a/test/utests.py b/test/utests.py index 5f3ede9..9c9ba12 100644 --- a/test/utests.py +++ b/test/utests.py @@ -93,45 +93,48 @@ def make_test(varname, wrf_in, referent, multi=False, repeat=3, pynio=False): multiproduct = varname in ("uvmet", "uvmet10", "cape_2d", "cape_3d", "cfrac") + # These varnames don't have NCL functions to test against + ignore_referent = ("zstag", "geopt_stag") - if not multi: - ref_vals = refnc.variables[varname][:] - else: - data = refnc.variables[varname][:] - if (varname != "uvmet" and varname != "uvmet10" - and varname != "cape_2d" and varname != "cape_3d"): - new_dims = [repeat] + [x for x in data.shape] - elif (varname == "uvmet" or varname == "uvmet10" - or varname == "cape_3d"): - new_dims = [2] + [repeat] + [x for x in data.shape[1:]] - elif (varname == "cape_2d"): - new_dims = [4] + [repeat] + [x for x in data.shape[1:]] - elif (varname == "cfrac"): - new_dims = [3] + [repeat] + [x for x in data.shape[1:]] - - - masked=False - if (isinstance(data, ma.core.MaskedArray)): - masked=True - - if not masked: - ref_vals = np.zeros(new_dims, data.dtype) + if varname not in ignore_referent: + if not multi: + ref_vals = refnc.variables[varname][:] else: - ref_vals = ma.asarray(np.zeros(new_dims, data.dtype)) - - for i in xrange(repeat): - if not multiproduct: - ref_vals[i,:] = data[:] + data = refnc.variables[varname][:] + if (varname != "uvmet" and varname != "uvmet10" + and varname != "cape_2d" and varname != "cape_3d"): + new_dims = [repeat] + [x for x in data.shape] + elif (varname == "uvmet" or varname == "uvmet10" + or varname == "cape_3d"): + new_dims = [2] + [repeat] + [x for x in data.shape[1:]] + elif (varname == "cape_2d"): + new_dims = [4] + [repeat] + [x for x in data.shape[1:]] + elif (varname == "cfrac"): + new_dims = [3] + [repeat] + [x for x in data.shape[1:]] + + + masked=False + if (isinstance(data, ma.core.MaskedArray)): + masked=True - if masked: - ref_vals.mask[i,:] = data.mask[:] - + if not masked: + ref_vals = np.zeros(new_dims, data.dtype) else: - for prod in xrange(ref_vals.shape[0]): - ref_vals[prod,i,:] = data[prod,:] - + ref_vals = ma.asarray(np.zeros(new_dims, data.dtype)) + + for i in xrange(repeat): + if not multiproduct: + ref_vals[i,:] = data[:] + if masked: - ref_vals.mask[prod,i,:] = data.mask[prod,:] + ref_vals.mask[i,:] = data.mask[:] + + else: + for prod in xrange(ref_vals.shape[0]): + ref_vals[prod,i,:] = data[prod,:] + + if masked: + ref_vals.mask[prod,i,:] = data.mask[prod,:] if (varname == "tc"): my_vals = getvar(in_wrfnc, "temp", timeidx=timeidx, units="c") @@ -176,6 +179,10 @@ def make_test(varname, wrf_in, referent, multi=False, repeat=3, pynio=False): #print np.amax(np.abs(to_np(cape_3d[0,:]) - ref_vals[0,:])) nt.assert_allclose(to_np(cape_3d), ref_vals, tol, atol) + elif (varname == "zstag" or varname == "geopt_stag"): + v = getvar(in_wrfnc, varname, timeidx=timeidx) + # For now, only make sure it runs without crashing since no NCL + # to compare with yet. else: my_vals = getvar(in_wrfnc, varname, timeidx=timeidx) tol = 2/100. @@ -274,6 +281,8 @@ def make_interp_test(varname, wrf_in, referent, multi=False, p = getvar(in_wrfnc, "pressure", timeidx=timeidx) pivot_point = CoordPair(hts.shape[-1] / 2, hts.shape[-2] / 2) + #ht_cross = vertcross(to_np(hts), p, pivot_point=pivot_point, + # angle=90., latlon=True) ht_cross = vertcross(hts, p, pivot_point=pivot_point, angle=90.) # Note: Until the bug is fixed in NCL, the wrf-python cross @@ -304,6 +313,8 @@ def make_interp_test(varname, wrf_in, referent, multi=False, t2 = getvar(in_wrfnc, "T2", timeidx=timeidx) pivot_point = CoordPair(t2.shape[-1] / 2, t2.shape[-2] / 2) + #t2_line1 = interpline(to_np(t2), pivot_point=pivot_point, + # angle=90.0, latlon=True) t2_line1 = interpline(t2, pivot_point=pivot_point, angle=90.0) # Note: After NCL is fixed, remove the slice. @@ -619,9 +630,9 @@ class WRFLatLonTest(ut.TestCase): if __name__ == "__main__": from wrf import (omp_set_num_threads, omp_set_schedule, omp_get_schedule, - omp_set_dynamic, Constants) - omp_set_num_threads(6) - omp_set_schedule(Constants.OMP_SCHED_STATIC, 0) + omp_set_dynamic, OMP_SCHED_STATIC) + omp_set_num_threads(8) + omp_set_schedule(OMP_SCHED_STATIC, 0) omp_set_dynamic(False) ignore_vars = [] # Not testable yet @@ -629,7 +640,7 @@ if __name__ == "__main__": "geopt", "helicity", "lat", "lon", "omg", "p", "pressure", "pvo", "pw", "rh2", "rh", "slp", "ter", "td2", "td", "tc", "theta", "tk", "tv", "twb", "updraft_helicity", "ua", "va", - "wa", "uvmet10", "uvmet", "z", "cfrac"] + "wa", "uvmet10", "uvmet", "z", "cfrac", "zstag", "geopt_stag"] interp_methods = ["interplevel", "vertcross", "interpline", "vinterp"] latlon_tests = ["xy", "ll"]