diff --git a/sorc/chgres_cube.fd/grib2_util.F90 b/sorc/chgres_cube.fd/grib2_util.F90 index c071be7ce..30eaf0f66 100644 --- a/sorc/chgres_cube.fd/grib2_util.F90 +++ b/sorc/chgres_cube.fd/grib2_util.F90 @@ -17,9 +17,16 @@ module grib2_util implicit none +public :: rh2spfh_gfs +public :: fpvsnew + + contains !> Convert relative humidity to specific humidity. +!> Calculation of saturation water vapor pressure is based on +!> Brock and Richardson 2001 (Meterological Measurement +!> Systems, p. 86, equation 5.1) !! !! @param[inout] rh_sphum rel humidity on input. spec hum on output. !! @param[in] p pressure in Pa @@ -57,6 +64,145 @@ subroutine rh2spfh(rh_sphum,p,t) end subroutine RH2SPFH +!> Convert relative humidity to specific humidity (GFS formula) +!> Calculation of saturation water vapor pressure is based on +!> GFS function fvpsnew (Phillips 1982). The model does account for the variation of the +!> latent heat of condensation with temperature. A linear interpolation is done +!> between values in a calculated lookup table. Ice and water are considered +!> separately. This option provides a consistent conversion for GFS grib2 data +!> to be ingested. +!! +!! @param[inout] rh_sphum rel humidity on input. spec hum on output. +!! @param[in] p pressure in Pa +!! @param[in] t temperature +!! @author Jili Dong NCEP/EMC + subroutine rh2spfh_gfs(rh_sphum,p,t) + + implicit none + + integer kind_phys + + parameter (kind_phys = selected_real_kind(13,60)) ! the '60' maps to 64-bit real + + + real(kind=kind_phys),parameter:: con_rd =2.8705e+2 ! gas constant air (J/kg/K) + real(kind=kind_phys),parameter:: con_rv =4.6150e+2 ! gas constant H2O (J/kg/K) + + real(kind=kind_phys),parameter:: con_eps =con_rd/con_rv + real(kind=kind_phys),parameter:: con_epsm1 =con_rd/con_rv-1. + + + + + real(esmf_kind_r4), intent(inout), dimension(i_input,j_input) ::rh_sphum + real(esmf_kind_r8), intent(in) :: p, t(i_input,j_input) + + real, dimension(i_input,j_input) :: QC, rh + real :: ES + integer :: i,j + + print*,"- CONVERT RH TO SPFH AT LEVEL ", p + + rh = rh_sphum + +do j=1,j_input + do i=1,i_input + ES = MIN(FPVSNEW(T(I,J)),P) + QC(i,j) = CON_EPS*ES/(P+CON_EPSM1*ES) + rh_sphum(i,j) = rh(i,j)*QC(i,j)/100.0 + end do +end do + + + !print *, 'T = ', T, ' RH = ', RH, ' P = ', P + !print *, 'q = ', sphum + + +end subroutine RH2SPFH_GFS + + +!> Compute saturation vapor pressure +!! +!! @param[in] t temperature in Kelvin +!! @return fpvsnew Saturation vapor pressure +!! @author N Phillips w/NMC2X2 +!! @date 30 dec 82 + +! +!------------------------------------------------------------------------------------- +! + elemental function fpvsnew(t) +! + implicit none + integer,parameter:: nxpvs=7501 + real,parameter:: con_ttp =2.7316e+2 ! temp at H2O 3pt + real,parameter:: con_psat =6.1078e+2 ! pres at H2O 3pt + real,parameter:: con_cvap =1.8460e+3 ! spec heat H2O gas (J/kg/K) + real,parameter:: con_cliq =4.1855e+3 ! spec heat H2O liq + real,parameter:: con_hvap =2.5000e+6 ! lat heat H2O cond + real,parameter:: con_rv =4.6150e+2 ! gas constant H2O + real,parameter:: con_csol =2.1060e+3 ! spec heat H2O ice + real,parameter:: con_hfus =3.3358e+5 ! lat heat H2O fusion + real,parameter:: tliq=con_ttp + real,parameter:: tice=con_ttp-20.0 + real,parameter:: dldtl=con_cvap-con_cliq + real,parameter:: heatl=con_hvap + real,parameter:: xponal=-dldtl/con_rv + real,parameter:: xponbl=-dldtl/con_rv+heatl/(con_rv*con_ttp) + real,parameter:: dldti=con_cvap-con_csol + real,parameter:: heati=con_hvap+con_hfus + real,parameter:: xponai=-dldti/con_rv + real,parameter:: xponbi=-dldti/con_rv+heati/(con_rv*con_ttp) + real tr,w,pvl,pvi + real fpvsnew + real(esmf_kind_r8),intent(in):: t + integer jx + real xj,x,tbpvs(nxpvs),xp1 + real xmin,xmax,xinc,c2xpvs,c1xpvs +! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + xmin=180.0 + xmax=330.0 + xinc=(xmax-xmin)/(nxpvs-1) +! c1xpvs=1.-xmin/xinc + c2xpvs=1./xinc + c1xpvs=1.-xmin*c2xpvs +! xj=min(max(c1xpvs+c2xpvs*t,1.0),real(nxpvs,krealfp)) + xj=min(max(c1xpvs+c2xpvs*t,1.0),float(nxpvs)) + jx=min(xj,float(nxpvs)-1.0) + x=xmin+(jx-1)*xinc + + tr=con_ttp/x + if(x>=tliq) then + tbpvs(jx)=con_psat*(tr**xponal)*exp(xponbl*(1.-tr)) + elseif(x=tliq) then + tbpvs(jx+1)=con_psat*(tr**xponal)*exp(xponbl*(1.-tr)) + elseif(xp1 Convert omega to vertical velocity. !! !! @param[inout] omega on input, vertical velocity on output diff --git a/sorc/chgres_cube.fd/input_data.F90 b/sorc/chgres_cube.fd/input_data.F90 index d149ec8c9..e3f4b12de 100644 --- a/sorc/chgres_cube.fd/input_data.F90 +++ b/sorc/chgres_cube.fd/input_data.F90 @@ -2459,7 +2459,7 @@ subroutine read_input_atm_grib2_file(localpet) use wgrib2api - use grib2_util, only : rh2spfh, convert_omega + use grib2_util, only : rh2spfh, rh2spfh_gfs, convert_omega implicit none @@ -2490,7 +2490,9 @@ subroutine read_input_atm_grib2_file(localpet) logical :: lret logical :: conv_omega=.false., & hasspfh=.true., & - isnative=.false. + isnative=.false., & + use_rh=.false. + real(esmf_kind_r8), allocatable :: rlevs(:) real(esmf_kind_r4), allocatable :: dummy2d(:,:) @@ -2603,11 +2605,23 @@ subroutine read_input_atm_grib2_file(localpet) if (localpet==0) print*, "- LEVEL AFTER SORT = ",slevs(i) enddo endif + +! Is SPFH on full levels Jili Dong + do vlev = 1, lev_input + iret = grb2_inq(the_file,inv_file,':SPFH:',slevs(vlev)) + if (iret <= 0) then + use_rh = .TRUE. + if (localpet == 0) print*, ':SPFH on level '//trim(slevs(vlev))//' does not exist. & + Will read in RH and convert to SPFH instead.' + exit + end if + end do + if (localpet == 0) print*,"- FIND SPFH OR RH IN FILE" iret = grb2_inq(the_file,inv_file,trim(trac_names_grib_1(1)),trac_names_grib_2(1),lvl_str_space) - if (iret <= 0) then + if (iret <= 0 .or. use_rh) then iret = grb2_inq(the_file,inv_file, ':var0_2','_1_1:',lvl_str_space) if (iret <= 0) call error_handler("READING ATMOSPHERIC WATER VAPOR VARIABLE.", iret) hasspfh = .false. @@ -2782,7 +2796,13 @@ subroutine read_input_atm_grib2_file(localpet) endif !iret<=0 if (n==1 .and. .not. hasspfh) then - call rh2spfh(dummy2d,rlevs(vlev),dummy3d(:,:,vlev)) + if (trim(external_model) .eq. 'GFS') then + print *,'CALRH GFS' + call rh2spfh_gfs(dummy2d,rlevs(vlev),dummy3d(:,:,vlev)) + else + print *,'CALRH non-GFS' + call rh2spfh(dummy2d,rlevs(vlev),dummy3d(:,:,vlev)) + end if endif print*,'tracer ',vlev, maxval(dummy2d),minval(dummy2d) diff --git a/tests/chgres_cube/CMakeLists.txt b/tests/chgres_cube/CMakeLists.txt index c86a6f788..f4abbc058 100644 --- a/tests/chgres_cube/CMakeLists.txt +++ b/tests/chgres_cube/CMakeLists.txt @@ -83,6 +83,13 @@ target_link_libraries(ftst_dint2p add_test(NAME chgres_cube-ftst_dint2p COMMAND ftst_dint2p) +add_executable(ftst_rh2spfh_gfs ftst_rh2spfh_gfs.F90) +target_link_libraries(ftst_rh2spfh_gfs + chgres_cube_lib) + +add_test(NAME chgres_cube-ftst_rh2spfh_gfs COMMAND ftst_rh2spfh_gfs) + + add_executable(ftst_quicksort ftst_quicksort.F90) target_link_libraries(ftst_quicksort chgres_cube_lib) diff --git a/tests/chgres_cube/ftst_rh2spfh_gfs.F90 b/tests/chgres_cube/ftst_rh2spfh_gfs.F90 new file mode 100644 index 000000000..9d79279ad --- /dev/null +++ b/tests/chgres_cube/ftst_rh2spfh_gfs.F90 @@ -0,0 +1,42 @@ + program ftst_rh2spfh_gfs + + use grib2_util + + implicit none + + real, parameter :: eps=1E-4 + + real*4, dimension(3,2) :: rh_500hpa, rh_900hpa, rh_tmp + real*4, dimension(3,2) :: spfh_gfs_900hpa_expected, spfh_gfs_500hpa_expected + real*8, dimension(3,2) :: temp_500hpa, temp_900hpa + real*8 :: pres + +data temp_900hpa /280.9, 282.6, 281.4, 289.9, 286.5, 288.7/ +data rh_900hpa /17.6, 75.2, 99.9, 95.4, 86.6, 89.1/ + +data temp_500hpa /254.1, 259.7, 259.7, 267.2, 262.0, 269.5/ +data rh_500hpa /10.7, 9.1, 0.5, 10.1, 4.2, 0.9/ + +data spfh_gfs_900hpa_expected /1.286259, 6.169457, 7.554718, 12.64650, 9.210890, 10.93505/ +data spfh_gfs_500hpa_expected /0.1519615, 0.2257435, 1.2403490E-02, 0.4856212, 0.1288972, 5.2061662E-02/ + +i_input = 3 +j_input = 2 + +! rh to spfh at 500 hPa with GFSv15/16 conversion +rh_tmp=rh_500hpa +pres=50000.0 +call rh2spfh_gfs(rh_tmp,pres,temp_500hpa) +if (any((abs(rh_tmp*1000.0-spfh_gfs_500hpa_expected)) .gt. eps)) stop 500 + +! rh to spfh at 900 hPa with GFSv15/16 conversion +rh_tmp=rh_900hpa +pres=90000.0 +call rh2spfh_gfs(rh_tmp,pres,temp_900hpa) +if (any((abs(rh_tmp*1000.0-spfh_gfs_900hpa_expected)) .gt. eps)) stop 900 + + print*, "OK" + print*, "SUCCESS!" + + end program ftst_rh2spfh_gfs +