From 5f34ba1ac41951b369fd1c719b8c1b9b0974807c Mon Sep 17 00:00:00 2001 From: David Wright Date: Fri, 5 Nov 2021 09:49:06 -0400 Subject: [PATCH 1/2] fvcom_tools - Add option to process 'cold' or 'warm' restart files (#595) Also, allow the user to select a specific time slice through the command line. Fixes #586 --- sorc/fvcom_tools.fd/CMakeLists.txt | 21 +- sorc/fvcom_tools.fd/docs/user_guide.md | 66 +++-- sorc/fvcom_tools.fd/fvcom_readme.md | 41 --- sorc/fvcom_tools.fd/module_ncio.f90 | 85 +++++-- sorc/fvcom_tools.fd/module_nwp.f90 | 277 +++++++++++++++++---- sorc/fvcom_tools.fd/process_FVCOM.f90 | 160 ++++++++---- tests/fvcom_tools/CMakeLists.txt | 26 ++ tests/fvcom_tools/LSanSuppress.supp | 2 + tests/fvcom_tools/ftst_readfvcomnetcdf.F90 | 146 +++++++++++ 9 files changed, 649 insertions(+), 175 deletions(-) delete mode 100644 sorc/fvcom_tools.fd/fvcom_readme.md create mode 100644 tests/fvcom_tools/CMakeLists.txt create mode 100644 tests/fvcom_tools/LSanSuppress.supp create mode 100644 tests/fvcom_tools/ftst_readfvcomnetcdf.F90 diff --git a/sorc/fvcom_tools.fd/CMakeLists.txt b/sorc/fvcom_tools.fd/CMakeLists.txt index 5bdac05a0..533435083 100644 --- a/sorc/fvcom_tools.fd/CMakeLists.txt +++ b/sorc/fvcom_tools.fd/CMakeLists.txt @@ -7,9 +7,10 @@ set(fortran_src kinds.f90 module_ncio.f90 module_nwp_base.f90 - module_nwp.f90 - process_FVCOM.f90) + module_nwp.f90) +# process_FVCOM.f90) +set(exe_src process_FVCOM.f90) if(CMAKE_Fortran_COMPILER_ID MATCHES "^(Intel)$") set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -r8") @@ -18,15 +19,27 @@ elseif(CMAKE_Fortran_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang)$") endif() set(exe_name fvcom_to_FV3) -add_executable(${exe_name} ${fortran_src}) + +add_library(fvcom_tools_lib STATIC ${fortran_src}) +add_executable(${exe_name} ${exe_src}) + +set(mod_dir "${CMAKE_CURRENT_BINARY_DIR}/mod") +set_target_properties(fvcom_tools_lib PROPERTIES Fortran_MODULE_DIRECTORY ${mod_dir}) +target_include_directories(fvcom_tools_lib INTERFACE ${mod_dir}) + target_link_libraries( - ${exe_name} +# ${exe_name} + fvcom_tools_lib + PUBLIC MPI::MPI_Fortran NetCDF::NetCDF_Fortran) +target_link_libraries(${exe_name} PRIVATE fvcom_tools_lib) + install(TARGETS ${exe_name} RUNTIME DESTINATION ${exec_dir}) # If doxygen documentation we enabled, build it. if(ENABLE_DOCS) add_subdirectory(docs) endif() + diff --git a/sorc/fvcom_tools.fd/docs/user_guide.md b/sorc/fvcom_tools.fd/docs/user_guide.md index 1525880fd..e0dd50c53 100644 --- a/sorc/fvcom_tools.fd/docs/user_guide.md +++ b/sorc/fvcom_tools.fd/docs/user_guide.md @@ -1,18 +1,48 @@ - -# fvcom_tools - -# Introduction - -The process_FVCOM.f90 program replaces lake surface and lake ice -temperature along with aerial ice concentration generated from the -Great Lakes Operational Forecast System (GLOFS) in an FV3 surface -restart file. See [fvcom documentation](@ref fvcom_readme). - -This document is part of the UFS_UTILS -documentation. - -The fvcom_tools program is part of the [NCEPLIBS -UFS_UTILS](https://github.com/NOAA-EMC/UFS_UTILS) project. - - - +@brief replaces lake surface and lake ice temperature @anchor user_guide + +**fvcom_to_FV3.exe** + +**Introduction:** + This code replaces lake surface and lake ice temperature along + with aerial ice concentration generated from Great Lakes + Operational Forecast System (GLOFS), an FVCOM-based model, into + sfc_data.nc. + **NOTE** that the variables in the input files must reside on + the same grid. This means data from FVCOM must be horizontally + interpolated to the FV3 grid. This routine will also force a + minimum ice concentration of 15%. If ice concentration is less + than 15% in FVCOM, it will be set to 0% to avoid FV3 from + changing values less than 15% to 15% and generating unrealistic + lake ice temperatures. + +**Library Dependencies:** + Installation depends on the netCDF library and cmake. + +**Running:** + This routine will take four variables from the command line: + 1. Name of FV3 sfc data file (e.g. sfc_data.tile7.halo0.nc) + which is generated from chgres_cube.exe. + 2. Name of FVCOM data file in netcdf format (e.g. fvcom.nc) + 3. "warm" or "cold" start. "warm" start will read in + sfc_data.nc files generated from a restart of UFS-SRW. + "cold" start will read in sfc_data.nc files generated + from chgres_cube. + 4. String of time slice to use in the fvcom.nc file. This string + should match exactly what is in the Times variable of the .nc file. + To run the script, use the following example, modifying file + names as needed: + ./fvcom_to_FV3 sfc_data.tile7.halo0.nc fvcom.nc cold \ + 2020-01-31T18:00:00.000000 + Output will be to the sfc data file and include lake surface + and lake ice temperature, and lake ice concentration from the + first time in the FVCOM file. + + +This routine is *strongly* based upon Eric James' (ESRL/GSL) work + to update HRRR/WRF Great Lakes' temperature data with FVCOM. + It also relies heavily on Ming Hu's (ESRL/GSL) ncio module. + +**For more information, please contact:** + David Wright + University of Michigan and GLERL + dmwright@umich.edu diff --git a/sorc/fvcom_tools.fd/fvcom_readme.md b/sorc/fvcom_tools.fd/fvcom_readme.md deleted file mode 100644 index 0eae64652..000000000 --- a/sorc/fvcom_tools.fd/fvcom_readme.md +++ /dev/null @@ -1,41 +0,0 @@ -@brief replaces lake surface and lake ice temperature @anchor fvcom_readme - -**fvcom_to_FV3.exe** - -**Introduction:** - This code replaces lake surface and lake ice temperature along - with aerial ice concentration generated from Great Lakes - Operational Forecast System (GLOFS), an FVCOM-based model, into - sfc_data.nc. - **NOTE** that the variables in the input files must reside on - the same grid. This means data from FVCOM must be horizontally - interpolated to the FV3 grid. This routine will also force a - minimum ice concentration of 15%. If ice concentration is less - than 15% in FVCOM, it will be set to 0% to avoid FV3 from - changing values less than 15% to 15% and generating unrealistic - lake ice temperatures. - -**Library Dependencies:** - Installation depends on the netCDF library and cmake. - -**Running:** - This routine will take two variables from the command line: - 1. Name of FV3 sfc data file (e.g. sfc_data.tile7.halo0.nc) - which is generated from chgres_cube.exe. - 2. Name of FVCOM data file in netcdf format (e.g. fvcom.nc) - - To run the script, use the following example, modifying file - names as needed: - ./fvcom_to_FV3 sfc_data.tile7.halo0.nc fvcom.nc - Output will be to the sfc data file and include lake surface - and lake ice temperature, and lake ice concentration from FVCOM. - - -This routine is *strongly* based upon Eric James' (ESRL/GSL) work - to update HRRR/WRF Great Lakes' temperature data with FVCOM. - It also relies heavily on Ming Hu's (ESRL/GSL) ncio module. - -**For more information, please contact:** - David Wright - University of Michigan and GLERL - dmwright@umich.edu diff --git a/sorc/fvcom_tools.fd/module_ncio.f90 b/sorc/fvcom_tools.fd/module_ncio.f90 index 318d9f172..548878979 100644 --- a/sorc/fvcom_tools.fd/module_ncio.f90 +++ b/sorc/fvcom_tools.fd/module_ncio.f90 @@ -85,7 +85,10 @@ module module_ncio procedure :: replace_var_nc_char_3d !< Replace 3D character type variable. @return procedure :: handle_err !< Handle netCDF errors. @return procedure :: convert_theta2t_2dgrid !< Convert theta T (Kelvin) to T (deg C). @return - procedure :: add_new_var => add_new_var_3d !< Add a new 3d variable to output file. @return + generic :: add_new_var => add_new_var_2d, & + add_new_var_3d !< Add a new 2d or 3d variable to ouput file. @return + procedure :: add_new_var_2d !< Add a new 2d variable to output file. @return + procedure :: add_new_var_3d !< Add a new 3d variable to output file. @return end type ncio contains @@ -1306,10 +1309,10 @@ subroutine get_var_nc_double_2d(this,varname,nd1,nd2,field) field(:,j)=temp(istart:iend) enddo ! - if(this%debug_level>100) then - write(*,*) trim(thissubname),' show samples:' - write(*,*) 'max,min:',maxval(field(:,:)),minval(field(:,:)) - endif +! if(this%debug_level>100) then +! write(*,*) trim(thissubname),' show samples:' +! write(*,*) 'max,min:',maxval(field(:,:)),minval(field(:,:)) +! endif else write(*,*) trim(thissubname),' ERROR: dimension does not match.' write(*,*) nd1,this%ends(1),nd2,this%ends(2) @@ -1362,12 +1365,12 @@ subroutine get_var_nc_double_3d(this,varname,nd1,nd2,nd3,field) enddo enddo ! - if(this%debug_level>100) then - write(*,*) trim(thissubname),' show samples:' - do k=1,nd3 - write(*,*) 'k,max,min:',k,maxval(field(:,:,k)),minval(field(:,:,k)) - enddo - endif +! if(this%debug_level>100) then +! write(*,*) trim(thissubname),' show samples:' +! do k=1,nd3 +! write(*,*) 'k,max,min:',k,maxval(field(:,:,k)),minval(field(:,:,k)) +! enddo +! endif else write(*,*) trim(thissubname),' ERROR: dimension does not match.' write(*,*) nd1,this%ends(1),nd2,this%ends(2),nd3,this%ends(3) @@ -1441,6 +1444,7 @@ subroutine get_var_nc_double(this,varname,ilength,field) if(status /= nf90_NoErr) call this%handle_err(status) do i=1,nDims dimname=" " + write(*,*) 'dimids(i) = ', dimids(i) status = nf90_inquire_dimension(ncid, dimids(i), dimname, len = ndim) if (status /= nf90_noerr) call this%handle_err(status) ends(i)=ndim @@ -2271,10 +2275,10 @@ subroutine get_var_nc_char_2d(this,varname,nd1,nd2,field) field(:,j)=temp(istart:iend) enddo ! - if(this%debug_level>100) then - write(*,*) trim(thissubname),' show samples:' - write(*,*) field(1,1) - endif +! if(this%debug_level>100) then +! write(*,*) trim(thissubname),' show samples:' +! write(*,*) field(1,1) +! endif else write(*,*) trim(thissubname),' ERROR: dimension does not match.' write(*,*) nd1,this%ends(1),nd2,this%ends(2) @@ -2327,10 +2331,10 @@ subroutine get_var_nc_char_3d(this,varname,nd1,nd2,nd3,field) enddo enddo ! - if(this%debug_level>100) then - write(*,*) trim(thissubname),' show samples:' - write(*,*) field(1,1,1) - endif +! if(this%debug_level>100) then +! write(*,*) trim(thissubname),' show samples:' +! write(*,*) field(1,1,1) +! endif else write(*,*) trim(thissubname),' ERROR: dimension does not match.' write(*,*) nd1,this%ends(1),nd2,this%ends(2),nd3,this%ends(3) @@ -2542,4 +2546,47 @@ subroutine add_new_var_3d(this,varname,dname1,dname2,dname3,lname,units) end subroutine add_new_var_3d + !> Add a new variable to sfc_data.nc with dimensions (yaxis_1, + !! xaxis_1). + !! + !! @param this instance of an ncio class + !! @param[in] varname Name of variable to be created in netcdf file + !! @param[in] dname1 1st dimension name + !! @param[in] dname2 2nd dimension name + !! @param[in] lname long name output for netcdf variable + !! @param[in] units units to use in netcdf variable + !! + !! @author David.M.Wright org: UM/GLERL @date 2021-10-07 + subroutine add_new_var_2d(this,varname,dname1,dname2,lname,units) + implicit none + ! + class(ncio) :: this + character(len=*),intent(in) :: varname,dname1,dname2 & + ,lname,units + integer :: status, ncid, dim1id, dim2id, varid + + status = nf90_redef(this%ncid) !Enter Define Mode + if (status /= nf90_noerr) call this%handle_err(status) + + status = nf90_inq_dimid(this%ncid, dname1, dim1id) + if (status /= nf90_noerr) call this%handle_err(status) + status = nf90_inq_dimid(this%ncid, dname2, dim2id) + if (status /= nf90_noerr) call this%handle_err(status) + + status = nf90_def_var(this%ncid, varname, nf90_double, & + (/ dim1id, dim2id /), varid) + if (status /= nf90_noerr) call this%handle_err(status) + + status = nf90_put_att(this%ncid, varid, 'long_name', lname) + if (status /= nf90_noerr) call this%handle_err(status) + status = nf90_put_att(this%ncid, varid, 'units', units) + if (status /= nf90_noerr) call this%handle_err(status) + + status = nf90_enddef(this%ncid) !Exit Define Mode and + ! return to Data Mode + if (status /= nf90_noerr) call this%handle_err(status) + + end subroutine add_new_var_2d + + end module module_ncio diff --git a/sorc/fvcom_tools.fd/module_nwp.f90 b/sorc/fvcom_tools.fd/module_nwp.f90 index c3c925333..a4894b6c0 100644 --- a/sorc/fvcom_tools.fd/module_nwp.f90 +++ b/sorc/fvcom_tools.fd/module_nwp.f90 @@ -31,23 +31,35 @@ module module_nwp integer :: xlat !< Number of latitudes. integer :: xlon !< Number of longitudes. integer :: xtime !< Number of times. + integer :: datelen !< Length of date string. integer :: i_mask !< Is var visible (always 1). integer :: i_sst !< Index of sst var. integer :: i_ice !< Index of ice var. integer :: i_sfcT !< Index of sst temp var. integer :: i_iceT !< Index of ice temp var. + integer :: i_sfcTl !< Index of sfcTl character(len=20), allocatable :: varnames(:) !< Variable names. character(len=20), allocatable :: latname !< Latitude name. character(len=20), allocatable :: lonname !< Longitude name. character(len=20), allocatable :: dimnameEW !< East/West dimension name. character(len=20), allocatable :: dimnameNS !< North/South dimension name. character(len=20), allocatable :: dimnameTIME !< Time dimension name. + character(len=20), allocatable :: dimnameDATE !< String dimension name. + character(len=1), allocatable :: times(:,:) !< Array of times in FVCOM. + + real(r_kind), allocatable :: nwp_mask_c(:,:) !< cold start land/water mask 3d array + real(r_kind), allocatable :: nwp_sst_c(:,:,:) !< cold start sst 3d array + real(r_kind), allocatable :: nwp_ice_c(:,:,:) !< cold start over water ice concentration 3d array + real(r_kind), allocatable :: nwp_sfct_c(:,:,:) !< cold start skin temperature 3d array + real(r_kind), allocatable :: nwp_icet_c(:,:,:) !< cold start ice skin temperature 3d array + + real(r_kind), allocatable :: nwp_mask_w(:,:) !< warm start land/water mask 3d array + real(r_kind), allocatable :: nwp_sst_w(:,:) !< warm start sst 3d array + real(r_kind), allocatable :: nwp_ice_w(:,:) !< warm start over water ice concentration 3d array + real(r_kind), allocatable :: nwp_sfct_w(:,:) !< warm start skin temperature 3d array + real(r_kind), allocatable :: nwp_icet_w(:,:) !< warm start ice skin temperature 3d array + real(r_kind), allocatable :: nwp_sfctl_w(:,:) !< warm start skin temperature 3d array - real(r_kind), allocatable :: nwp_mask(:,:,:) !< Land/water mask 3D array - real(r_kind), allocatable :: nwp_sst(:,:,:) !< SST 3D array - real(r_kind), allocatable :: nwp_ice(:,:,:) !< Over water ice concentration 3D array - real(r_kind), allocatable :: nwp_sfcT(:,:,:) !< Skin temperature 3D array - real(r_kind), allocatable :: nwp_iceT(:,:,:) !< Ice skin temperature 3D array end type nwp_type type, extends(nwp_type) :: fcst_nwp @@ -61,6 +73,7 @@ module module_nwp procedure :: initial => initial_nwp !< Defines vars and names. @return procedure :: list_initial => list_initial_nwp !< List the setup. @return procedure :: read_n => read_nwp !< Initialize arrays, get data. @return + procedure :: get_time_ind => get_time_ind_nwp !< Get time ind. @return procedure :: finish => finish_nwp !< Finish and deallocate. @return end type fcst_nwp @@ -75,11 +88,13 @@ module module_nwp !! !! @param this fcst_nwp object !! @param[in] itype either ' FVCOM' or 'FV3LAM'. + !! @param[in] wcstart either 'warm' or 'cold'. !! @author David Wright, University of Michigan and GLERL - subroutine initial_nwp(this,itype) + subroutine initial_nwp(this,itype,wcstart) class(fcst_nwp) :: this character(len=6), intent(in) :: itype + character(len=4), intent(in) :: wcstart ! FVCOM grid if (itype==' FVCOM') then @@ -106,13 +121,46 @@ subroutine initial_nwp(this,itype) allocate(this%dimnameEW) allocate(this%dimnameNS) allocate(this%dimnameTIME) + allocate(this%dimnameDATE) this%dimnameEW = 'lon' this%dimnameNS = 'lat' this%dimnameTIME = 'Time' + this%dimnameDATE = 'DateStrLen' ! FV3LAM grid - else if (trim(itype)=='FV3LAM') then + else if (trim(itype)=='FV3LAM' .AND. wcstart=='warm') then + this%datatype = itype + this%numvar = 6 + + this%i_mask = 1 + this%i_sst = 2 + this%i_ice = 3 + this%i_iceT = 4 + this%i_sfcT = 5 + this%i_sfcTl= 6 + + allocate(this%varnames(this%numvar)) + this%varnames(1) = 'slmsk' + this%varnames(2) = 'tsea' + this%varnames(3) = 'fice' + this%varnames(4) = 'tisfc' + this%varnames(5) = 'tsfc' + this%varnames(6) = 'tsfcl' + + allocate(this%latname) + allocate(this%lonname) + this%latname = 'yaxis_1' + this%lonname = 'xaxis_1' + + allocate(this%dimnameEW) + allocate(this%dimnameNS) + allocate(this%dimnameTIME) + this%dimnameEW = 'xaxis_1' + this%dimnameNS = 'yaxis_1' + this%dimnameTIME = 'Time' + + else if (trim(itype)=='FV3LAM' .AND. wcstart=='cold') then this%datatype = itype this%numvar = 4 @@ -168,9 +216,9 @@ subroutine list_initial_nwp(this) write(*,*) 'List initial setup for ', this%datatype write(*,*) 'number of variables ', this%numvar - write(*,*) 'variable index: mask, sst, ice, sfcT' + write(*,*) 'variable index: mask, sst, ice, sfcT, sfcTl' write(*,'(15x,10I3)') this%i_mask, this%i_sst, this%i_ice, & - & this%i_sfcT + & this%i_sfcT, this%i_sfcTl write(*,*) 'variable name:' do k=1,this%numvar write(*,*) k,trim(this%varnames(k)) @@ -187,6 +235,7 @@ end subroutine list_initial_nwp !! @param this fcst_nwp ojbect !! @param[in] filename netcdf file name !! @param[in] itype either ' FVCOM' or 'FV3LAM' + !! @param[in] wcstart either 'warm' or 'cold'. !! @param[inout] numlon number of grid points in x-direction !! @param[inout] numlat number of grid poinst in y-direction !! @param[inout] numtimes length of time dimension @@ -196,20 +245,22 @@ end subroutine list_initial_nwp !! @param[inout] ice Ice concentration (%) !! @param[inout] sfcT Skin Temperature !! @param[inout] iceT Ice Skin Temperature + !! @param[inout] sfcTl Skin Temperature in restart file !! !! @author David Wright, University of Michigan and GLERL - subroutine read_nwp(this,filename,itype,numlon,numlat,numtimes,time_to_get,mask,sst,ice,sfcT,iceT) + subroutine read_nwp(this,filename,itype,wcstart,numlon,numlat,numtimes,time_to_get,mask,sst,ice,sfcT,iceT,sfcTl) class(fcst_nwp) :: this - character(len=5), intent(in) :: itype + character(len=6), intent(in) :: itype character(len=*), intent(in) :: filename + character(len=4), intent(in) :: wcstart integer, intent(in) :: time_to_get integer, intent(inout) :: numlon, numlat, numtimes ! real(r_single), intent(inout) :: mask(:,:), sst(:,:), ice(:,:), sfcT(:,:) real(r_kind), intent(inout) :: mask(:,:),sst(:,:),ice(:,:),sfcT(:,:) & - ,iceT(:,:) + ,iceT(:,:),sfcTl(:,:) ! Open the file using module_ncio.f90 code, and find the number of ! lat/lon points @@ -227,42 +278,100 @@ subroutine read_nwp(this,filename,itype,numlon,numlat,numtimes,time_to_get,mask, numtimes = this%xtime ! Allocate all the arrays to receive data - - allocate(this%nwp_mask(this%xlon,this%xlat,this%xtime)) - allocate(this%nwp_sst(this%xlon,this%xlat,this%xtime)) - allocate(this%nwp_ice(this%xlon,this%xlat,this%xtime)) - allocate(this%nwp_sfcT(this%xlon,this%xlat,this%xtime)) - allocate(this%nwp_iceT(this%xlon,this%xlat,this%xtime)) + if (wcstart == 'cold' .OR. itype == ' FVCOM') then + allocate(this%nwp_mask_c(this%xlon,this%xlat)) + allocate(this%nwp_sst_c(this%xlon,this%xlat,this%xtime)) + allocate(this%nwp_ice_c(this%xlon,this%xlat,this%xtime)) + allocate(this%nwp_sfcT_c(this%xlon,this%xlat,this%xtime)) + allocate(this%nwp_iceT_c(this%xlon,this%xlat,this%xtime)) ! Get variables from the data file, but only if the variable is ! defined for that data type. - if (this%i_mask .gt. 0) then - call ncdata%get_var(this%varnames(this%i_mask),this%xlon, & - this%xlat,this%xtime,this%nwp_mask) - mask = this%nwp_mask(:,:,1) - end if - if (this%i_sst .gt. 0) then - call ncdata%get_var(this%varnames(this%i_sst),this%xlon, & - this%xlat,this%xtime,this%nwp_sst) - sst = this%nwp_sst(:,:,time_to_get) - end if - if (this%i_ice .gt. 0) then - call ncdata%get_var(this%varnames(this%i_ice),this%xlon, & - this%xlat,this%xtime,this%nwp_ice) - ice = this%nwp_ice(:,:,time_to_get) - end if - if (this%i_sfcT .gt. 0) then - call ncdata%get_var(this%varnames(this%i_sfcT),this%xlon, & - this%xlat,this%xtime,this%nwp_sfcT) - sfcT = this%nwp_sfcT(:,:,time_to_get) + write(*,*) 'itype = ', itype + write(*,*) 'wcstart = ', wcstart + write(*,*) 'xlat = ', this%xlat + write(*,*) 'xlon = ', this%xlon + write(*,*) 'xtime = ', this%xtime + + if (this%i_mask .gt. 0) then + call ncdata%get_var(this%varnames(this%i_mask),this%xlon, & + this%xlat,this%nwp_mask_c) + mask = this%nwp_mask_c(:,:) + end if + if (this%i_sst .gt. 0) then + write(*,*) 'get sst for cold or FVCOM' + call ncdata%get_var(this%varnames(this%i_sst),this%xlon, & + this%xlat,this%xtime,this%nwp_sst_c) + sst = this%nwp_sst_c(:,:,time_to_get) + end if + if (this%i_ice .gt. 0) then + call ncdata%get_var(this%varnames(this%i_ice),this%xlon, & + this%xlat,this%xtime,this%nwp_ice_c) + ice = this%nwp_ice_c(:,:,time_to_get) + end if + if (this%i_sfcT .gt. 0) then + call ncdata%get_var(this%varnames(this%i_sfcT),this%xlon, & + this%xlat,this%xtime,this%nwp_sfcT_c) + sfcT = this%nwp_sfcT_c(:,:,time_to_get) + end if + if (this%i_iceT .gt. 0) then + call ncdata%get_var(this%varnames(this%i_iceT),this%xlon, & + this%xlat,this%xtime,this%nwp_iceT_c) + iceT = this%nwp_iceT_c(:,:,time_to_get) + end if + else if (wcstart == 'warm') then + allocate(this%nwp_mask_w(this%xlon,this%xlat)) + allocate(this%nwp_sst_w(this%xlon,this%xlat)) + allocate(this%nwp_ice_w(this%xlon,this%xlat)) + allocate(this%nwp_sfcT_w(this%xlon,this%xlat)) + allocate(this%nwp_iceT_w(this%xlon,this%xlat)) + allocate(this%nwp_sfcTl_w(this%xlon,this%xlat)) +! Get variables from the data file, but only if the variable is +! defined for that data type. + + write(*,*) 'itype = ', itype + write(*,*) 'wcstart =', wcstart + write(*,*) 'xlat = ', this%xlat + write(*,*) 'xlon = ', this%xlon + write(*,*) 'xtime = ', this%xtime + + + + if (this%i_mask .gt. 0) then + call ncdata%get_var(this%varnames(this%i_mask),this%xlon, & + this%xlat,this%nwp_mask_w) + mask = this%nwp_mask_w(:,:) + end if + if (this%i_sst .gt. 0) then + call ncdata%get_var(this%varnames(this%i_sst),this%xlon, & + this%xlat,this%nwp_sst_w) + sst = this%nwp_sst_w(:,:) + end if + if (this%i_ice .gt. 0) then + call ncdata%get_var(this%varnames(this%i_ice),this%xlon, & + this%xlat,this%nwp_ice_w) + ice = this%nwp_ice_w(:,:) + end if + if (this%i_sfcT .gt. 0) then + call ncdata%get_var(this%varnames(this%i_sfcT),this%xlon, & + this%xlat,this%nwp_sfcT_w) + sfcT = this%nwp_sfcT_w(:,:) + end if + if (this%i_iceT .gt. 0) then + call ncdata%get_var(this%varnames(this%i_iceT),this%xlon, & + this%xlat,this%nwp_iceT_w) + iceT = this%nwp_iceT_w(:,:) + end if + if (this%i_sfcTl .gt. 0) then + call ncdata%get_var(this%varnames(this%i_sfcTl),this%xlon, & + this%xlat,this%nwp_sfcTl_w) + sfcTl = this%nwp_sfcTl_w(:,:) + end if + else + write(*,*) 'Choose either "warm" or "cold" for file' + stop 'Error in wcstart. Check spelling or if variable was assigned' end if - if (this%i_iceT .gt. 0) then - call ncdata%get_var(this%varnames(this%i_iceT),this%xlon, & - this%xlat,this%xtime,this%nwp_iceT) - iceT = this%nwp_iceT(:,:,time_to_get) - end if - ! Close the netCDF file. call ncdata%close @@ -275,10 +384,14 @@ end subroutine read_nwp !> Finish and deallocate. !! !! @param this fcst_nwp object + !! @param[in] itype either ' FVCOM' or 'FV3LAM' + !! @param[in] wcstart either 'warm' or 'cold'. !! @author David Wright, University of Michigan and GLERL - subroutine finish_nwp(this) + subroutine finish_nwp(this,itype,wcstart) class(fcst_nwp) :: this + character(len=6), intent(in) :: itype + character(len=4), intent(in) :: wcstart type(nwpbase), pointer :: thisobs,thisobsnext @@ -288,11 +401,21 @@ subroutine finish_nwp(this) deallocate(this%dimnameEW) deallocate(this%dimnameNS) deallocate(this%dimnameTIME) - deallocate(this%nwp_mask) - deallocate(this%nwp_sst) - deallocate(this%nwp_ice) - deallocate(this%nwp_sfcT) - deallocate(this%nwp_iceT) + if (wcstart == 'cold' .OR. itype==' FVCOM') then + deallocate(this%nwp_mask_c) + deallocate(this%nwp_sst_c) + deallocate(this%nwp_ice_c) + deallocate(this%nwp_sfcT_c) + deallocate(this%nwp_iceT_c) + else if (wcstart == 'warm') then + deallocate(this%nwp_mask_w) + deallocate(this%nwp_sst_w) + deallocate(this%nwp_ice_w) + deallocate(this%nwp_sfcT_w) + deallocate(this%nwp_iceT_w) + else + write(*,*) 'no deallocation' + end if thisobs => this%head if(.NOT.associated(thisobs)) then @@ -312,4 +435,60 @@ subroutine finish_nwp(this) end subroutine finish_nwp + !> This subroutine searches the FVCOM 'Times' variable + !! and returns the matching index + !! + !! @param this fcst_nwp ojbect + !! @param[in] filename netcdf file name + !! @param[in] instr string of requested time + !! @param[out] outindex int index that matches instr + !! + !! @author David Wright, University of Michigan and GLERL + subroutine get_time_ind_nwp(this,filename,instr,outindex) + + class(fcst_nwp) :: this + + character(len=*), intent(in) :: filename + character(len=*), intent(in) :: instr + integer, intent(out) :: outindex + + character(len=26) :: temp + integer :: foundind + integer :: k,i + +! Open the file using module_ncio.f90 code, and find the length of +! time in the file + call ncdata%open(trim(filename),'r',200) + call ncdata%get_dim(this%dimnameTIME,this%xtime) + call ncdata%get_dim(this%dimnameDATE,this%datelen) + write(*,*) 'xtime = ', this%xtime + write(*,*) 'datelen = ', this%datelen + allocate(this%times(this%datelen,this%xtime)) + call ncdata%get_var('Times',this%datelen,this%xtime,this%times) + + foundind = 0 + + do k=1,this%xtime,1 + do i = 1,len(temp),1 + temp(i:i) = this%times(i,k) + end do + if (trim(temp) == trim(instr)) then !If times are equal return k + outindex = k + foundind = 1 + end if + end do + if (foundind == 0) then + outindex = -999 + deallocate(this%times) + call ncdata%close + write(*,*) 'WARNING: Supplied time not found in file: ', trim(instr) + write(*,*) 'Stoppping fvcom_to_FV3 and proceeding without using FVCOM data' + stop + end if + + deallocate(this%times) + call ncdata%close + + end subroutine get_time_ind_nwp + end module module_nwp diff --git a/sorc/fvcom_tools.fd/process_FVCOM.f90 b/sorc/fvcom_tools.fd/process_FVCOM.f90 index 960980cc7..bbcffcec9 100755 --- a/sorc/fvcom_tools.fd/process_FVCOM.f90 +++ b/sorc/fvcom_tools.fd/process_FVCOM.f90 @@ -8,14 +8,19 @@ !! GLERL-provided FVCOM forecast files (which have already been mapped !! to the FV3-LAM grid) into sfc_data.nc. !! -!! This script will take two variables from the command line: +!! This script will take four variables from the command line: !! 1. Name of FV3 sfc data file (e.g. sfc_data.tile7.halo0.nc) !! 2. Name of FVCOM data file (e.g. fvcom.nc) -!! +!! 3. "warm" or "cold" start. "warm" start will read in +!! sfc_data.nc files generated from a restart of UFS-SRW. +!! "cold" start will read in sfc_data.nc files generated +!! from chgres_cube. +!! 4. String of time slice to use in the fvcom.nc file. This string +!! should match exactly what is in the Times variable of the .nc file. !! To run the script, use the following example, modifying file !! names as needed: -!! ./fvcom_to_FV3 sfc_data.tile7.halo0.nc fvcom.nc -!! +!! ./fvcom_to_FV3 sfc_data.tile7.halo0.nc fvcom.nc cold \ +!! 2020-01-31T18:00:00.000000 !! Code is strongly based upon Eric James' (ESRL/GSL) work to update !! HRRR/WRF Great Lakes' temperature data with FVCOM. Code also !! relies heavily on Ming Hu's ncio module. @@ -53,6 +58,7 @@ program process_FVCOM integer :: lbclon, lbclat, lbctimes integer :: i, j, t1, t2 integer :: num_args, ix + integer :: indexFVCOMsel real :: rad2deg = 180.0/3.1415926 real :: userDX, userDY, CEN_LAT, CEN_LON @@ -63,11 +69,13 @@ program process_FVCOM character(len=180) :: fv3file character(len=180) :: fvcomfile + character(len=180) :: wcstart + character(len=180) :: inputFVCOMselStr character(len=180), dimension(:), allocatable :: args real(r_kind), allocatable :: fv3ice(:,:), fv3sst(:,:) real(r_kind), allocatable :: fv3sfcT(:,:), fv3mask(:,:) - real(r_kind), allocatable :: fv3iceT(:,:) + real(r_kind), allocatable :: fv3iceT(:,:), fv3sfcTl(:,:) real(r_kind), allocatable :: lbcice(:,:), lbcsst(:,:) real(r_kind), allocatable :: lbcsfcT(:,:), lbcmask(:,:) real(r_kind), allocatable :: lbciceT(:,:) @@ -96,13 +104,20 @@ program process_FVCOM do ix = 1, num_args call get_command_argument(ix,args(ix)) end do - +! fv3file: location of UFS grid +! fvcomfile: location of FVCOM data file +! wcstart: warm (restart) or cold start +! inputFVCOMtimes: string of time to use fv3file=trim(args(1)) write(*,*) trim(fv3file) fvcomfile=trim(args(2)) write(*,*) trim(fvcomfile) + wcstart=trim(args(3)) + write(*,*) 'warm or cold start = ', wcstart + inputFVCOMselStr=trim(args(4)) +! read(inputFVCOMselStr,*) inputFVCOMsel + write(*,*) 'select time = ', inputFVCOMselStr - t2 = 1 ! Obtain grid parameters workPath='./' @@ -124,6 +139,7 @@ program process_FVCOM allocate(fv3sst(nlon,nlat)) allocate(fv3mask(nlon,nlat)) allocate(fv3iceT(nlon,nlat)) + allocate(fv3sfcTl(nlon,nlat)) allocate(lbcice(nlon,nlat)) allocate(lbcsfcT(nlon,nlat)) @@ -134,12 +150,15 @@ program process_FVCOM ! Read fv3 sfc_data.nc before update ! fv3file='sfc_data.nc' +! fv3times: length of time dimension of UFS atmospheric grid (should be 1) +! t1: index of time dimension to pull (should be 1) + fv3times=1 t1=1 - call fcst%initial('FV3LAM') + call fcst%initial('FV3LAM',wcstart) call fcst%list_initial - call fcst%read_n(trim(fv3file),'FV3LAM',fv3lon,fv3lat,fv3times,t1,fv3mask,fv3sst,fv3ice,fv3sfcT,fv3iceT) - call fcst%finish + call fcst%read_n(trim(fv3file),'FV3LAM',wcstart,fv3lon,fv3lat,fv3times,t1,fv3mask,fv3sst,fv3ice,fv3sfcT,fv3iceT,fv3sfcTl) + call fcst%finish('FV3LAM',wcstart) write(*,*) 'fv3times: ', fv3times @@ -148,57 +167,101 @@ program process_FVCOM ! Read FVCOM input datasets ! fvcomfile='fvcom.nc' +! lbctimes: length of time dimension of FVCOM input data (command line input) ! Space infront of ' FVCOM' below is important!! - call fcst%initial(' FVCOM') + call fcst%initial(' FVCOM',wcstart) call fcst%list_initial - call fcst%read_n(trim(fvcomfile),' FVCOM',lbclon,lbclat,lbctimes,t2,lbcmask,lbcsst,lbcice,lbcsfcT,lbciceT) - call fcst%finish + call fcst%get_time_ind(trim(fvcomfile),inputFVCOMselStr,indexFVCOMsel) +! t2: index of time dimension to pull from FVCOM + t2=indexFVCOMsel + write(*,*) 'time asked for =', trim(inputFVCOMselStr) + write(*,*) 'time index selected = ', t2 + call fcst%read_n(trim(fvcomfile),' FVCOM',wcstart,lbclon,lbclat,lbctimes,t2,lbcmask,lbcsst,lbcice,lbcsfcT,lbciceT,fv3sfcTl) + call fcst%finish(' FVCOM',wcstart) ! Check that the dimensions match if (lbclon .ne. nlon .or. lbclat .ne. nlat) then - write(*,*) 'ERROR: FVCOM/FV3 dimensions do not match:' + write(*,*) 'ERROR: FVCOM/FV3 dimensions do not match:' write(*,*) 'lbclon: ', lbclon write(*,*) 'nlon: ', nlon write(*,*) 'lbclat: ', lbclat write(*,*) 'nlat: ', nlat - stop 135 + stop 'error' endif write(*,*) 'lbctimes: ', lbctimes write(*,*) 'time to use: ', t2 + if (t2 .gt. lbctimes) then + write(*,*) 'ERROR: Requested time dimension out of range' + write(*,*) 'Length of time dimension: ', lbctimes + write(*,*) 'Time index to use: ', t2 + stop 'error' + endif + ! Update with FVCOM fields and process ! ice cover data. Ice fraction is set ! to a minimum of 15% due to FV3-LAM ! raising any value below 15% to 15%. - - do j=1,nlat - do i=1,nlon - if (lbcmask(i,j) > 0. .and. lbcsst(i,j) .ge. -90.0) then - !If ice fraction below 15%, set to 0 - if (lbcice(i,j) < 0.15) then - lbcice(i,j) = 0.0 - endif - fv3ice(i,j) = lbcice(i,j) - !If ice in FVCOM, but not in FV3-LAM, change to ice - if (lbcice(i,j) > 0. .and. fv3mask(i,j) == 0.) then - fv3mask(i,j) = 2. - endif - !If ice in FV3-LAM and not FVCOM, remove it from FV3-LAM - if (fv3mask(i,j) == 2. .and. lbcice(i,j) == 0.) then - fv3mask(i,j) = 0. - endif - fv3sst(i,j) = lbcsst(i,j) + 273.15 - fv3sfcT(i,j) = lbcsst(i,j) + 273.15 - fv3iceT(i,j) = lbcsst(i,j) + 273.15 - !If ice exists in FVCOM, change ice surface temp - if (lbcice(i,j) > 0.) then - fv3iceT(i,j) = lbciceT(i,j) + 273.15 - end if - endif - enddo - enddo + if (wcstart == 'warm') then + do j=1,nlat + do i=1,nlon + if (lbcmask(i,j) > 0. .and. lbcsst(i,j) .ge. -90.0) then + !If ice fraction below 15%, set to 0 + if (lbcice(i,j) < 0.15) then + lbcice(i,j) = 0.0 + endif + fv3ice(i,j) = lbcice(i,j) + !If ice in FVCOM, but not in FV3-LAM, change to ice + if (lbcice(i,j) > 0. .and. fv3mask(i,j) == 0.) then + fv3mask(i,j) = 2. + endif + !If ice in FV3-LAM and not FVCOM, remove it from FV3-LAM + if (fv3mask(i,j) == 2. .and. lbcice(i,j) == 0.) then + fv3mask(i,j) = 0. + endif + fv3sst(i,j) = lbcsst(i,j) + 273.15 + fv3sfcT(i,j) = lbcsst(i,j) + 273.15 + fv3iceT(i,j) = lbcsst(i,j) + 273.15 + fv3sfcTl(i,j)= lbcsst(i,j) + 273.15 + !If ice exists in FVCOM, change ice surface temp + if (lbcice(i,j) > 0.) then + fv3iceT(i,j) = lbciceT(i,j) + 273.15 + end if + end if + enddo + enddo + else if (wcstart == 'cold') then + do j=1,nlat + do i=1,nlon + if (lbcmask(i,j) > 0. .and. lbcsst(i,j) .ge. -90.0) then + !If ice fraction below 15%, set to 0 + if (lbcice(i,j) < 0.15) then + lbcice(i,j) = 0.0 + endif + fv3ice(i,j) = lbcice(i,j) + !If ice in FVCOM, but not in FV3-LAM, change to ice + if (lbcice(i,j) > 0. .and. fv3mask(i,j) == 0.) then + fv3mask(i,j) = 2. + endif + !If ice in FV3-LAM and not FVCOM, remove it from FV3-LAM + if (fv3mask(i,j) == 2. .and. lbcice(i,j) == 0.) then + fv3mask(i,j) = 0. + endif + fv3sst(i,j) = lbcsst(i,j) + 273.15 + fv3sfcT(i,j) = lbcsst(i,j) + 273.15 + fv3iceT(i,j) = lbcsst(i,j) + 273.15 + !If ice exists in FVCOM, change ice surface temp + if (lbcice(i,j) > 0.) then + fv3iceT(i,j) = lbciceT(i,j) + 273.15 + end if + end if + enddo + enddo + else + write(*,*) 'Variable wcstart is not set to either warm or cold' + end if ! Write out sfc file again @@ -207,9 +270,18 @@ program process_FVCOM call geo%replace_var("fice",NLON,NLAT,fv3ice) call geo%replace_var("slmsk",NLON,NLAT,fv3mask) call geo%replace_var("tisfc",NLON,NLAT,fv3iceT) + + if (wcstart == 'cold') then ! Add_New_Var takes names of (Variable,Dim1,Dim2,Dim3,Long_Name,Units) - call geo%add_new_var('glmsk','xaxis_1','yaxis_1','Time','glmsk','none') - call geo%replace_var('glmsk',NLON,NLAT,lbcmask) + call geo%add_new_var('glmsk','xaxis_1','yaxis_1','Time','glmsk','none') + call geo%replace_var('glmsk',NLON,NLAT,lbcmask) + end if + if (wcstart == 'warm') then + call geo%replace_var("tsfc",NLON,NLAT,fv3sfcT) + call geo%replace_var("tsfcl",NLON,NLAT,fv3sfcTl) + call geo%add_new_var('glmsk','xaxis_1','yaxis_1','glmsk','none') + call geo%replace_var('glmsk',NLON,NLAT,lbcmask) + end if call geo%close write(6,*) "=== LOWBC UPDATE SUCCESS ===" diff --git a/tests/fvcom_tools/CMakeLists.txt b/tests/fvcom_tools/CMakeLists.txt new file mode 100644 index 000000000..50ac42441 --- /dev/null +++ b/tests/fvcom_tools/CMakeLists.txt @@ -0,0 +1,26 @@ +# This is the cmake build file for the tests directory of the +# UFS_UTILS project. +# +# David Wright + +if(CMAKE_Fortran_COMPILER_ID MATCHES "^(Intel)$") + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -r8 -assume byterecl") +elseif(CMAKE_Fortran_COMPILER_ID MATCHES "^(GNU)$") + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -ffree-line-length-0 -fdefault-real-8") +endif() + +include_directories(${PROJECT_SOURCE_DIR}) + +# Copy necessary test files from the source data directory to the +# build data directory. +execute_process( COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/data/sfcdata_unittest.nc ${CMAKE_CURRENT_BINARY_DIR}/sfcdata_unittest.nc) +execute_process( COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/data/fvcom_unittest.nc ${CMAKE_CURRENT_BINARY_DIR}/fvcom_unittest.nc) +execute_process( COMMAND ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_SOURCE_DIR}/LSanSuppress.supp ${CMAKE_CURRENT_BINARY_DIR}/LSanSuppress.supp) + +add_executable(ftst_readfvcomnetcdf ftst_readfvcomnetcdf.F90) +add_test(NAME fvcom_tools-ftst_readfvcomnetcdf COMMAND ftst_readfvcomnetcdf) +target_link_libraries(ftst_readfvcomnetcdf fvcom_tools_lib) + diff --git a/tests/fvcom_tools/LSanSuppress.supp b/tests/fvcom_tools/LSanSuppress.supp new file mode 100644 index 000000000..5a129f48e --- /dev/null +++ b/tests/fvcom_tools/LSanSuppress.supp @@ -0,0 +1,2 @@ +leak:ESMCI +leak:esmf diff --git a/tests/fvcom_tools/ftst_readfvcomnetcdf.F90 b/tests/fvcom_tools/ftst_readfvcomnetcdf.F90 new file mode 100644 index 000000000..0f8f68e2c --- /dev/null +++ b/tests/fvcom_tools/ftst_readfvcomnetcdf.F90 @@ -0,0 +1,146 @@ + program readfvcomnetcdf + +! Unit test for the fvcom_tools routines. +! +! Reads in a 5x5 user generated grid to see if +! file is read in correctly. Grid generated via +! netCDF4 python library. Expected values come +! from those used in the python generation +! script. +! +! Author David Wright + + + use module_ncio, only: ncio + use module_nwp, only: fcst_nwp + + implicit none + + integer, parameter :: NUM_VALUES=2 !number of values to check + + real, parameter :: EPSILON=0.0001 !error difference to check against + + integer :: nlat, nlon, t1, t2 + integer :: fv3lon, fv3lat, fv3times + integer :: lbclon, lbclat, lbctimes + integer :: t2_expected + logical :: fv3_exists, fvcom_exists + + integer :: lat_lon_expected_values(NUM_VALUES) !expected number of lat/lons + integer :: fv3mask_expected(NUM_VALUES) !expected fv3 mask values + integer :: fv3sst_expected(NUM_VALUES) !expected fv3 sst values + real :: fv3ice_expected(NUM_VALUES) !expected fv3 ice values + real :: fv3iceT_expected(NUM_VALUES) !expected fv3 ice temp values + integer :: lbcmask_expected(NUM_VALUES) !expected fvcom mask values + real :: lbcsst_expected(NUM_VALUES) !expected fvcom sst values + real :: lbcice_expected(NUM_VALUES) !expected fvcom ice values + real :: lbciceT_expected(NUM_VALUES) !expected fvcom ice temp values + +! Create allocabable arrays to read from .nc files + real, allocatable :: fv3ice(:,:), fv3sst(:,:) + real, allocatable :: fv3sfcT(:,:), fv3mask(:,:) + real, allocatable :: fv3iceT(:,:), fv3sfcTl(:,:) + real, allocatable :: lbcice(:,:), lbcsst(:,:) + real, allocatable :: lbcsfcT(:,:), lbcmask(:,:) + real, allocatable :: lbciceT(:,:) + +! Expected values from the dummy files + data lat_lon_expected_values /5, 5/ + data fv3mask_expected /1, 0/ + data fv3sst_expected /1, 0/ + data fv3ice_expected /.1, 0/ + data fv3iceT_expected /.1, 0/ + data lbcmask_expected /1, 0/ + data lbcsst_expected /1, -99.99999/ + data lbcice_expected /1, -99.99999/ + data lbciceT_expected /1, -99.99999/ + data t2_expected /2 / !expect second time index from fvcom file + + type(ncio) :: geo !grid data object + type(fcst_nwp) :: fcst !object to read data into + + character(len=180) :: fv3file !fv3 file name + character(len=180) :: fvcomfile !fvcom file name + character(len=180) :: wcstart !warm or cold start + character(len=180) :: inputFVCOMselStr !time str in fvcom file + + + print*,"Starting test of fvcom_tools." +!Set default file names, cold start, and time str + fv3file = 'sfcdata_unittest.nc' + fvcomfile = 'fvcom_unittest.nc' + wcstart = 'cold' + inputFVCOMselStr = '3333-44-55T66:77:88.000000' + t1 = 1 + +!If files do not exist, stop + INQUIRE(FILE=trim(fv3file), EXIST=fv3_exists) + if(.not.fv3_exists) stop 1 + INQUIRE(FILE=trim(fvcomfile), EXIST=fvcom_exists) + if(.not.fvcom_exists) stop 2 +!Open grid file and read in number of lat/lon points + call geo%open(trim(fv3file), 'r', 200) + call geo%get_dim("xaxis_1",nlon) + call geo%get_dim("yaxis_1",nlat) + call geo%close +!If file does not have expected lat/lon points, stop + if (abs(nlon - lat_lon_expected_values(2)) > EPSILON) stop 3 + if (abs(nlat - lat_lon_expected_values(1)) > EPSILON) stop 4 + + allocate(fv3ice(nlon,nlat)) + allocate(fv3sfcT(nlon,nlat)) + allocate(fv3sst(nlon,nlat)) + allocate(fv3mask(nlon,nlat)) + allocate(fv3iceT(nlon,nlat)) + allocate(fv3sfcTl(nlon,nlat)) + + allocate(lbcice(nlon,nlat)) + allocate(lbcsfcT(nlon,nlat)) + allocate(lbcsst(nlon,nlat)) + allocate(lbcmask(nlon,nlat)) + allocate(lbciceT(nlon,nlat)) + +!Initialize and read in fv3 sfc data + call fcst%initial('FV3LAM',wcstart) + call fcst%read_n(trim(fv3file),'FV3LAM',wcstart,fv3lon,fv3lat,fv3times,t1,fv3mask,fv3sst,fv3ice,fv3sfcT,fv3iceT,fv3sfcTl) + call fcst%finish('FV3LAM',wcstart) +!If variables in fv3 sfc file do not match expected, stop + if (abs(fv3mask(1,1) - fv3mask_expected(1)) > EPSILON) stop 5 + if (abs(fv3mask(5,5) - fv3mask_expected(2)) > EPSILON) stop 6 + + if (abs(fv3sst(1,1) - fv3sst_expected(1)) > EPSILON) stop 7 + if (abs(fv3sst(5,5) - fv3sst_expected(2)) > EPSILON) stop 8 + + if (abs(fv3ice(1,1) - fv3ice_expected(1)) > EPSILON) stop 7 + if (abs(fv3ice(5,5) - fv3ice_expected(2)) > EPSILON) stop 8 + + if (abs(fv3iceT(1,1) - fv3iceT_expected(1)) > EPSILON) stop 9 + if (abs(fv3iceT(5,5) - fv3iceT_expected(2)) > EPSILON) stop 10 + +!Initialize and read in fvcom data + call fcst%initial(' FVCOM',wcstart) + call fcst%get_time_ind(trim(fvcomfile),inputFVCOMselStr,t2) +!If second time index is not returned, stop + if (abs(t2 - t2_expected) > EPSILON) stop 11 + call fcst%read_n(trim(fvcomfile),' FVCOM',wcstart,lbclon,lbclat,lbctimes,t2,lbcmask,lbcsst,lbcice,lbcsfcT,lbciceT,fv3sfcTl) + call fcst%finish(' FVCOM',wcstart) +!If variables in fvcom file do not match expected, stop + if (abs(lbcmask(1,1) - lbcmask_expected(1)) > EPSILON) stop 12 + if (abs(lbcmask(5,5) - lbcmask_expected(2)) > EPSILON) stop 13 + + if (abs(lbcsst(1,1) - lbcsst_expected(1)) > EPSILON) stop 14 + if (abs(lbcsst(5,5) - lbcsst_expected(2)) > EPSILON) stop 15 + + if (abs(lbcice(1,1) - lbcice_expected(1)) > EPSILON) stop 16 + if (abs(lbcice(5,5) - lbcice_expected(2)) > EPSILON) stop 17 + + if (abs(lbciceT(1,1) - lbciceT_expected(1)) > EPSILON) stop 18 + if (abs(lbciceT(5,5) - lbciceT_expected(2)) > EPSILON) stop 19 + + + print*,"OK" + + print*,"SUCCESS!" + + end program readfvcomnetcdf + From f30740edc06c5a815722f67838ea6af1e515fb45 Mon Sep 17 00:00:00 2001 From: GeorgeGayno-NOAA <52789452+GeorgeGayno-NOAA@users.noreply.github.com> Date: Fri, 5 Nov 2021 16:49:11 -0400 Subject: [PATCH 2/2] Update more documentation after move to ufs-community (#597) Fixes #593. --- sorc/chgres_cube.fd/docs/user_guide.md | 4 +- sorc/emcsfc_ice_blend.fd/docs/user_guide.md | 8 +- sorc/emcsfc_snow2mdl.fd/docs/user_guide.md | 6 +- sorc/fvcom_tools.fd/docs/user_guide.md | 84 ++++++++++--------- sorc/gblevents.fd/docs/user_guide.md | 8 +- sorc/global_cycle.fd/docs/user_guide.md | 4 +- sorc/grid_tools.fd/docs/user_guide.md | 7 +- sorc/lsm_routines.fd/docs/user_guide.md | 4 +- .../noah.fd/docs/user_guide.md | 4 +- sorc/orog_mask_tools.fd/docs/user_guide.md | 6 +- sorc/sfc_climo_gen.fd/docs/user_guide.md | 6 +- sorc/vcoord_gen.fd/docs/user_guide.md | 6 +- 12 files changed, 68 insertions(+), 79 deletions(-) diff --git a/sorc/chgres_cube.fd/docs/user_guide.md b/sorc/chgres_cube.fd/docs/user_guide.md index d2cb6f290..6b2be5019 100644 --- a/sorc/chgres_cube.fd/docs/user_guide.md +++ b/sorc/chgres_cube.fd/docs/user_guide.md @@ -11,8 +11,8 @@ Common Data Form (NetCDF) data. This document is part of the UFS_UTILS documentation. -The chgres_cube program is part of the [NCEPLIBS -UFS_UTILS](https://github.com/NOAA-EMC/UFS_UTILS) project. +The chgres_cube program is part of the +[UFS_UTILS](https://github.com/ufs-community/UFS_UTILS) project. ## Where to find GFS GRIB2, NEMSIO and NetCDF data diff --git a/sorc/emcsfc_ice_blend.fd/docs/user_guide.md b/sorc/emcsfc_ice_blend.fd/docs/user_guide.md index b09aaa049..b8d53252e 100644 --- a/sorc/emcsfc_ice_blend.fd/docs/user_guide.md +++ b/sorc/emcsfc_ice_blend.fd/docs/user_guide.md @@ -10,9 +10,5 @@ analysis used to update the GFS once per day. This document is part of the UFS_UTILS documentation. -The emcsfc_ice_blend program is part of the [NCEPLIBS -UFS_UTILS](https://github.com/NOAA-EMC/UFS_UTILS) project. - - - - +The emcsfc_ice_blend program is part of the +[UFS_UTILS](https://github.com/ufs-community/UFS_UTILS) project. diff --git a/sorc/emcsfc_snow2mdl.fd/docs/user_guide.md b/sorc/emcsfc_snow2mdl.fd/docs/user_guide.md index a680c4004..ef109c64f 100644 --- a/sorc/emcsfc_snow2mdl.fd/docs/user_guide.md +++ b/sorc/emcsfc_snow2mdl.fd/docs/user_guide.md @@ -10,7 +10,5 @@ analysis used to update the GFS snow field once per day. This document is part of the UFS_UTILS documentation. -The emcsfc_snow2mdl program is part of the [NCEPLIBS -UFS_UTILS](https://github.com/NOAA-EMC/UFS_UTILS) project. - - +The emcsfc_snow2mdl program is part of the +[UFS_UTILS](https://github.com/ufs-community/UFS_UTILS) project. diff --git a/sorc/fvcom_tools.fd/docs/user_guide.md b/sorc/fvcom_tools.fd/docs/user_guide.md index e0dd50c53..f24124ade 100644 --- a/sorc/fvcom_tools.fd/docs/user_guide.md +++ b/sorc/fvcom_tools.fd/docs/user_guide.md @@ -1,48 +1,56 @@ -@brief replaces lake surface and lake ice temperature @anchor user_guide - -**fvcom_to_FV3.exe** - -**Introduction:** - This code replaces lake surface and lake ice temperature along - with aerial ice concentration generated from Great Lakes - Operational Forecast System (GLOFS), an FVCOM-based model, into - sfc_data.nc. - **NOTE** that the variables in the input files must reside on - the same grid. This means data from FVCOM must be horizontally - interpolated to the FV3 grid. This routine will also force a - minimum ice concentration of 15%. If ice concentration is less - than 15% in FVCOM, it will be set to 0% to avoid FV3 from - changing values less than 15% to 15% and generating unrealistic - lake ice temperatures. - -**Library Dependencies:** - Installation depends on the netCDF library and cmake. - -**Running:** - This routine will take four variables from the command line: - 1. Name of FV3 sfc data file (e.g. sfc_data.tile7.halo0.nc) +# fvcom_tools + +# Introduction + +This code replaces lake surface and lake ice temperature along +with aerial ice concentration generated from Great Lakes +Operational Forecast System (GLOFS), an FVCOM-based model, into +sfc_data.nc. + +This document is part of the UFS_UTILS +documentation. + +The fvcom_tools program is part of the +[UFS_UTILS](https://github.com/ufs-community/UFS_UTILS) project. + +## NOTE + +The variables in the input files must reside on +the same grid. This means data from FVCOM must be horizontally +interpolated to the FV3 grid. This routine will also force a +minimum ice concentration of 15%. If ice concentration is less +than 15% in FVCOM, it will be set to 0% to avoid FV3 from +changing values less than 15% to 15% and generating unrealistic +lake ice temperatures. + +## Library Dependencies: + +Installation depends on the netCDF library and cmake. + +## Running + +This routine will take four variables from the command line: +1. Name of FV3 sfc data file (e.g. sfc_data.tile7.halo0.nc) which is generated from chgres_cube.exe. - 2. Name of FVCOM data file in netcdf format (e.g. fvcom.nc) - 3. "warm" or "cold" start. "warm" start will read in +2. Name of FVCOM data file in netcdf format (e.g. fvcom.nc) +3. "warm" or "cold" start. "warm" start will read in sfc_data.nc files generated from a restart of UFS-SRW. "cold" start will read in sfc_data.nc files generated from chgres_cube. - 4. String of time slice to use in the fvcom.nc file. This string +4. String of time slice to use in the fvcom.nc file. This string should match exactly what is in the Times variable of the .nc file. - To run the script, use the following example, modifying file - names as needed: +To run the script, use the following example, modifying file +names as needed: ./fvcom_to_FV3 sfc_data.tile7.halo0.nc fvcom.nc cold \ 2020-01-31T18:00:00.000000 - Output will be to the sfc data file and include lake surface - and lake ice temperature, and lake ice concentration from the - first time in the FVCOM file. - +Output will be to the sfc data file and include lake surface +and lake ice temperature, and lake ice concentration from the +first time in the FVCOM file. This routine is *strongly* based upon Eric James' (ESRL/GSL) work - to update HRRR/WRF Great Lakes' temperature data with FVCOM. - It also relies heavily on Ming Hu's (ESRL/GSL) ncio module. +to update HRRR/WRF Great Lakes' temperature data with FVCOM. +It also relies heavily on Ming Hu's (ESRL/GSL) ncio module. + +## For more information, please contact: -**For more information, please contact:** - David Wright - University of Michigan and GLERL - dmwright@umich.edu +David Wright, University of Michigan and GLERL: dmwright@umich.edu diff --git a/sorc/gblevents.fd/docs/user_guide.md b/sorc/gblevents.fd/docs/user_guide.md index bd2ac1b93..2de2c6d90 100644 --- a/sorc/gblevents.fd/docs/user_guide.md +++ b/sorc/gblevents.fd/docs/user_guide.md @@ -8,9 +8,5 @@ The gblevents program performs pre and post processing of prepbufr events. This document is part of the UFS_UTILS documentation. -The gblevents program is part of the [NCEPLIBS -UFS_UTILS](https://github.com/NOAA-EMC/UFS_UTILS) project. - - - - +The gblevents program is part of the +[UFS_UTILS](https://github.com/ufs-community/UFS_UTILS) project. diff --git a/sorc/global_cycle.fd/docs/user_guide.md b/sorc/global_cycle.fd/docs/user_guide.md index cfd05706b..2df4f8cdc 100644 --- a/sorc/global_cycle.fd/docs/user_guide.md +++ b/sorc/global_cycle.fd/docs/user_guide.md @@ -11,5 +11,5 @@ the GFS and GDAS cycles. This document is part of the UFS_UTILS documentation. -The global_cycle program is part of the [NCEPLIBS -UFS_UTILS](https://github.com/NOAA-EMC/UFS_UTILS) project. +The global_cycle program is part of the +[UFS_UTILS](https://github.com/ufs-community/UFS_UTILS) project. diff --git a/sorc/grid_tools.fd/docs/user_guide.md b/sorc/grid_tools.fd/docs/user_guide.md index cef921ba5..2acf0c492 100644 --- a/sorc/grid_tools.fd/docs/user_guide.md +++ b/sorc/grid_tools.fd/docs/user_guide.md @@ -14,8 +14,5 @@ The grid_tools include: This document is part of the UFS_UTILS documentation. -The grid_tools programs are part of the [NCEPLIBS -UFS_UTILS](https://github.com/NOAA-EMC/UFS_UTILS) project. - - - +The grid_tools programs are part of the +[UFS_UTILS](https://github.com/ufs-community/UFS_UTILS) project. diff --git a/sorc/lsm_routines.fd/docs/user_guide.md b/sorc/lsm_routines.fd/docs/user_guide.md index 94e301939..5829de1a2 100644 --- a/sorc/lsm_routines.fd/docs/user_guide.md +++ b/sorc/lsm_routines.fd/docs/user_guide.md @@ -13,5 +13,5 @@ The land models included are: - Noah - routines for the Noah land surface model. -The lsm_routines libraries are used in the [NCEPLIBS -UFS_UTILS](https://github.com/NOAA-EMC/UFS_UTILS) project. +The lsm_routines libraries are used in the +[UFS_UTILS](https://github.com/ufs-community/UFS_UTILS) project. diff --git a/sorc/lsm_routines.fd/noah.fd/docs/user_guide.md b/sorc/lsm_routines.fd/noah.fd/docs/user_guide.md index 18c4936c9..2937b6fe5 100644 --- a/sorc/lsm_routines.fd/noah.fd/docs/user_guide.md +++ b/sorc/lsm_routines.fd/noah.fd/docs/user_guide.md @@ -21,5 +21,5 @@ This document is part of the UFS_UTILS documentation.lsm_routines directory. -The NOAH library created here is used in the [NCEPLIBS -UFS_UTILS](https://github.com/NOAA-EMC/UFS_UTILS) project. +The NOAH library created here is used in the +[UFS_UTILS](https://github.com/ufs-community/UFS_UTILS) project. diff --git a/sorc/orog_mask_tools.fd/docs/user_guide.md b/sorc/orog_mask_tools.fd/docs/user_guide.md index 285dde897..3f085055c 100644 --- a/sorc/orog_mask_tools.fd/docs/user_guide.md +++ b/sorc/orog_mask_tools.fd/docs/user_guide.md @@ -11,7 +11,5 @@ The orog_mask_tools include: This document is part of the UFS_UTILS documentation. -The orog_mask_tools programs are part of the [NCEPLIBS -UFS_UTILS](https://github.com/NOAA-EMC/UFS_UTILS) project. - - +The orog_mask_tools programs are part of the +[UFS_UTILS](https://github.com/ufs-community/UFS_UTILS) project. diff --git a/sorc/sfc_climo_gen.fd/docs/user_guide.md b/sorc/sfc_climo_gen.fd/docs/user_guide.md index e7fae3a5c..aa9c6a80a 100644 --- a/sorc/sfc_climo_gen.fd/docs/user_guide.md +++ b/sorc/sfc_climo_gen.fd/docs/user_guide.md @@ -9,7 +9,5 @@ fields, such as vegetation type and albedo, for an FV3 grid. This document is part of the UFS_UTILS documentation. -The orog_mask_tools programs are part of the [NCEPLIBS -UFS_UTILS](https://github.com/NOAA-EMC/UFS_UTILS) project. - - +The orog_mask_tools programs are part of the +[UFS_UTILS](https://github.com/ufs-community/UFS_UTILS) project. diff --git a/sorc/vcoord_gen.fd/docs/user_guide.md b/sorc/vcoord_gen.fd/docs/user_guide.md index cb689eb25..0c8a01306 100644 --- a/sorc/vcoord_gen.fd/docs/user_guide.md +++ b/sorc/vcoord_gen.fd/docs/user_guide.md @@ -11,7 +11,5 @@ by the forecast model to define the hybrid levels. This document is part of the UFS_UTILS documentation. -The vcoord_gen programs are part of the [NCEPLIBS -UFS_UTILS](https://github.com/NOAA-EMC/UFS_UTILS) project. - - +The vcoord_gen programs are part of the +[UFS_UTILS](https://github.com/ufs-community/UFS_UTILS) project.