diff --git a/reg_tests/global_cycle/C768.lndincsnow.sh b/reg_tests/global_cycle/C768.lndincsnow.sh new file mode 100755 index 000000000..62a91fa7a --- /dev/null +++ b/reg_tests/global_cycle/C768.lndincsnow.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +#------------------------------------------------------------------ +# Run global_cycle for a C768 case to test the ingest of snow +# increments from JEDI. Compare output to a baseline set of +# files using the 'nccmp' utility. +#------------------------------------------------------------------ + +set -x + +NCCMP=${NCCMP:-$(which nccmp)} + +export MAX_TASKS_CY=6 + +export HOMEgfs=$NWPROD +export BASE_GSM=$NWPROD + +export CYCLEXEC=$BASE_GSM/exec/global_cycle + +export CDATE=2019073000 +export FHOUR=00 +export DELTSFC=6 + +export CASE=C768 + +export COMIN=$HOMEreg/input_data +export FNTSFA=$COMIN/gdas.t00z.rtgssthr.grb +export FNSNOA=$COMIN/gdas.t00z.snogrb_t1534.3072.1536 +export FNACNA=$COMIN/gdas.t00z.seaice.5min.blend.grb +export NST_FILE=$COMIN/gdas.t00z.dtfanl.nc + +export DO_SNO_INC=.true. # must be lower-case. +export JCAP=1534 +export LONB=3072 +export LATB=1536 + +export FIXgsm=$BASE_GSM/fix/fix_am + +export DONST="NO" +export use_ufo=.true. + +export DO_SFCCYCLE=".FALSE." +export DO_LNDINC=".TRUE." + +export VERBOSE=YES +export CYCLVARS=FSNOL=99999.,FSNOS=99999., + +$BASE_GSM/ush/global_cycle_driver.sh + +iret=$? +if [ $iret -ne 0 ]; then + set +x + echo "<<< C768 LANDINC SNOW CYCLE TEST FAILED. >>>" + exit $iret +fi + +test_failed=0 + +cd $DATA +for files in *tile*.nc +do + if [ -f $files ]; then + echo CHECK $files + $NCCMP -dmfqS $files $HOMEreg/baseline_data/c768.lndincsnow/$files + iret=$? + if [ $iret -ne 0 ]; then + test_failed=1 + fi + fi +done + +set +x +if [ $test_failed -ne 0 ]; then + echo + echo "****************************************" + echo "<<< C768 LANDINC SNOW CYCLE TEST FAILED. >>>" + echo "****************************************" +else + echo + echo "***************************************" + echo "<<< C768 LANDINC SNOW CYCLE TEST PASSED. >>>" + echo "***************************************" +fi + +exit diff --git a/reg_tests/global_cycle/C768.lndinc.sh b/reg_tests/global_cycle/C768.lndincsoil.sh similarity index 66% rename from reg_tests/global_cycle/C768.lndinc.sh rename to reg_tests/global_cycle/C768.lndincsoil.sh index 54241dabc..5d50c4221 100755 --- a/reg_tests/global_cycle/C768.lndinc.sh +++ b/reg_tests/global_cycle/C768.lndincsoil.sh @@ -1,8 +1,10 @@ #!/bin/bash #------------------------------------------------------------------ -# Run global_cycle for a C768 test case. Compare output -# to a baseline set of files using the 'nccmp' utility. +# Run global_cycle for a C768 case to test the ingest and +# application of soil temperature increments from the GSI. +# Compare output to a baseline set of files using the 'nccmp' +# utility. #------------------------------------------------------------------ set -x @@ -50,7 +52,7 @@ $BASE_GSM/ush/global_cycle_driver.sh iret=$? if [ $iret -ne 0 ]; then set +x - echo "<<< C768 LANDINC CYCLE TEST FAILED. >>>" + echo "<<< C768 LANDINC SOILT CYCLE TEST FAILED. >>>" exit $iret fi @@ -61,7 +63,7 @@ for files in *tile*.nc do if [ -f $files ]; then echo CHECK $files - $NCCMP -dmfqS $files $HOMEreg/baseline_data/c768.lndinc/$files + $NCCMP -dmfqS $files $HOMEreg/baseline_data/c768.lndincsoil/$files iret=$? if [ $iret -ne 0 ]; then test_failed=1 @@ -72,14 +74,14 @@ done set +x if [ $test_failed -ne 0 ]; then echo - echo "*********************************" - echo "<<< C768 LANDINC CYCLE TEST FAILED. >>>" - echo "*********************************" + echo "*****************************************" + echo "<<< C768 LANDINC SOILT CYCLE TEST FAILED. >>>" + echo "*****************************************" else echo - echo "*********************************" - echo "<<< C768 LANDINC CYCLE TEST PASSED. >>>" - echo "*********************************" + echo "*****************************************" + echo "<<< C768 LANDINC SOILT CYCLE TEST PASSED. >>>" + echo "*****************************************" fi exit diff --git a/reg_tests/global_cycle/driver.hera.sh b/reg_tests/global_cycle/driver.hera.sh index 369e91869..a7058347a 100755 --- a/reg_tests/global_cycle/driver.hera.sh +++ b/reg_tests/global_cycle/driver.hera.sh @@ -57,13 +57,19 @@ TEST1=$(sbatch --parsable --ntasks-per-node=6 --nodes=1 -t 0:05:00 -A $PROJECT_C LOG_FILE=consistency.log02 export DATA="${DATA_DIR}/test2" export COMOUT=$DATA -TEST2=$(sbatch --parsable --ntasks-per-node=6 --nodes=1 -t 0:05:00 -A $PROJECT_CODE -q $QUEUE -J c768.lndinc \ - -o $LOG_FILE -e $LOG_FILE ./C768.lndinc.sh) +TEST2=$(sbatch --parsable --ntasks-per-node=6 --nodes=1 -t 0:05:00 -A $PROJECT_CODE -q $QUEUE -J c768.lndincsoil \ + -o $LOG_FILE -e $LOG_FILE ./C768.lndincsoil.sh) + +LOG_FILE=consistency.log03 +export DATA="${DATA_DIR}/test3" +export COMOUT=$DATA +TEST3=$(sbatch --parsable --ntasks-per-node=6 --nodes=1 -t 0:05:00 -A $PROJECT_CODE -q $QUEUE -J c768.lndincsnow \ + -o $LOG_FILE -e $LOG_FILE ./C768.lndincsnow.sh) LOG_FILE=consistency.log sbatch --nodes=1 -t 0:01:00 -A $PROJECT_CODE -J chgres_summary -o $LOG_FILE -e $LOG_FILE \ --open-mode=append -q $QUEUE -d\ - afterok:$TEST1:$TEST2 << EOF + afterok:$TEST1:$TEST2:$TEST3 << EOF #!/bin/bash grep -a '<<<' ${LOG_FILE}* > summary.log EOF diff --git a/reg_tests/global_cycle/driver.jet.sh b/reg_tests/global_cycle/driver.jet.sh index 9ebae7207..f5f488415 100755 --- a/reg_tests/global_cycle/driver.jet.sh +++ b/reg_tests/global_cycle/driver.jet.sh @@ -27,8 +27,8 @@ module list export WORK_DIR="${WORK_DIR:-/lfs4/HFIP/emcda/$LOGNAME/stmp}" -PROJECT_CODE="${PROJECT_CODE:-emcda}" -QUEUE="${QUEUE:-windfall}" +PROJECT_CODE="${PROJECT_CODE:-hfv3gfs}" +QUEUE="${QUEUE:-batch}" #----------------------------------------------------------------------------- # Should not have to change anything below. @@ -55,13 +55,19 @@ TEST1=$(sbatch --parsable --ntasks-per-node=6 --nodes=1 -t 0:05:00 -A $PROJECT_C LOG_FILE=consistency.log02 export DATA="${DATA_DIR}/test2" export COMOUT=$DATA -TEST2=$(sbatch --parsable --ntasks-per-node=6 --nodes=1 -t 0:05:00 -A $PROJECT_CODE -q $QUEUE -J c768.lndinc \ - --partition=xjet -o $LOG_FILE -e $LOG_FILE ./C768.lndinc.sh) +TEST2=$(sbatch --parsable --ntasks-per-node=6 --nodes=1 -t 0:05:00 -A $PROJECT_CODE -q $QUEUE -J c768.lndincsoil \ + --partition=xjet -o $LOG_FILE -e $LOG_FILE ./C768.lndincsoil.sh) + +LOG_FILE=consistency.log03 +export DATA="${DATA_DIR}/test3" +export COMOUT=$DATA +TEST3=$(sbatch --parsable --ntasks-per-node=6 --nodes=1 -t 0:05:00 -A $PROJECT_CODE -q $QUEUE -J c768.lndincsnow \ + --partition=xjet -o $LOG_FILE -e $LOG_FILE ./C768.lndincsnow.sh) LOG_FILE=consistency.log sbatch --partition=xjet --nodes=1 -t 0:01:00 -A $PROJECT_CODE -J summary -o $LOG_FILE -e $LOG_FILE \ --open-mode=append -q $QUEUE -d\ - afterok:$TEST1:$TEST2 << EOF + afterok:$TEST1:$TEST2:$TEST3 << EOF #!/bin/bash grep -a '<<<' ${LOG_FILE}* > ./summary.log EOF diff --git a/reg_tests/global_cycle/driver.orion.sh b/reg_tests/global_cycle/driver.orion.sh index d7801a4f4..27d138c8d 100755 --- a/reg_tests/global_cycle/driver.orion.sh +++ b/reg_tests/global_cycle/driver.orion.sh @@ -55,13 +55,19 @@ TEST1=$(sbatch --parsable --ntasks-per-node=6 --nodes=1 -t 0:05:00 -A $PROJECT_C LOG_FILE=consistency.log02 export DATA="${DATA_DIR}/test2" export COMOUT=$DATA -TEST2=$(sbatch --parsable --ntasks-per-node=6 --nodes=1 -t 0:05:00 -A $PROJECT_CODE -q $QUEUE -J c768.lndinc \ - -o $LOG_FILE -e $LOG_FILE ./C768.lndinc.sh) +TEST2=$(sbatch --parsable --ntasks-per-node=6 --nodes=1 -t 0:05:00 -A $PROJECT_CODE -q $QUEUE -J c768.lndincsoil \ + -o $LOG_FILE -e $LOG_FILE ./C768.lndincsoil.sh) + +LOG_FILE=consistency.log03 +export DATA="${DATA_DIR}/test3" +export COMOUT=$DATA +TEST3=$(sbatch --parsable --ntasks-per-node=6 --nodes=1 -t 0:05:00 -A $PROJECT_CODE -q $QUEUE -J c768.lndincsnow \ + -o $LOG_FILE -e $LOG_FILE ./C768.lndincsnow.sh) LOG_FILE=consistency.log sbatch --nodes=1 -t 0:01:00 -A $PROJECT_CODE -J chgres_summary -o $LOG_FILE -e $LOG_FILE \ --open-mode=append -q $QUEUE -d\ - afterok:$TEST1:$TEST2 << EOF + afterok:$TEST1:$TEST2:$TEST3 << EOF #!/bin/bash grep -a '<<<' ${LOG_FILE}* > summary.log EOF diff --git a/reg_tests/global_cycle/driver.wcoss_cray.sh b/reg_tests/global_cycle/driver.wcoss_cray.sh index 2e927c87f..f18242917 100755 --- a/reg_tests/global_cycle/driver.wcoss_cray.sh +++ b/reg_tests/global_cycle/driver.wcoss_cray.sh @@ -26,7 +26,7 @@ module list WORK_DIR="${WORK_DIR:-/gpfs/hps3/stmp/$LOGNAME}" PROJECT_CODE="${PROJECT_CODE:-GDAS-T2O}" -QUEUE="${QUEUE:-debug}" +QUEUE="${QUEUE:-dev}" #----------------------------------------------------------------------------- # Should not have to change anything below. @@ -59,8 +59,14 @@ bsub -e $LOG_FILE -o $LOG_FILE -q $QUEUE -P $PROJECT_CODE -J c768.fv3gfs -M 2400 LOG_FILE=consistency.log02 export DATA="${DATA_DIR}/test2" export COMOUT=$DATA -bsub -e $LOG_FILE -o $LOG_FILE -q $QUEUE -P $PROJECT_CODE -J c768.lndinc -M 2400 -W 0:05 \ - -extsched 'CRAYLINUX[]' "export NODES=1; $PWD/C768.lndinc.sh" +bsub -e $LOG_FILE -o $LOG_FILE -q $QUEUE -P $PROJECT_CODE -J c768.lndincsoil -M 2400 -W 0:05 \ + -extsched 'CRAYLINUX[]' "export NODES=1; $PWD/C768.lndincsoil.sh" + +LOG_FILE=consistency.log03 +export DATA="${DATA_DIR}/test3" +export COMOUT=$DATA +bsub -e $LOG_FILE -o $LOG_FILE -q $QUEUE -P $PROJECT_CODE -J c768.lndincsnow -M 2400 -W 0:05 \ + -extsched 'CRAYLINUX[]' "export NODES=1; $PWD/C768.lndincsnow.sh" LOG_FILE=consistency.log bsub -o $LOG_FILE -q $QUEUE -P $PROJECT_CODE -J summary -R "rusage[mem=100]" -W 0:01 \ diff --git a/reg_tests/global_cycle/driver.wcoss_dell_p3.sh b/reg_tests/global_cycle/driver.wcoss_dell_p3.sh index 736ee1a87..13896deb0 100755 --- a/reg_tests/global_cycle/driver.wcoss_dell_p3.sh +++ b/reg_tests/global_cycle/driver.wcoss_dell_p3.sh @@ -28,7 +28,7 @@ module list WORK_DIR="${WORK_DIR:-/gpfs/dell1/stmp/$LOGNAME}" PROJECT_CODE="${PROJECT_CODE:-GFS-DEV}" -QUEUE="${QUEUE:-debug}" +QUEUE="${QUEUE:-dev}" #----------------------------------------------------------------------------- # Should not have to change anything below. @@ -55,8 +55,14 @@ bsub -e $LOG_FILE -o $LOG_FILE -q $QUEUE -P $PROJECT_CODE -J c768.fv3gfs -W 0:05 LOG_FILE=consistency.log02 export DATA="${DATA_DIR}/test2" export COMOUT=$DATA -bsub -e $LOG_FILE -o $LOG_FILE -q $QUEUE -P $PROJECT_CODE -J c768.lndinc -W 0:05 -x -n 6 \ - -M 2400 -R "span[ptile=6]" -R "affinity[core(1)]" "$PWD/C768.lndinc.sh" +bsub -e $LOG_FILE -o $LOG_FILE -q $QUEUE -P $PROJECT_CODE -J c768.lndincsoil -W 0:05 -x -n 6 \ + -M 2400 -R "span[ptile=6]" -R "affinity[core(1)]" "$PWD/C768.lndincsoil.sh" + +LOG_FILE=consistency.log03 +export DATA="${DATA_DIR}/test3" +export COMOUT=$DATA +bsub -e $LOG_FILE -o $LOG_FILE -q $QUEUE -P $PROJECT_CODE -J c768.lndincsnow -W 0:05 -x -n 6 \ + -M 2400 -R "span[ptile=6]" -R "affinity[core(1)]" "$PWD/C768.lndincsnow.sh" LOG_FILE=consistency.log bsub -o $LOG_FILE -q $QUEUE -P $PROJECT_CODE -J summary -R "affinity[core(1)]" -R "rusage[mem=100]" -W 0:01 \ diff --git a/sorc/global_cycle.fd/cycle.f90 b/sorc/global_cycle.fd/cycle.f90 index 19e71ce9e..05459f2c1 100644 --- a/sorc/global_cycle.fd/cycle.f90 +++ b/sorc/global_cycle.fd/cycle.f90 @@ -3,28 +3,32 @@ !! @author Mark Iredell NCEP/EMC !> Stand alone surface/NSST cycle driver for the cubed-sphere grid. -!! Each cubed-sphere tile runs independently on its own mpi task. +!! Each cubed-sphere tile runs independently on its own mpi task. !! The surface update component runs with threads. The NSST !! update component in not threaded. !! -!! There are three main options (which can be called in combination): -!! 1. Update the surface fields with sfccylce (do_sfccycle = .true.) -!! 2. Update the land states with increments read in from file (do_lndinc = .true.) -!! Designed to work with a land increment file created by the GSI on the Gaussian -!! grid. The increments are interpolated here to the model grid, using the -!! same method as for the NST increments. Initially implemented for -!! applying soil temperature increments calculated from the EnKF -!! assimilation of T2m (but this is not a requirement - any -!! GSI-generated soil temperature increment file can be applied here). -!! 3. Update the NSST field, several options: +!! There are three main options (which can be called in combination): +!! 1. Update the surface fields with sfccylce (do_sfccycle = .true.) +!! 2. Update the land states with increments read in from file (do_lndinc = .true.) +!! Designed to work with either: +!! 2a. A land increment file created by the GSI on the Gaussian +!! grid. The increments are interpolated here to the model grid, using the +!! same method as for the NST increments. This is currently implemented for +!! applying soil temperature increments calculated from the EnKF +!! assimilation of T2m (but this is not a requirement - any +!! GSI-generated soil temperature increment file can be applied here). +!! 2b. A land increment file created by JEDI, on the native model grid +!! (cube sphere tiles). This is currently implemented for snow depth +!! updates for the Noah model. +!! 3. Update the NSST field, several options: !! !! 3a. Update the NSST TREF field using !! GSI increments on the Gaussian grid. All other NSST !! fields are cycled. Invoke this option by setting -!! namelist variable DONST=.true. and NST_FILE to +!! namelist variable DONST=.true. and NST_FILE to !! the name of the GSI increment file. -!! -!! 3b. Run with NSST, but postpone the TREF update. +!! +!! 3b. Run with NSST, but postpone the TREF update. !! Here all NSST fields are cycled. But the NSST IFD field is !! used to flag points that flipped from ice to open water. !! To invoke this option, set DONST=.true. and NST_FILE="NULL". @@ -38,8 +42,11 @@ !! file. !! - $NST_FILE Gaussian GSI file which contains NSST !! TREF increments -!! - $LND_SNO_FILE Gaussian GSI file which contains soil state +!! - $LND_SOI_FILE Gaussian GSI file which contains soil state !! increments +!! - xainc.$NNN The cubed-sphere increment file (contains +!! increments calculated by JEDI on the native +!! model grid). !! !! OUTPUT FILES: !! - fnbgso.$NNN The updated sfc/nsst restart file. @@ -64,10 +71,12 @@ !! - USE_UFO Adjust sst and soil substrate temperature for !! differences between the filtered and unfiltered !! terrain. -!! -DONST Process NSST records. -!! -DO_SFCCYCLE Call sfccycle routine to update surface fields -!! -DO_LNDINC Read in land increment files, and add increments to -!! soil states. +!! -DONST Process NSST records. +!! -DO_SFCCYCLE Call sfccycle routine to update surface fields +!! -DO_LNDINC Read in land increment files, and add increments to +!! relevant states. +!! -DO_SOI_INC Do land increments to soil states. +!! -DO_SNO_INC Do land increments to snow states. !! - ISOT Use statsgo soil type when '1'. Use zobler when '0'. !! - IVEGSRC Use igbp veg type when '1'. Use sib when '2'. !! - ZSEA1/2_MM When running with NSST model, this is the lower/ @@ -81,7 +90,7 @@ !! (max_tasks-1). !! -NST_FILE path/name of the gaussian GSI file which contains NSST !! TREF increments. -!! -LND_SOI_FILE path/name of the gaussian GSI file which contains soil +!! -LND_SOI_FILE path/name of the gaussian GSI file which contains soil !! state increments. !! !! -2005-02-03: Iredell for global_analysis @@ -133,14 +142,14 @@ PROGRAM SFC_DRV USE_UFO = .FALSE. DONST = "NO" DO_LNDINC = .FALSE. - DO_SFCCYCLE = .TRUE. + DO_SFCCYCLE = .TRUE. PRINT* PRINT*,"READ NAMCYC NAMELIST." CALL BAOPENR(36, "fort.36", IERR) READ(36, NML=NAMCYC) - IF (MYRANK==0) WRITE(6,NAMCYC) + !IF (MYRANK==0) WRITE(6,NAMCYC) IF (MAX_TASKS < 99999 .AND. MYRANK > (MAX_TASKS - 1)) THEN PRINT*,"USER SPECIFIED MAX NUMBER OF TASKS: ", MAX_TASKS @@ -210,7 +219,7 @@ END PROGRAM SFC_DRV !! !! - OROG .. Orography !! - ALB .. Snow-free albedo - !! - SNO .. Liquid-equivalent snow depth (SWE) + !! - SWE .. Snow water equivalent !! - ZOR .. Surface roughness length !! - VET .. Vegetation type !! - TSF .. Surface skin temperature. Sea surface temp. over ocean. @@ -227,7 +236,7 @@ END PROGRAM SFC_DRV !! - SOT .. Soil type !! - SIH .. Sea ice thickness !! - SIC .. Sea ice concentration - !! - SWD .. Actual snow depth + !! - SND .. Snow depth !! - SLC .. Liquid soil moisture (LSOIL layers) !! - VMN .. Vegetation cover minimum !! - VMX .. Vegetation cover maximum @@ -283,7 +292,7 @@ END PROGRAM SFC_DRV !! @param[in] DO_NSST When true, process NSST records. !! @param[in] DO_SFCCYCLE Call sfccycle routine to update surface fields !! @param[in] DO_LNDINC Read in land increment files, and add increments to - !! soil states. + !! requested states. !! @param[in] ZSEA1 When running NSST model, this is the lower bound !! of depth of sea temperature. In whole mm. !! @param[in] ZSEA2 When running NSST model, this is the upper bound @@ -299,9 +308,11 @@ SUBROUTINE SFCDRV(LUGB, IDIM,JDIM,LSM,LENSFC,LSOIL,DELTSFC, & ! USE READ_WRITE_DATA USE MPI - USE LAND_INCREMENTS, ONLY: ADD_INCREMENT_SOIL, & - CALCULATE_SOILSNOWMASK, & - APPLY_LAND_DA_ADJUSTMENTS + USE LAND_INCREMENTS, ONLY: ADD_INCREMENT_SOIL, & + ADD_INCREMENT_SNOW, & + CALCULATE_LANDINC_MASK, & + APPLY_LAND_DA_ADJUSTMENTS_STC, & + APPLY_LAND_DA_ADJUSTMENTS_SND IMPLICIT NONE @@ -329,7 +340,7 @@ SUBROUTINE SFCDRV(LUGB, IDIM,JDIM,LSM,LENSFC,LSOIL,DELTSFC, & REAL :: SLMASK(LENSFC), OROG(LENSFC) REAL :: SIHFCS(LENSFC), SICFCS(LENSFC) REAL :: SITFCS(LENSFC), TSFFCS(LENSFC) - REAL :: SNOFCS(LENSFC), ZORFCS(LENSFC) + REAL :: SWEFCS(LENSFC), ZORFCS(LENSFC) REAL :: ALBFCS(LENSFC,4), TG3FCS(LENSFC) REAL :: CNPFCS(LENSFC), SMCFCS(LENSFC,LSOIL) REAL :: STCFCS(LENSFC,LSOIL), SLIFCS(LENSFC) @@ -338,7 +349,7 @@ SUBROUTINE SFCDRV(LUGB, IDIM,JDIM,LSM,LENSFC,LSOIL,DELTSFC, & REAL :: SOTFCS(LENSFC), ALFFCS(LENSFC,2) REAL :: CVFCS(LENSFC), CVTFCS(LENSFC) REAL :: CVBFCS(LENSFC), TPRCP(LENSFC) - REAL :: SRFLAG(LENSFC), SWDFCS(LENSFC) + REAL :: SRFLAG(LENSFC), SNDFCS(LENSFC) REAL :: SLCFCS(LENSFC,LSOIL), VMXFCS(LENSFC) REAL :: VMNFCS(LENSFC), T2M(LENSFC) REAL :: Q2M(LENSFC), SLPFCS(LENSFC) @@ -352,22 +363,28 @@ SUBROUTINE SFCDRV(LUGB, IDIM,JDIM,LSM,LENSFC,LSOIL,DELTSFC, & !! start. REAL, ALLOCATABLE :: STC_BCK(:,:), SMC_BCK(:,:), SLC_BCK(:,:) REAL, ALLOCATABLE :: SLIFCS_FG(:) - INTEGER, ALLOCATABLE :: SOILSNOW_FG_MASK(:), SOILSNOW_MASK(:) + INTEGER, ALLOCATABLE :: LANDINC_MASK_FG(:), LANDINC_MASK(:) + REAL, ALLOCATABLE :: SND_BCK(:), SND_INC(:), SWE_BCK(:) TYPE(NSST_DATA) :: NSST real, dimension(idim,jdim) :: tf_clm,tf_trd,sal_clm real, dimension(lensfc) :: tf_clm_tile,tf_trd_tile,sal_clm_tile + INTEGER :: veg_type_landice - logical :: file_exists + LOGICAL :: FILE_EXISTS, DO_SOI_INC, DO_SNO_INC !-------------------------------------------------------------------------------- ! NST_FILE is the path/name of the gaussian GSI file which contains NSST ! increments. !-------------------------------------------------------------------------------- + NAMELIST/NAMSFCD/ NST_FILE, LND_SOI_FILE, DO_SNO_INC + DATA NST_FILE/'NULL'/ DATA LND_SOI_FILE/'NULL'/ + + DO_SNO_INC = .FALSE. + DO_SOI_INC = .FALSE. - NAMELIST/NAMSFCD/ NST_FILE, LND_SOI_FILE SIG1T = 0.0 ! Not a dead start! @@ -375,7 +392,6 @@ SUBROUTINE SFCDRV(LUGB, IDIM,JDIM,LSM,LENSFC,LSOIL,DELTSFC, & CALL BAOPENR(37, "fort.37", IERR) READ (37, NML=NAMSFCD) - WRITE(6,NAMSFCD) PRINT* PRINT*,'IN ROUTINE SFCDRV,IDIM=',IDIM,'JDIM=',JDIM,'FH=',FH @@ -423,26 +439,47 @@ SUBROUTINE SFCDRV(LUGB, IDIM,JDIM,LSM,LENSFC,LSOIL,DELTSFC, & ALLOCATE(SLIFCS_FG(LENSFC)) ENDIF -IF (DO_LNDINC) THEN - ! CSD-todo: here determine types of increments being applied - PRINT* - PRINT*," APPLYING LAND INCREMENTS FROM THE GSI" - ALLOCATE(SOILSNOW_FG_MASK(LENSFC)) - ALLOCATE(SOILSNOW_MASK(LENSFC)) - ALLOCATE(STC_BCK(LENSFC, LSOIL), SMC_BCK(LENSFC, LSOIL), SLC_BCK(LENSFC,LSOIL)) +IF (DO_LNDINC) THEN + ! identify variables to be updates, and allocate arrays. + IF (TRIM(LND_SOI_FILE) .NE. "NULL") THEN + DO_SOI_INC = .TRUE. + PRINT* + PRINT*," APPLYING SOIL INCREMENTS FROM THE GSI" + ALLOCATE(STC_BCK(LENSFC, LSOIL), SMC_BCK(LENSFC, LSOIL), SLC_BCK(LENSFC,LSOIL)) + ALLOCATE(LANDINC_MASK_FG(LENSFC)) + ENDIF + ! FOR NOW, CODE SO CAN DO BOTH, BUT MIGHT NEED TO THINK ABOUT THIS SOME MORE. + IF (DO_SNO_INC) THEN + ! ideally, would check here that sfcsub snow DA update is not also requested + ! but latter is controlled by fnsol, which is read in within that routine. + ! should be done at script level. + PRINT* + PRINT*," APPLYING SNOW INCREMENTS FROM JEDI" + ALLOCATE(SND_BCK(LENSFC), SND_INC(LENSFC), SWE_BCK(LENSFC)) + ENDIF + ! set-up land mask info + ALLOCATE(LANDINC_MASK(LENSFC)) + if (ivegsrc == 2) then ! sib + veg_type_landice=13 + else + veg_type_landice=15 + endif ENDIF !-------------------------------------------------------------------------------- ! READ THE INPUT SURFACE DATA ON THE CUBED-SPHERE TILE. !-------------------------------------------------------------------------------- - CALL READ_DATA(TSFFCS,SMCFCS,SNOFCS,STCFCS,TG3FCS,ZORFCS, & - CVFCS,CVBFCS,CVTFCS,ALBFCS,SLIFCS, & - VEGFCS,CNPFCS,F10M,VETFCS,SOTFCS, & - ALFFCS,USTAR,FMM,FHH,SIHFCS,SICFCS, & - SITFCS,TPRCP,SRFLAG,SWDFCS,VMNFCS, & - VMXFCS,SLCFCS,SLPFCS,ABSFCS,T2M,Q2M, & - SLMASK,ZSOIL,LSOIL,LENSFC,DO_NSST,NSST) + CALL READ_DATA(LSOIL,LENSFC,DO_NSST,.false.,TSFFCS=TSFFCS,SMCFCS=SMCFCS, & + SWEFCS=SWEFCS,STCFCS=STCFCS,TG3FCS=TG3FCS,ZORFCS=ZORFCS, & + CVFCS=CVFCS, CVBFCS=CVBFCS,CVTFCS=CVTFCS,ALBFCS=ALBFCS, & + VEGFCS=VEGFCS,SLIFCS=SLIFCS,CNPFCS=CNPFCS,F10M=F10M , & + VETFCS=VETFCS,SOTFCS=SOTFCS,ALFFCS=ALFFCS,USTAR=USTAR , & + FMM=FMM ,FHH=FHH ,SIHFCS=SIHFCS,SICFCS=SICFCS, & + SITFCS=SITFCS,TPRCP=TPRCP ,SRFLAG=SRFLAG,SNDFCS=SNDFCS, & + VMNFCS=VMNFCS,VMXFCS=VMXFCS,SLCFCS=SLCFCS,SLPFCS=SLPFCS, & + ABSFCS=ABSFCS,T2M=T2M ,Q2M=Q2M ,SLMASK=SLMASK, & + ZSOIL=ZSOIL, NSST=NSST) IF (USE_UFO) THEN PRINT* @@ -470,8 +507,9 @@ SUBROUTINE SFCDRV(LUGB, IDIM,JDIM,LSM,LENSFC,LSOIL,DELTSFC, & ENDIF ! CALCULATE MASK FOR LAND INCREMENTS - IF (DO_LNDINC) & - CALL CALCULATE_SOILSNOWMASK(SLCFCS(:,1),SNOFCS, LENSFC, SOILSNOW_FG_MASK) + IF (DO_LNDINC) & + CALL CALCULATE_LANDINC_MASK(SLCFCS(:,1),SWEFCS, VETFCS, & + LENSFC,VEG_TYPE_LANDICE, LANDINC_MASK) !-------------------------------------------------------------------------------- ! UPDATE SURFACE FIELDS. @@ -483,9 +521,9 @@ SUBROUTINE SFCDRV(LUGB, IDIM,JDIM,LSM,LENSFC,LSOIL,DELTSFC, & CALL SFCCYCLE(LUGB,LENSFC,LSOIL,SIG1T,DELTSFC, & IY,IM,ID,IH,FH,RLA,RLO, & SLMASK,OROG, OROG_UF, USE_UFO, DO_NSST, & - SIHFCS,SICFCS,SITFCS,SWDFCS,SLCFCS, & + SIHFCS,SICFCS,SITFCS,SNDFCS,SLCFCS, & VMNFCS,VMXFCS,SLPFCS,ABSFCS, & - TSFFCS,SNOFCS,ZORFCS,ALBFCS,TG3FCS, & + TSFFCS,SWEFCS,ZORFCS,ALBFCS,TG3FCS, & CNPFCS,SMCFCS,STCFCS,SLIFCS,AISFCS, & VEGFCS,VETFCS,SOTFCS,ALFFCS, & CVFCS,CVBFCS,CVTFCS,MYRANK,NLUNIT, & @@ -537,76 +575,120 @@ SUBROUTINE SFCDRV(LUGB, IDIM,JDIM,LSM,LENSFC,LSOIL,DELTSFC, & ENDIF !-------------------------------------------------------------------------------- -! READ IN AND APPLY LAND INCEREMENTS FROM THE GSI +! READ IN AND APPLY LAND INCREMENTS FROM THE GSI !-------------------------------------------------------------------------------- - IF (DO_LNDINC) THEN + IF (DO_LNDINC) THEN -!-------------------------------------------------------------------------------- -! RE-CALCULATE SOILSNOW MASK AFTER SNOW UPDATE -!-------------------------------------------------------------------------------- + ! SNOW INCREMENTS + ! do snow first, as temperature updates will use snow analaysis + IF (DO_SNO_INC) THEN + ! updates are made to snow depth only over land (and not-land ice). + ! SWE is then updated from the snow depth analysis, using the model + ! forecast density - IF (DO_SFCCYCLE) THEN ! should really make this a snow DA flag, but this will do - CALL CALCULATE_SOILSNOWMASK(SLCFCS(:,1),SNOFCS, LENSFC, SOILSNOW_MASK ) - ELSE - SOILSNOW_MASK = SOILSNOW_FG_MASK - ENDIF + !-------------------------------------------------------------------------------- + ! read increments in + !-------------------------------------------------------------------------------- -!-------------------------------------------------------------------------------- -! read increments in -!-------------------------------------------------------------------------------- + ! Only coded for DA on native model grid (would always be the case for cycling DA) + CALL READ_DATA(LSOIL,LENSFC,.false.,.true.,SNDFCS=SND_INC) + + !-------------------------------------------------------------------------------- + ! add increments to state vars + !-------------------------------------------------------------------------------- + + ! store background states + SND_BCK = SNDFCS + SWE_BCK = SWEFCS + + CALL ADD_INCREMENT_SNOW(SND_INC,LANDINC_MASK,LENSFC,SNDFCS) + + !-------------------------------------------------------------------------------- + ! make any necessary adjustments to dependent variables + !-------------------------------------------------------------------------------- + + CALL APPLY_LAND_DA_ADJUSTMENTS_SND(LSM, LENSFC, LANDINC_MASK, SWE_BCK, SND_BCK, & + SNDFCS, SWEFCS) - INQUIRE(FILE=trim(LND_SOI_FILE), EXIST=file_exists) - IF (.not. file_exists) then - print *, 'FATAL ERROR: land increment update requested, but file does not exist: ', & - trim(lnd_soi_file) - call MPI_ABORT(MPI_COMM_WORLD, 10, IERR) ENDIF - CALL READ_GSI_DATA(LND_SOI_FILE, 'LND', LSOIL=LSOIL) + ! SOIL INCREMENTS + IF (DO_SOI_INC) THEN + !-------------------------------------------------------------------------------- + ! re-calculate soilsnow mask if snow has been updated. + !-------------------------------------------------------------------------------- -!-------------------------------------------------------------------------------- -! add increments to state vars -!-------------------------------------------------------------------------------- -! when applying increments, will often need to adjust other land states in response -! to the changes made. Need to store bacground, apply the increments, then make -! secondart adjustments. When updating more than one state, be careful about the -! orfer if increments and secondary adjustments. + LANDINC_MASK_FG = LANDINC_MASK -! store background states - STC_BCK = STCFCS - SMC_BCK = SMCFCS ! not used yet. - SLC_BCK = SLCFCS ! not used yet. + IF (DO_SFCCYCLE .OR. DO_SNO_INC) THEN + CALL CALCULATE_LANDINC_MASK(SLCFCS(:,1),SWEFCS, VETFCS, LENSFC, & + VEG_TYPE_LANDICE, LANDINC_MASK ) + ENDIF - CALL ADD_INCREMENT_SOIL(RLA,RLO,STCFCS,SOILSNOW_MASK,SOILSNOW_FG_MASK, & - LENSFC,LSOIL,IDIM,JDIM, MYRANK) + !-------------------------------------------------------------------------------- + ! read increments in + !-------------------------------------------------------------------------------- -!-------------------------------------------------------------------------------- -! make any necessary adjustments to dependent variables -!-------------------------------------------------------------------------------- + INQUIRE(FILE=trim(LND_SOI_FILE), EXIST=file_exists) + IF (.not. file_exists) then + print *, 'FATAL ERROR: land increment update requested, but file does not exist: ', & + trim(lnd_soi_file) + call MPI_ABORT(MPI_COMM_WORLD, 10, IERR) + ENDIF - CALL APPLY_LAND_DA_ADJUSTMENTS('stc', LSM, ISOT, IVEGSRC,LENSFC, LSOIL, & - SOTFCS,SMC_BCK, SLC_BCK,STC_BCK, SMCFCS, SLCFCS,STCFCS) + CALL READ_GSI_DATA(LND_SOI_FILE, 'LND', LSOIL=LSOIL) + + !-------------------------------------------------------------------------------- + ! add increments to state vars + !-------------------------------------------------------------------------------- + ! when applying increments, will often need to adjust other land states in response + ! to the changes made. Need to store bacground, apply the increments, then make + ! secondart adjustments. When updating more than one state, be careful about the + ! order if increments and secondary adjustments. + + ! store background states + STC_BCK = STCFCS + SMC_BCK = SMCFCS ! not used yet. + SLC_BCK = SLCFCS ! not used yet. + + CALL ADD_INCREMENT_SOIL(RLA,RLO,STCFCS,LANDINC_MASK,LANDINC_MASK_FG, & + LENSFC,LSOIL,IDIM,JDIM, MYRANK) + + !-------------------------------------------------------------------------------- + ! make any necessary adjustments to dependent variables + !-------------------------------------------------------------------------------- + + CALL APPLY_LAND_DA_ADJUSTMENTS_STC(LSM, ISOT, IVEGSRC,LENSFC, LSOIL, & + SOTFCS, LANDINC_MASK_FG, STC_BCK, STCFCS, SMCFCS, SLCFCS) + + ENDIF ! soil increments !-------------------------------------------------------------------------------- ! clean up !-------------------------------------------------------------------------------- ! to do - save and write out STC_INC? (soil temperature increments) - DEALLOCATE(SOILSNOW_FG_MASK, SOILSNOW_MASK) - DEALLOCATE(STC_BCK, SMC_BCK, SLC_BCK) + IF(ALLOCATED(LANDINC_MASK_FG)) DEALLOCATE(LANDINC_MASK_FG) + IF(ALLOCATED(LANDINC_MASK)) DEALLOCATE(LANDINC_MASK) + IF(ALLOCATED(STC_BCK)) DEALLOCATE(STC_BCK) + IF(ALLOCATED(SMC_BCK)) DEALLOCATE(SMC_BCK) + IF(ALLOCATED(SLC_BCK)) DEALLOCATE(SLC_BCK) + IF(ALLOCATED(SND_BCK)) DEALLOCATE(SND_BCK) + IF(ALLOCATED(SWE_BCK)) DEALLOCATE(SWE_BCK) + IF(ALLOCATED(SND_INC)) DEALLOCATE(SND_INC) - ENDIF + ENDIF !-------------------------------------------------------------------------------- ! WRITE OUT UPDATED SURFACE DATA ON THE CUBED-SPHERE TILE. !-------------------------------------------------------------------------------- - CALL WRITE_DATA(SLIFCS,TSFFCS,SNOFCS,TG3FCS,ZORFCS, & + CALL WRITE_DATA(SLIFCS,TSFFCS,SWEFCS,TG3FCS,ZORFCS, & ALBFCS,ALFFCS,VEGFCS,CNPFCS,F10M, & T2M,Q2M,VETFCS,SOTFCS,USTAR,FMM,FHH, & SICFCS,SIHFCS,SITFCS, & - TPRCP,SRFLAG,SWDFCS, & + TPRCP,SRFLAG,SNDFCS, & VMNFCS,VMXFCS,SLPFCS,ABSFCS, & SLCFCS,SMCFCS,STCFCS, & IDIM,JDIM,LENSFC,LSOIL,DO_NSST,NSST) diff --git a/sorc/global_cycle.fd/land_increments.f90 b/sorc/global_cycle.fd/land_increments.f90 index 2a898644f..688087002 100644 --- a/sorc/global_cycle.fd/land_increments.f90 +++ b/sorc/global_cycle.fd/land_increments.f90 @@ -2,28 +2,37 @@ !! @brief Routines for applyng land DA increments !! @author Clara Draper ESRL/PSL -module land_increments +module land_increments - private + private public add_increment_soil - public calculate_soilsnowmask - public apply_land_da_adjustments + public add_increment_snow + public calculate_landinc_mask + public apply_land_da_adjustments_stc + public apply_land_da_adjustments_snd -contains + integer, parameter :: lsm_noah=1 !< flag for NOAH land surface model + !! copied from GFS_typedefs.F90 +contains !> Read in gsi file with soil state increments (on the gaussian !! grid), interpolate increments to the cubed-sphere tile, and - !! add to the soil states. Adapted from adjust_nsst. - !! Currently only coded for soil temperature. Soil moisture will + !! add to the soil states. Adapted from adjust_nsst. + !! Currently only coded for soil temperature. Soil moisture will !! need the model soil moisture paramaters for regridding. !! + !! Does not make a temperature update if snow differ + !! between fg and anal (allow correction of snow to + !! address temperature error first), or if snow is present + !! (will eventually updating of snow temperature in this case) + !! !! @param[inout] RLA Latitude on the cubed-sphere tile !! @param[inout] RLO Longitude on the cubed-sphere tile - !! @param[inout] STC_STATE - !! @param[in] SOILSNOW_TILE Snow mask for land on the cubed-sphere tile - !! @param[in] SOILSNOW_FG_TILE First guess snow mask for land on the cubed-sphere tile - !! @param[in] LENSFC Number of points on a tile + !! @param[inout] STC_STATE + !! @param[in] SOILSNOW_TILE Land mask for increments on the cubed-sphere tile + !! @param[in] SOILSNOW_FG_TILE First guess land mask for increments on the cubed-sphere tile + !! @param[in] LENSFC Number of land points on a tile !! @param[in] LSOIL Number of soil layers !! @param[in] IDIM 'I' dimension of a tile !! @param[in] JDIM 'J' dimension of a tile @@ -31,13 +40,13 @@ module land_increments !! !! @author Clara Draper. @date March 2021 -subroutine add_increment_soil(rla,rlo,stc_state,soilsnow_tile, soilsnow_fg_tile, & - lensfc,lsoil,idim,jdim, myrank) +subroutine add_increment_soil(rla,rlo,stc_state,soilsnow_tile, soilsnow_fg_tile, & + lensfc,lsoil,idim,jdim, myrank) use utils use gdswzd_mod use read_write_data, only : idim_gaus, jdim_gaus, & - stc_inc_gaus, soilsnow_gaus + stc_inc_gaus, soilsnow_gaus use mpi implicit none @@ -58,7 +67,7 @@ subroutine add_increment_soil(rla,rlo,stc_state,soilsnow_tile, soilsnow_fg_tile, integer, allocatable :: id1(:,:), id2(:,:), jdc(:,:) - real :: wsum + real :: wsum real :: stc_inc(lsoil) real, allocatable :: xpts(:), ypts(:), lats(:), lons(:) real, allocatable :: dum2d(:,:), lats_rad(:), lons_rad(:) @@ -118,7 +127,7 @@ subroutine add_increment_soil(rla,rlo,stc_state,soilsnow_tile, soilsnow_fg_tile, dum2d = reshape(lats, (/idim_gaus,jdim_gaus/) ) deallocate(lats) - allocate(lats_rad(jdim_gaus)) + allocate(lats_rad(jdim_gaus)) do j = 1, jdim_gaus lats_rad(j) = dum2d(1,jdim_gaus-j+1) * 3.1415926 / 180.0 @@ -155,32 +164,27 @@ subroutine add_increment_soil(rla,rlo,stc_state,soilsnow_tile, soilsnow_fg_tile, ! initialize variables for counts statitics to be zeros ! - ! + ! nother = 0 ! grid cells not land - nsnowupd = 0 ! grid cells with snow (temperature not yet updated) - nsnowchange = 0 ! grid cells where no temp upd made, because snow occurence changed - nnosoilnear = 0 ! grid cells where model has soil, but 4 closest gaus grids don't + nsnowupd = 0 ! grid cells with snow (temperature not yet updated) + nsnowchange = 0 ! grid cells where no temp upd made, because snow occurence changed + nnosoilnear = 0 ! grid cells where model has soil, but 4 closest gaus grids don't ! (no update made here) - nsoilupd = 0 + nsoilupd = 0 ij_loop : do ij = 1, lensfc - ! for now, do not make a temperature update if snow differs - ! between fg and anal (allow correction of snow to - ! address temperature error first) - - mask_tile = soilsnow_tile(ij) mask_fg_tile = soilsnow_fg_tile(ij) !---------------------------------------------------------------------- - ! mask: 1 - soil, 2 - snow, 0 - neither + ! mask: 1 - soil, 2 - snow, 0 - land-ice, -1 - not land !---------------------------------------------------------------------- - if (mask_tile == 0) then ! skip if neither soil nor snow + if (mask_tile <= 0) then ! skip if neither soil nor snow nother = nother + 1 - cycle ij_loop + cycle ij_loop endif @@ -191,18 +195,18 @@ subroutine add_increment_soil(rla,rlo,stc_state,soilsnow_tile, soilsnow_fg_tile, if (itile==0) itile = idim !---------------------------------------------------------------------- - ! if the snow analysis has chnaged to occurence of snow, skip the + ! if the snow analysis has chnaged to occurence of snow, skip the ! temperature analysis !---------------------------------------------------------------------- - if ((mask_fg_tile == 2 .and. mask_tile == 1) .or. & + if ((mask_fg_tile == 2 .and. mask_tile == 1) .or. & (mask_fg_tile == 1 .and. mask_tile == 2) ) then nsnowchange = nsnowchange + 1 - cycle ij_loop + cycle ij_loop endif !---------------------------------------------------------------------- - ! do update to soil temperature grid cells, using bilinear interp + ! do update to soil temperature grid cells, using bilinear interp !---------------------------------------------------------------------- if (mask_tile == 1) then @@ -212,25 +216,25 @@ subroutine add_increment_soil(rla,rlo,stc_state,soilsnow_tile, soilsnow_fg_tile, igausp1 = id2(itile,jtile) jgausp1 = jdc(itile,jtile)+1 - ! make sure gaus grid has soil nearby + ! make sure gaus grid has soil nearby gaus_has_soil = .false. if (soilsnow_gaus(igaus,jgaus) == 1 .or. & soilsnow_gaus(igausp1,jgaus) == 1 .or. & soilsnow_gaus(igausp1,jgausp1) == 1 .or. & - soilsnow_gaus(igaus,jgausp1) == 1) gaus_has_soil = .true. + soilsnow_gaus(igaus,jgausp1) == 1) gaus_has_soil = .true. if (.not. gaus_has_soil) then - nnosoilnear = nnosoilnear + 1 - cycle ij_loop + nnosoilnear = nnosoilnear + 1 + cycle ij_loop endif ! calcualate weighted increment over nearby grid cells that have soil - ! Draper: to-do, code adding increments to soil moisture. - ! will require converting to soil wetness index first + ! Draper: to-do, code adding increments to soil moisture. + ! will require converting to soil wetness index first ! (need to add soil properties to the increment file) - nsoilupd = nsoilupd + 1 + nsoilupd = nsoilupd + 1 stc_inc = 0.0 wsum = 0.0 @@ -270,20 +274,20 @@ subroutine add_increment_soil(rla,rlo,stc_state,soilsnow_tile, soilsnow_fg_tile, ! todo, apply some bounds? enddo + ! don't update soil states if snow present. elseif(mask_tile==2) then - !print *, 'csd2', rlo(ij), rla(ij) - nsnowupd = nsnowupd + 1 + nsnowupd = nsnowupd + 1 endif ! if soil/snow point enddo ij_loop write(*,'(a,i2)') 'statistics of grids number processed for rank : ', myrank - write(*,'(a,i8)') ' soil grid cells updated = ',nsoilupd + write(*,'(a,i8)') ' soil grid cells updated = ',nsoilupd write(*,'(a,i8)') ' (not updated) soil grid cells, no soil nearby on gsi grid = ',nnosoilnear write(*,'(a,i8)') ' (not updated) soil grid cells, change in presence of snow = ', nsnowchange write(*,'(a,i8)') ' (not updated yet) snow grid cells = ', nsnowupd - write(*,'(a,i8)') ' grid cells, without soil of snow = ', nother + write(*,'(a,i8)') ' grid cells, without soil or snow = ', nother nother = 0 ! grid cells not land nsnowupd = 0 ! grid cells where no temp upd made, because snow occurence changed @@ -295,62 +299,100 @@ subroutine add_increment_soil(rla,rlo,stc_state,soilsnow_tile, soilsnow_fg_tile, end subroutine add_increment_soil -!> Calculate soil mask for land on model grid. -!! Output is 1 - soil, 2 - snow-covered, 0 - land ice or not land. -!! @param[in] lensfc Total numberof points for the cubed-sphere tile. + !> Add snow depth increment to model snow depth state, + !! and limit output to be non-negative. JEDI increments are + !! calculated globally, so must be screened to land-only locations + !! here. + !! + !! @param[in] lensfc Number of land points on this tile + !! @param[in] snd_inc Soil depth increments + !! @param[in] mask Land mask for increments + !! @param[inout] snd Soil depth background (in), and analysis (out) + !! + !! @author Clara Draper. @date August 2021 + +subroutine add_increment_snow(snd_inc,mask,lensfc,snd) + + implicit none + + integer, intent(in) :: lensfc + real, intent(in) :: snd_inc(lensfc) + integer, intent(in) :: mask(lensfc) + real, intent(inout) :: snd(lensfc) + + integer :: i + + + do i =1, lensfc + if (mask(i) > 0) then ! if land + snd(i) = max( snd(i) + snd_inc(i), 0.) + endif + enddo + +end subroutine add_increment_snow + +!> Calculate soil mask for land on model grid. +!! Output is 1 - soil, 2 - snow-covered, 0 - land ice, -1 not land. +!! +!! @param[in] lensfc Number of land points for this tile +!! @param[in] veg_type_landice Value of vegetion class that indicates land-ice !! @param[in] smc Model soil moisture. !! @param[in] swe Model snow water equivalent -!! @param[out] mask Output mask: 1 - soil, 2 - snow-covered, 0 - land ice or not land. +!! @param[in] vtype Model vegetation type +!! @param[out] mask Land mask for increments !! @author Clara Draper @date March 2021 -subroutine calculate_soilsnowmask(smc,swe,lensfc,mask) +subroutine calculate_landinc_mask(smc,swe,vtype,lensfc,veg_type_landice,mask) - implicit none + implicit none - integer, intent(in) :: lensfc - real, intent(in) :: smc(lensfc), swe(lensfc) - integer, intent(out) :: mask(lensfc) + integer, intent(in) :: lensfc, veg_type_landice + real, intent(in) :: smc(lensfc), swe(lensfc) + real, intent(in) :: vtype(lensfc) + integer, intent(out) :: mask(lensfc) integer :: i - mask = 0 - do i=1,lensfc - if (smc(i) .LT. 1.0) then - mask(i) = 1 - endif - end do + mask = -1 ! not land + ! land (but not land-ice) do i=1,lensfc - if (swe(i) .GT. 0.001) then - mask(i) = 2 + if (smc(i) .LT. 1.0) then + if (swe(i) .GT. 0.001) then ! snow covered land + mask(i) = 2 + else ! non-snow covered land + mask(i) = 1 + endif + end if ! else should work here too + if ( nint(vtype(i)) == veg_type_landice ) then ! land-ice + mask(i) = 0 endif end do -end subroutine calculate_soilsnowmask +end subroutine calculate_landinc_mask !> Make adjustments to dependent variables after applying land increments. -!! These adjustments are model-dependent, and are currently only coded -!! for Noah LSM. -!! For Noah LSM, copy relevent code blocks from model code (same as has -!! been done in sfc_sub). For Noah-MP, will call into the model code -!! to use same routines / code as in the model. - -!> @param[in] update_type Code for variable being updated (options: 'stc' - soil temperature) -!! @param[in] lsm Integer code for the LSM +!! These adjustments are model-dependent, and are currently only coded +!! for Noah LSM. +!! For Noah LSM, copy relevent code blocks from model code (same as has +!! been done in sfc_sub). +!! Here: adjust (frozen) soil moisture to be consistent with changes in +!! soil temperature from DA + +!> @param[in] lsm Integer code for the LSM !! @param[in] isot Integer code for the soil type data set !! @param[in] ivegsrc Integer code for the vegetation type data set -!! @param[in] lensfc Length of land state vector -!! @param[in] lsoil Number of soil layers -!! @param[in] rsoiltype rsoiltype Array of input soil types -!! @param[in] smc_bck Background soil moisture states -!! @param[in] slc_bck Background liquid soil moisture states -!! @param[in] stc_bck Background soil temperature states -!! @param[inout] smc_anl Analysis soil moisture states -!! @param[inout] slc_anl Analysis liquid soil moisture states -!! @param[inout] stc_anl Analysis soil temperature states +!! @param[in] lensfc Number of land points for this tile +!! @param[in] lsoil Number of soil layers +!! @param[in] rsoiltype Array of input soil types +!! @param[in] mask Mask indicating surface type +!! @param[in] stc_bck Background soil temperature states +!! @param[in] stc_anl Analysis soil temperature states +!! @param[inout] smc_adj Soil moisture state to be adjusted +!! @param[inout] slc_adj Liquid soil moisture states to be adjusted !! @author Clara Draper @date April 2021 -subroutine apply_land_da_adjustments(update_type, lsm, isot, ivegsrc,lensfc, & - lsoil, rsoiltype, smc_bck, slc_bck,stc_bck, smc_anl, slc_anl, stc_anl) +subroutine apply_land_da_adjustments_stc(lsm, isot, ivegsrc,lensfc, & + lsoil, rsoiltype, mask, stc_bck, stc_anl, smc_adj, slc_adj ) use mpi use set_soilveg_snippet_mod, only: set_soilveg @@ -358,27 +400,24 @@ subroutine apply_land_da_adjustments(update_type, lsm, isot, ivegsrc,lensfc, & implicit none - character(len=3), intent(in) :: update_type integer, intent(in) :: lsm, lensfc, lsoil, isot, ivegsrc real, intent(in) :: rsoiltype(lensfc) ! soil types, as real - real, intent(in) :: smc_bck(lensfc,lsoil), slc_bck(lensfc,lsoil) - real, intent(in) :: stc_bck(lensfc, lsoil) - real, intent(inout) :: smc_anl(lensfc,lsoil), slc_anl(lensfc,lsoil) - real, intent(inout) :: stc_anl(lensfc, lsoil) + integer, intent(in) :: mask(lensfc) + real, intent(in) :: stc_bck(lensfc, lsoil) , stc_anl(lensfc, lsoil) + real, intent(inout) :: smc_adj(lensfc,lsoil), slc_adj(lensfc,lsoil) + logical :: frzn_bck, frzn_anl - integer :: i, l, n_freeze, n_thaw, ierr + integer :: i, l, n_freeze, n_thaw, ierr integer :: myrank, soiltype, iret real :: slc_new - integer, parameter :: lsm_noah=1 !< flag for NOAH land surface model - !! copied from GFS_typedefs.F90 real, parameter :: tfreez=273.16 !< con_t0c in physcons real, dimension(30) :: maxsmc, bb, satpsi - call mpi_comm_rank(mpi_comm_world, myrank, ierr) + call mpi_comm_rank(mpi_comm_world, myrank, ierr) if (lsm .NE. lsm_noah) then print *, 'FATAL ERROR: apply_land_da_adjustments not coded for models other than noah', lsm @@ -386,52 +425,94 @@ subroutine apply_land_da_adjustments(update_type, lsm, isot, ivegsrc,lensfc, & endif ! initialise soil properties - call set_soilveg(isot, ivegsrc, maxsmc, bb, satpsi, iret) + call set_soilveg(isot, ivegsrc, maxsmc, bb, satpsi, iret) if (iret < 0) then print *, 'FATAL ERROR: problem in set_soilveg' call mpi_abort(mpi_comm_world, 10, ierr) endif - select case (update_type) + print *, 'Adjusting smc after stc DA update' - case ('stc') - print *, 'Adjusting smc after stc DA update' + n_freeze = 0 + n_thaw = 0 + + do i=1,lensfc + if (mask(i) > 0) then ! if soil location + do l = 1, lsoil + frzn_bck = (stc_bck(i,l) .LT. tfreez ) + frzn_anl = (stc_anl(i,l) .LT. tfreez ) + + if (frzn_bck .eqv. frzn_anl) then + cycle + elseif (frzn_bck .and. .not. frzn_anl) then + n_thaw = n_thaw + 1 + else + n_freeze = n_freeze + 1 + endif - n_freeze = 0 - n_thaw = 0 - - do i=1,lensfc - do l = 1, lsoil - if (smc_bck(i,l) < 1.0) then ! if soil location - frzn_bck = (stc_bck(i,l) .LT. tfreez ) - frzn_anl = (stc_anl(i,l) .LT. tfreez ) - - if (frzn_bck .eqv. frzn_anl) then - cycle - elseif (frzn_bck .and. .not. frzn_anl) then - n_thaw = n_thaw + 1 - else - n_freeze = n_freeze + 1 - endif - - ! make adjustment (same routine for both) - soiltype = nint(rsoiltype(i)) - ! bb and maxsmc are in the namelist_soilveg, need soiltype index - call frh2o(stc_anl(i,l), smc_anl(i,l),slc_anl(i,l), maxsmc(soiltype), & - bb(soiltype), satpsi(soiltype),slc_new) - - slc_anl(i,l) = max( min( slc_new, smc_anl(i,l)), 0.0 ) - endif - enddo - enddo - - print *, 'adjusted: ', n_thaw,' thawed,', n_freeze, ' frozen' + ! make adjustment (same routine for both) + soiltype = nint(rsoiltype(i)) + ! bb and maxsmc are in the namelist_soilveg, need soiltype index + call frh2o(stc_anl(i,l), smc_adj(i,l),slc_adj(i,l), maxsmc(soiltype), & + bb(soiltype), satpsi(soiltype),slc_new) + + slc_adj(i,l) = max( min( slc_new, smc_adj(i,l)), 0.0 ) + enddo + endif + enddo + print *, 'adjusted: ', n_thaw,' thawed,', n_freeze, ' frozen' - case default - print *, 'FATAL ERROR: apply_land_da_adjustments not code for variable', lsm - call MPI_ABORT(MPI_COMM_WORLD, 10, IERR) - end select +end subroutine apply_land_da_adjustments_stc + +!> Make adjustments to dependent variables after applying land increments. +!! These adjustments are model-dependent, and are currently only coded +!! for Noah LSM. +!! Here: adjust SWE to be consistent with updated SND, using snow density +!! from the forecast. + +!> @param[in] lsm Integer code for the LSM +!! @param[in] lensfc Number of land points for this tile +!! @param[in] mask Land mask for increments +!! @param[in] swe_bck Background SWE +!! @param[in] snd_bck Background snow depth +!! @param[in] snd_anl Analysis snow depth +!! @param[inout] swe_adj SWE to be adjusted +!! @author Clara Draper @date August 2021 + +subroutine apply_land_da_adjustments_snd(lsm, lensfc, mask, swe_bck, snd_bck, snd_anl, swe_adj) + + use mpi + use bulk_snow_module, only: calc_density + + implicit none + + integer, intent(in) :: lsm, lensfc + integer, intent(in) :: mask(lensfc) + real, intent(in) :: swe_bck(lensfc), snd_bck(lensfc) + real, intent(in) :: snd_anl(lensfc) + real, intent(inout) :: swe_adj(lensfc) + + integer :: ierr, myrank, i + + real :: density(lensfc) + + call mpi_comm_rank(mpi_comm_world, myrank, ierr) + + if (lsm .NE. lsm_noah) then + print *, 'FATAL ERROR: apply_land_da_adjustments not coded for models other than noah', lsm + call mpi_abort(mpi_comm_world, 10, ierr) + endif + + ! calculate snow density from forecasts + call calc_density(lensfc, mask, swe_bck, snd_bck, myrank, density) + + do i =1, lensfc + if ( mask(i)>0 ) then + swe_adj(i) = snd_anl(i)*density(i) + endif + enddo + -end subroutine apply_land_da_adjustments +end subroutine apply_land_da_adjustments_snd end module land_increments diff --git a/sorc/global_cycle.fd/read_write_data.f90 b/sorc/global_cycle.fd/read_write_data.f90 index 0ac762f57..340d511b7 100644 --- a/sorc/global_cycle.fd/read_write_data.f90 +++ b/sorc/global_cycle.fd/read_write_data.f90 @@ -74,7 +74,7 @@ MODULE READ_WRITE_DATA !! !! @param[in] slifcs Land-sea mask. !! @param[in] tsffcs Skin temperature. - !! @param[in] snofcs Liquid-equivalent snow depth. + !! @param[in] swefcs Snow water equivalent !! @param[in] tg3fcs Soil substrate temperature. !! @param[in] zorfcs Roughness length. !! @param[in] albfcs Snow-free albedo. @@ -114,7 +114,7 @@ MODULE READ_WRITE_DATA !! @param[in] nsst Data structure containing nsst fields. !! !! @author George Gayno NOAA/EMC - subroutine write_data(slifcs,tsffcs,snofcs,tg3fcs,zorfcs, & + subroutine write_data(slifcs,tsffcs,swefcs,tg3fcs,zorfcs, & albfcs,alffcs,vegfcs,cnpfcs,f10m, & t2m,q2m,vetfcs,sotfcs,ustar,fmm,fhh, & sicfcs,sihfcs,sitfcs,tprcp,srflag, & @@ -132,7 +132,7 @@ subroutine write_data(slifcs,tsffcs,snofcs,tg3fcs,zorfcs, & logical, intent(in) :: do_nsst real, intent(in) :: slifcs(lensfc), tsffcs(lensfc) - real, intent(in) :: snofcs(lensfc), tg3fcs(lensfc) + real, intent(in) :: swefcs(lensfc), tg3fcs(lensfc) real, intent(in) :: vegfcs(lensfc), cnpfcs(lensfc) real, intent(in) :: zorfcs(lensfc), albfcs(lensfc,4) real, intent(in) :: f10m(lensfc), alffcs(lensfc,2) @@ -676,7 +676,7 @@ subroutine write_data(slifcs,tsffcs,snofcs,tg3fcs,zorfcs, & error = nf90_put_var( ncid, id_tsea, dum2d, dims_strt, dims_end) call netcdf_err(error, 'WRITING TSEA RECORD' ) - dum2d = reshape(snofcs, (/idim,jdim/)) + dum2d = reshape(swefcs, (/idim,jdim/)) error = nf90_put_var( ncid, id_sheleg, dum2d, dims_strt, dims_end) call netcdf_err(error, 'WRITING SHELEG RECORD' ) @@ -1200,9 +1200,11 @@ END SUBROUTINE READ_GSI_DATA !! @param[in] LSOIL Number of soil layers. !! @param[in] LENSFC Total number of points on a tile. !! @param[in] DO_NSST When true, nsst fields are read. + !! @param[in] INC_FILE When true, read from an increment file. + !! False reads from a restart file. !! @param[out] TSFFCS Skin Temperature. !! @param[out] SMCFCS Total volumetric soil moisture. - !! @param[out] SNOFCS Liquid-equivalent snow depth. + !! @param[out] SWEFCS Snow water equivalent. !! @param[out] STCFCS Soil temperature. !! @param[out] TG3FCS Soil substrate temperature. !! @param[out] ZORFCS Roughness length. @@ -1227,7 +1229,7 @@ END SUBROUTINE READ_GSI_DATA !! @param[out] SITFCS Sea ice temperature. !! @param[out] TPRCP Precipitation. !! @param[out] SRFLAG Snow/rain flag. - !! @param[out] SWDFCS Physical snow depth. + !! @param[out] SNDFCS Snow depth. !! @param[out] VMNFCS Minimum vegetation greenness. !! @param[out] VMXFCS Maximum vegetation greenness. !! @param[out] SLCFCS Liquid portion of volumetric soil moisture. @@ -1239,46 +1241,46 @@ END SUBROUTINE READ_GSI_DATA !! @param[out] ZSOIL Soil layer thickness. !! @param[out] NSST Data structure containing nsst fields. !! @author George Gayno NOAA/EMC - SUBROUTINE READ_DATA(TSFFCS,SMCFCS,SNOFCS,STCFCS, & + SUBROUTINE READ_DATA(LSOIL,LENSFC,DO_NSST,INC_FILE,TSFFCS,SMCFCS,SWEFCS,STCFCS, & TG3FCS,ZORFCS, & CVFCS,CVBFCS,CVTFCS,ALBFCS, & - SLIFCS,VEGFCS,CNPFCS,F10M, & + VEGFCS,SLIFCS,CNPFCS,F10M, & VETFCS,SOTFCS,ALFFCS, & USTAR,FMM,FHH, & SIHFCS,SICFCS,SITFCS, & - TPRCP,SRFLAG,SWDFCS, & + TPRCP,SRFLAG,SNDFCS, & VMNFCS,VMXFCS,SLCFCS, & SLPFCS,ABSFCS,T2M,Q2M,SLMASK, & - ZSOIL,LSOIL,LENSFC,DO_NSST,NSST) + ZSOIL,NSST) USE MPI IMPLICIT NONE INTEGER, INTENT(IN) :: LSOIL, LENSFC - - LOGICAL, INTENT(IN) :: DO_NSST - - REAL, INTENT(OUT) :: CVFCS(LENSFC), CVBFCS(LENSFC) - REAL, INTENT(OUT) :: CVTFCS(LENSFC), ALBFCS(LENSFC,4) - REAL, INTENT(OUT) :: SLIFCS(LENSFC), CNPFCS(LENSFC) - REAL, INTENT(OUT) :: VEGFCS(LENSFC), F10M(LENSFC) - REAL, INTENT(OUT) :: VETFCS(LENSFC), SOTFCS(LENSFC) - REAL, INTENT(OUT) :: TSFFCS(LENSFC), SNOFCS(LENSFC) - REAL, INTENT(OUT) :: TG3FCS(LENSFC), ZORFCS(LENSFC) - REAL, INTENT(OUT) :: ALFFCS(LENSFC,2), USTAR(LENSFC) - REAL, INTENT(OUT) :: FMM(LENSFC), FHH(LENSFC) - REAL, INTENT(OUT) :: SIHFCS(LENSFC), SICFCS(LENSFC) - REAL, INTENT(OUT) :: SITFCS(LENSFC), TPRCP(LENSFC) - REAL, INTENT(OUT) :: SRFLAG(LENSFC), SWDFCS(LENSFC) - REAL, INTENT(OUT) :: VMNFCS(LENSFC), VMXFCS(LENSFC) - REAL, INTENT(OUT) :: SLPFCS(LENSFC), ABSFCS(LENSFC) - REAL, INTENT(OUT) :: T2M(LENSFC), Q2M(LENSFC), SLMASK(LENSFC) - REAL, INTENT(OUT) :: SLCFCS(LENSFC,LSOIL) - REAL, INTENT(OUT) :: SMCFCS(LENSFC,LSOIL) - REAL, INTENT(OUT) :: STCFCS(LENSFC,LSOIL) - REAL(KIND=4), INTENT(OUT) :: ZSOIL(LSOIL) - - TYPE(NSST_DATA) :: NSST + LOGICAL, INTENT(IN) :: DO_NSST, INC_FILE + + REAL, OPTIONAL, INTENT(OUT) :: CVFCS(LENSFC), CVBFCS(LENSFC) + REAL, OPTIONAL, INTENT(OUT) :: CVTFCS(LENSFC), ALBFCS(LENSFC,4) + REAL, OPTIONAL, INTENT(OUT) :: SLIFCS(LENSFC), CNPFCS(LENSFC) + REAL, OPTIONAL, INTENT(OUT) :: VEGFCS(LENSFC), F10M(LENSFC) + REAL, OPTIONAL, INTENT(OUT) :: VETFCS(LENSFC), SOTFCS(LENSFC) + REAL, OPTIONAL, INTENT(OUT) :: TSFFCS(LENSFC), SWEFCS(LENSFC) + REAL, OPTIONAL, INTENT(OUT) :: TG3FCS(LENSFC), ZORFCS(LENSFC) + REAL, OPTIONAL, INTENT(OUT) :: ALFFCS(LENSFC,2), USTAR(LENSFC) + REAL, OPTIONAL, INTENT(OUT) :: FMM(LENSFC), FHH(LENSFC) + REAL, OPTIONAL, INTENT(OUT) :: SIHFCS(LENSFC), SICFCS(LENSFC) + REAL, OPTIONAL, INTENT(OUT) :: SITFCS(LENSFC), TPRCP(LENSFC) + REAL, OPTIONAL, INTENT(OUT) :: SRFLAG(LENSFC), SNDFCS(LENSFC) + REAL, OPTIONAL, INTENT(OUT) :: VMNFCS(LENSFC), VMXFCS(LENSFC) + REAL, OPTIONAL, INTENT(OUT) :: SLPFCS(LENSFC), ABSFCS(LENSFC) + REAL, OPTIONAL, INTENT(OUT) :: T2M(LENSFC), Q2M(LENSFC), SLMASK(LENSFC) + REAL, OPTIONAL, INTENT(OUT) :: SLCFCS(LENSFC,LSOIL) + REAL, OPTIONAL, INTENT(OUT) :: SMCFCS(LENSFC,LSOIL) + REAL, OPTIONAL, INTENT(OUT) :: STCFCS(LENSFC,LSOIL) + REAL(KIND=4), OPTIONAL, INTENT(OUT) :: ZSOIL(LSOIL) + + TYPE(NSST_DATA), OPTIONAL :: NSST ! intent(out) will crash + ! because subtypes are allocated in main. CHARACTER(LEN=50) :: FNBGSI CHARACTER(LEN=3) :: RANKCH @@ -1292,8 +1294,12 @@ SUBROUTINE READ_DATA(TSFFCS,SMCFCS,SNOFCS,STCFCS, & CALL MPI_COMM_RANK(MPI_COMM_WORLD, MYRANK, ERROR) WRITE(RANKCH, '(I3.3)') (MYRANK+1) - - FNBGSI = "./fnbgsi." // RANKCH + + IF (INC_FILE) THEN + FNBGSI = "./xainc." // RANKCH + ELSE + FNBGSI = "./fnbgsi." // RANKCH + ENDIF PRINT* PRINT*, "READ INPUT SFC DATA FROM: "//TRIM(FNBGSI) @@ -1318,29 +1324,39 @@ SUBROUTINE READ_DATA(TSFFCS,SMCFCS,SNOFCS,STCFCS, & ALLOCATE(DUMMY(IDIM,JDIM)) + IF (PRESENT(TSFFCS)) THEN ERROR=NF90_INQ_VARID(NCID, "tsea", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING tsea ID' ) ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy) CALL NETCDF_ERR(ERROR, 'READING tsea' ) TSFFCS = RESHAPE(DUMMY, (/LENSFC/)) + ENDIF + IF (PRESENT(SWEFCS)) THEN ERROR=NF90_INQ_VARID(NCID, "sheleg", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING sheleg ID' ) ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy) CALL NETCDF_ERR(ERROR, 'READING sheleg' ) - SNOFCS = RESHAPE(DUMMY, (/LENSFC/)) + SWEFCS = RESHAPE(DUMMY, (/LENSFC/)) + ENDIF + IF (PRESENT(TG3FCS)) THEN ERROR=NF90_INQ_VARID(NCID, "tg3", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING tg3 ID' ) ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy) CALL NETCDF_ERR(ERROR, 'READING tg3' ) TG3FCS = RESHAPE(DUMMY, (/LENSFC/)) + ENDIF + IF (PRESENT(ZORFCS)) THEN ERROR=NF90_INQ_VARID(NCID, "zorl", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING zorl ID' ) ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy) CALL NETCDF_ERR(ERROR, 'READING zorl' ) ZORFCS = RESHAPE(DUMMY, (/LENSFC/)) + ENDIF + + IF (PRESENT(ALBFCS)) THEN ERROR=NF90_INQ_VARID(NCID, "alvsf", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING alvsf ID' ) @@ -1366,6 +1382,9 @@ SUBROUTINE READ_DATA(TSFFCS,SMCFCS,SNOFCS,STCFCS, & CALL NETCDF_ERR(ERROR, 'READING alnwf' ) ALBFCS(:,4) = RESHAPE(DUMMY, (/LENSFC/)) + ENDIF + + IF (PRESENT(SLIFCS)) THEN ERROR=NF90_INQ_VARID(NCID, "slmsk", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING slmsk ID' ) ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy) @@ -1373,37 +1392,49 @@ SUBROUTINE READ_DATA(TSFFCS,SMCFCS,SNOFCS,STCFCS, & SLIFCS = RESHAPE(DUMMY, (/LENSFC/)) SLMASK = SLIFCS WHERE (SLMASK > 1.5) SLMASK=0.0 ! remove sea ice - + ENDIF + + IF (PRESENT(CNPFCS)) THEN ERROR=NF90_INQ_VARID(NCID, "canopy", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING canopy ID' ) ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy) CALL NETCDF_ERR(ERROR, 'READING canopy' ) CNPFCS = RESHAPE(DUMMY, (/LENSFC/)) - + ENDIF + + IF (PRESENT(VEGFCS)) THEN ERROR=NF90_INQ_VARID(NCID, "vfrac", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING vfrac ID' ) ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy) CALL NETCDF_ERR(ERROR, 'READING vfrac' ) VEGFCS = RESHAPE(DUMMY, (/LENSFC/)) - + ENDIF + + IF (PRESENT(F10M)) THEN ERROR=NF90_INQ_VARID(NCID, "f10m", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING f10m ID' ) ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy) CALL NETCDF_ERR(ERROR, 'READING f10m' ) F10M = RESHAPE(DUMMY, (/LENSFC/)) - + ENDIF + + IF (PRESENT(VETFCS)) THEN ERROR=NF90_INQ_VARID(NCID, "vtype", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING vtype ID' ) ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy) CALL NETCDF_ERR(ERROR, 'READING vtype' ) VETFCS = RESHAPE(DUMMY, (/LENSFC/)) - + ENDIF + + IF (PRESENT(SOTFCS)) THEN ERROR=NF90_INQ_VARID(NCID, "stype", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING stype ID' ) ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy) CALL NETCDF_ERR(ERROR, 'READING stype' ) SOTFCS = RESHAPE(DUMMY, (/LENSFC/)) - + ENDIF + + IF (PRESENT(ALFFCS)) THEN ERROR=NF90_INQ_VARID(NCID, "facsf", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING facsf ID' ) ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy) @@ -1415,96 +1446,127 @@ SUBROUTINE READ_DATA(TSFFCS,SMCFCS,SNOFCS,STCFCS, & ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy) CALL NETCDF_ERR(ERROR, 'READING facwf' ) ALFFCS(:,2) = RESHAPE(DUMMY, (/LENSFC/)) - + ENDIF + + IF (PRESENT(USTAR)) THEN ERROR=NF90_INQ_VARID(NCID, "uustar", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING uustar ID' ) ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy) CALL NETCDF_ERR(ERROR, 'READING uustar' ) USTAR = RESHAPE(DUMMY, (/LENSFC/)) - + ENDIF + + IF (PRESENT(FMM)) THEN ERROR=NF90_INQ_VARID(NCID, "ffmm", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING ffmm ID' ) ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy) CALL NETCDF_ERR(ERROR, 'READING ffmm' ) FMM = RESHAPE(DUMMY, (/LENSFC/)) - + ENDIF + + IF (PRESENT(FHH)) THEN ERROR=NF90_INQ_VARID(NCID, "ffhh", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING ffhh ID' ) ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy) CALL NETCDF_ERR(ERROR, 'READING ffhh' ) FHH = RESHAPE(DUMMY, (/LENSFC/)) - + ENDIF + + IF (PRESENT(SIHFCS)) THEN ERROR=NF90_INQ_VARID(NCID, "hice", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING hice ID' ) ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy) CALL NETCDF_ERR(ERROR, 'READING hice' ) SIHFCS = RESHAPE(DUMMY, (/LENSFC/)) - + ENDIF + + IF (PRESENT(SICFCS)) THEN ERROR=NF90_INQ_VARID(NCID, "fice", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING fice ID' ) ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy) CALL NETCDF_ERR(ERROR, 'READING fice' ) SICFCS = RESHAPE(DUMMY, (/LENSFC/)) - + ENDIF + + IF (PRESENT(SITFCS)) THEN ERROR=NF90_INQ_VARID(NCID, "tisfc", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING tisfc ID' ) ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy) CALL NETCDF_ERR(ERROR, 'READING tisfc' ) SITFCS = RESHAPE(DUMMY, (/LENSFC/)) - + ENDIF + + IF (PRESENT(TPRCP)) THEN ERROR=NF90_INQ_VARID(NCID, "tprcp", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING tprcp ID' ) ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy) CALL NETCDF_ERR(ERROR, 'READING tprcp' ) TPRCP = RESHAPE(DUMMY, (/LENSFC/)) - + ENDIF + + IF (PRESENT(SRFLAG)) THEN ERROR=NF90_INQ_VARID(NCID, "srflag", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING srflag ID' ) ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy) CALL NETCDF_ERR(ERROR, 'READING srflag' ) SRFLAG = RESHAPE(DUMMY, (/LENSFC/)) - + ENDIF + + IF (PRESENT(SNDFCS)) THEN ERROR=NF90_INQ_VARID(NCID, "snwdph", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING snwdph ID' ) ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy) CALL NETCDF_ERR(ERROR, 'READING snwdph' ) - SWDFCS = RESHAPE(DUMMY, (/LENSFC/)) - + SNDFCS = RESHAPE(DUMMY, (/LENSFC/)) + ENDIF + + IF (PRESENT(VMNFCS)) THEN ERROR=NF90_INQ_VARID(NCID, "shdmin", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING shdmin ID' ) ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy) CALL NETCDF_ERR(ERROR, 'READING shdmin' ) VMNFCS = RESHAPE(DUMMY, (/LENSFC/)) - + ENDIF + + IF (PRESENT(VMXFCS)) THEN ERROR=NF90_INQ_VARID(NCID, "shdmax", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING shdmax ID' ) ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy) CALL NETCDF_ERR(ERROR, 'READING shdmax' ) VMXFCS = RESHAPE(DUMMY, (/LENSFC/)) - + ENDIF + + IF (PRESENT(SLPFCS)) THEN ERROR=NF90_INQ_VARID(NCID, "slope", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING slope ID' ) ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy) CALL NETCDF_ERR(ERROR, 'READING slope' ) SLPFCS = RESHAPE(DUMMY, (/LENSFC/)) - + ENDIF + + IF (PRESENT(ABSFCS)) THEN ERROR=NF90_INQ_VARID(NCID, "snoalb", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING snoalb ID' ) ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy) CALL NETCDF_ERR(ERROR, 'READING snoalb' ) ABSFCS = RESHAPE(DUMMY, (/LENSFC/)) - + ENDIF + + IF (PRESENT(T2M)) THEN ERROR=NF90_INQ_VARID(NCID, "t2m", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING t2m ID' ) ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy) CALL NETCDF_ERR(ERROR, 'READING t2m' ) T2M = RESHAPE(DUMMY, (/LENSFC/)) - + ENDIF + + IF (PRESENT(Q2M)) THEN ERROR=NF90_INQ_VARID(NCID, "q2m", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING q2m ID' ) ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy) CALL NETCDF_ERR(ERROR, 'READING q2m' ) Q2M = RESHAPE(DUMMY, (/LENSFC/)) + ENDIF NSST_READ : IF(DO_NSST) THEN @@ -1625,39 +1687,47 @@ SUBROUTINE READ_DATA(TSFFCS,SMCFCS,SNOFCS,STCFCS, & ALLOCATE(DUMMY3D(IDIM,JDIM,LSOIL)) + IF (PRESENT(SMCFCS)) THEN ERROR=NF90_INQ_VARID(NCID, "smc", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING smc ID' ) ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy3d) CALL NETCDF_ERR(ERROR, 'READING smc' ) SMCFCS = RESHAPE(DUMMY3D, (/LENSFC,LSOIL/)) + ENDIF + IF (PRESENT(SLCFCS)) THEN ERROR=NF90_INQ_VARID(NCID, "slc", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING slc ID' ) ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy3d) CALL NETCDF_ERR(ERROR, 'READING slc' ) SLCFCS = RESHAPE(DUMMY3D, (/LENSFC,LSOIL/)) + ENDIF + IF (PRESENT(STCFCS)) THEN ERROR=NF90_INQ_VARID(NCID, "stc", ID_VAR) CALL NETCDF_ERR(ERROR, 'READING stc ID' ) ERROR=NF90_GET_VAR(NCID, ID_VAR, dummy3d) CALL NETCDF_ERR(ERROR, 'READING stc' ) STCFCS = RESHAPE(DUMMY3D, (/LENSFC,LSOIL/)) + ENDIF DEALLOCATE(DUMMY3D) ! cloud fields not in warm restart files. set to zero? - CVFCS = 0.0 - CVTFCS = 0.0 - CVBFCS = 0.0 + IF (PRESENT(CVFCS)) CVFCS = 0.0 + IF (PRESENT(CVTFCS)) CVTFCS = 0.0 + IF (PRESENT(CVBFCS)) CVBFCS = 0.0 ! soil layer thicknesses not in warm restart files. hardwire ! for now. - + + IF (PRESENT(ZSOIL)) THEN ZSOIL(1) = -0.1 ZSOIL(2) = -0.4 ZSOIL(3) = -1.0 ZSOIL(4) = -2.0 + ENDIF ERROR = NF90_CLOSE(NCID) diff --git a/sorc/lsm_routines.fd/noah.fd/CMakeLists.txt b/sorc/lsm_routines.fd/noah.fd/CMakeLists.txt index 7f9df480d..b971b9712 100644 --- a/sorc/lsm_routines.fd/noah.fd/CMakeLists.txt +++ b/sorc/lsm_routines.fd/noah.fd/CMakeLists.txt @@ -5,7 +5,8 @@ set(fortran_src set_soilveg_snippet.f90 - sflx_snippet.f90) + sflx_snippet.f90 + bulk_snow_module.f90) if(CMAKE_Fortran_COMPILER_ID MATCHES "^(Intel)$") set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -r8 -convert big_endian") diff --git a/sorc/lsm_routines.fd/noah.fd/bulk_snow_module.f90 b/sorc/lsm_routines.fd/noah.fd/bulk_snow_module.f90 new file mode 100644 index 000000000..d8b62bdd2 --- /dev/null +++ b/sorc/lsm_routines.fd/noah.fd/bulk_snow_module.f90 @@ -0,0 +1,65 @@ +!> @file +!> @brief Routines to make DA updates to a bulk (single layer) snow model +!! such as that in Noah. +!> @author Clara Draper + +module bulk_snow_module + + implicit none + + private + + public calc_density + +contains + +!> This subroutine calculates snow density from forecast fields. +!! density = SWE/SND where snow present. +!! = average from snow forecasts over land, where snow not present +!! @param[in] lensfc Number of sfc grid cells +!! @param[in] rank Processor rank +!! @param[in] landmask Mask for land increments +!! @param[in] swe Snow Water Equivalent +!! @param[in] snd Snow Depth +!! @param[out] density Snow density [-] + + subroutine calc_density(lensfc, landmask, swe, snd, rank, density) + + implicit none + + integer, intent(in) :: lensfc, rank + integer, intent(in) :: landmask(lensfc) + real, intent(in) :: swe(lensfc), snd(lensfc) + real, intent(out) :: density(lensfc) + + real :: dens_mean + integer :: n + + ! density = swe/snd + do n =1,lensfc + if (snd(n) > 0.001 ) then + density(n) = swe(n)/snd(n) + else + density(n)=0.1 + endif + enddo + + where (density < 0.0001) density = 0.1 + + ! calculate mean density of snow over land + if (count (landmask==2) > 0) then + ! mean density over snow-covered land + dens_mean = sum(density, mask = (landmask==2 )) & + / count (landmask==2) + print *, 'mean density on rank ', rank,': ', dens_mean + else + dens_mean = 0.1 ! default value if have no snow in tile + print *, 'no snow on rank ', rank, ' using default density ', dens_mean + endif + + ! for grid cells with no valid density, fill in the average snodens + where( swe <= 0.001 ) density = dens_mean + + end subroutine calc_density + +end module bulk_snow_module diff --git a/tests/global_cycle/ftst_land_increments.F90 b/tests/global_cycle/ftst_land_increments.F90 index 66c96782c..dad0e7481 100644 --- a/tests/global_cycle/ftst_land_increments.F90 +++ b/tests/global_cycle/ftst_land_increments.F90 @@ -17,8 +17,7 @@ program ftst_land_increments real, parameter :: EPSILON=0.001 real, allocatable :: rsoiltype(:) - real, allocatable :: smc_bck(:,:) - real, allocatable :: slc_bck(:,:) + integer, allocatable :: mask(:) real, allocatable :: stc_bck(:,:) real, allocatable :: smc_anl(:,:) real, allocatable :: slc_anl(:,:) @@ -37,8 +36,7 @@ program ftst_land_increments lensfc= 3 ! Number of test points. allocate(rsoiltype(lensfc)) ! Soil type. - allocate(smc_bck(lensfc,lsoil)) ! Background total soil moisture. - allocate(slc_bck(lensfc,lsoil)) ! Background liquid soil moisture. + allocate(mask(lensfc)) ! Land mask allocate(stc_bck(lensfc,lsoil)) ! Background soil temperature (K). allocate(smc_anl(lensfc,lsoil)) ! Analyzed total soil moisture. allocate(slc_anl(lensfc,lsoil)) ! Analyzed liquid soil moisture. @@ -50,8 +48,7 @@ program ftst_land_increments ! be unchanged and equal. rsoiltype(1) = 5. - smc_bck(1,:) = .25 - slc_bck(1,:) = .25 + mask(1) = 1 stc_bck(1,:) = 280.0 smc_anl(1,:) = .25 @@ -64,8 +61,7 @@ program ftst_land_increments ! be adjusted to equal the total soil moisture. rsoiltype(2) = 5. - smc_bck(2,:) = .25 - slc_bck(2,:) = .23 + mask(2) = 1 stc_bck(2,:) = 270.0 smc_anl(2,:) = .25 @@ -77,17 +73,15 @@ program ftst_land_increments ! moisture. rsoiltype(3) = 5. - smc_bck(3,:) = .25 - slc_bck(3,:) = .25 + mask(3) = 1 stc_bck(3,:) = 274.0 smc_anl(3,:) = .25 slc_anl(3,:) = .25 stc_anl(3,:) = 271.0 - call apply_land_da_adjustments(update_type, lsm, isot, ivegsrc,lensfc, & - lsoil, rsoiltype, smc_bck, slc_bck,stc_bck, smc_anl, slc_anl, stc_anl) - + call apply_land_da_adjustments_stc(lsm, isot, ivegsrc,lensfc, & + lsoil, rsoiltype, mask, stc_bck, stc_anl, smc_anl, slc_anl) do l = 1, lsoil if (abs(smc_anl(1,l) - 0.25) > EPSILON) stop 2 @@ -106,7 +100,7 @@ program ftst_land_increments call mpi_finalize(ierr) - deallocate(rsoiltype,smc_bck,slc_bck,stc_bck,smc_anl,slc_anl,stc_anl) + deallocate(rsoiltype,stc_bck,smc_anl,slc_anl,stc_anl,mask) if (my_rank .eq. 0) print*, "OK" if (my_rank .eq. 0) print*, "SUCCESS!" diff --git a/ush/global_cycle.sh b/ush/global_cycle.sh index 3ab730f5d..06a21c93b 100755 --- a/ush/global_cycle.sh +++ b/ush/global_cycle.sh @@ -145,6 +145,7 @@ # DONST Process NST records when using NST model. Default is 'no'. # DO_SFCCYCLE Call sfcsub routine # DO_LNDINC Call routine to update soil states with increment files +# DO_SNO_INC Call routine to update snow states with increment files # zsea1/zsea2 When running with NST model, this is the lower/upper bound # of depth of sea temperature. In whole mm. # MAX_TASKS_CY Normally, program should be run with a number of mpi tasks @@ -267,6 +268,7 @@ use_ufo=${use_ufo:-.true.} DONST=${DONST:-"NO"} DO_SFCCYCLE=${DO_SFCCYCLE:-.true.} DO_LNDINC=${DO_LNDINC:-.false.} +DO_SNO_INC=${DO_SNO_INC:-.false.} zsea1=${zsea1:-0} zsea2=${zsea2:-0} MAX_TASKS_CY=${MAX_TASKS_CY:-99999} @@ -389,7 +391,8 @@ EOF cat << EOF > fort.37 &NAMSFCD NST_FILE="$NST_FILE", - LND_SOI_FILE="$LND_SOI_FILE" + LND_SOI_FILE="$LND_SOI_FILE", + DO_SNO_INC=$DO_SNO_INC / EOF diff --git a/ush/global_cycle_driver.sh b/ush/global_cycle_driver.sh index e75985a3c..3ae35094d 100755 --- a/ush/global_cycle_driver.sh +++ b/ush/global_cycle_driver.sh @@ -53,6 +53,7 @@ fi export DO_SFCCYLE=${DO_SFCCYCLE:-".true."} export DO_LNDINC=${DO_LNDINC:-".false."} export LND_SOI_FILE=${LND_SOI_FILE:-"NULL"} +export DO_SNO_INC=${DO_SNO_INC:-".false."} CRES=$(echo $CASE | cut -c 2-) JCAP_CASE=$((2*CRES-2)) @@ -75,6 +76,9 @@ for n in $(seq 1 $ntiles); do ln -fs $COMOUT/$PDY.${cyc}0000.sfcanl_data.tile${n}.nc $DATA/fnbgso.00$n ln -fs $FIXfv3/C${CRES}/C${CRES}_grid.tile${n}.nc $DATA/fngrid.00$n ln -fs $FIXfv3/C${CRES}/C${CRES}_oro_data.tile${n}.nc $DATA/fnorog.00$n + if [[ "$DO_SNO_INC" == ".true." ]] ; then + ln -fs $COMIN/$PDY.${cyc}0000.xainc.tile${n}.nc $DATA/xainc.00$n + fi done $CYCLESH