Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

User defined top snow layer #792

Merged
merged 23 commits into from
Sep 9, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
63b2c81
Changes from @olyson to fix #780
ekluzek Aug 16, 2019
20cbf74
Merge tag 'ctsm1.0.dev059' into snow_layers_user_defined
slevis-lmwg Aug 25, 2019
3e5856a
Allow users to specify snow layer structure in namelist
slevis-lmwg Aug 26, 2019
986cf49
Merge commit 'f2869fe202f71c4fd8c854fce47089630b4396c5' into clm50cos…
ekluzek Aug 28, 2019
7f67efc
Remove the commit to update the params file, which shouldn't be done …
ekluzek Aug 28, 2019
ee939a7
Merge tag 'ctsm1.0.dev061' into clm50coslatfirefix
ekluzek Sep 2, 2019
b2cf3a6
Changes for #787 for dwt_slash_flux
ekluzek Sep 2, 2019
f293334
Make sure urbantv is set for clm4_5 #175
ekluzek Sep 2, 2019
f49dac1
Revisions in response to @billsacks code review
slevis-lmwg Sep 2, 2019
47cd874
Merge tag 'ctsm1.0.dev061' into user_defined_top_snow_layer
slevis-lmwg Sep 2, 2019
ae415da
Merge tag 'ctsm1.0.dev062' into clm50coslatfirefix
ekluzek Sep 4, 2019
d31b2ee
Fix urbantv to point to different file for clm4_5 than clm5_0 as it s…
ekluzek Sep 4, 2019
ffe027c
Remove DWT_SLASH_* from output usermod at scales finer than gridcell,…
ekluzek Sep 6, 2019
ec4d91a
Update changelog
ekluzek Sep 6, 2019
5bcf4d6
Merge tag 'ctsm1.0.dev063' into user_defined_top_snow_layer
slevis-lmwg Sep 7, 2019
e00da7c
Revisions in response to code review
slevis-lmwg Sep 7, 2019
d7292f3
First draft of ChangeLog entry
slevis-lmwg Sep 7, 2019
e478244
Updated temporary test code checking bit-for-bit
slevis-lmwg Sep 7, 2019
db4bb80
Updated temporary test code checking bit-for-bit (part 2)
slevis-lmwg Sep 7, 2019
97cef78
Removing temporary test code
slevis-lmwg Sep 7, 2019
bd7f647
Updates needed for snowhydrology unit tests to pass
slevis-lmwg Sep 9, 2019
4c19afc
Updated ChangeLog/Sum
slevis-lmwg Sep 9, 2019
1d5ea12
Minor edit in ChangeLog
billsacks Sep 9, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 48 additions & 20 deletions src/biogeophys/SnowHydrologyMod.F90
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ module SnowHydrologyMod
use clm_varpar , only : nlevsno, nlevsoi, nlevgrnd
use clm_varctl , only : iulog, use_subgrid_fluxes
use clm_varcon , only : namec, h2osno_max, hfus, denice, rpi, spval, tfrz
use clm_varcon, only: dzmin, dzmax_l, dzmax_u
use atm2lndType , only : atm2lnd_type
use AerosolMod , only : aerosol_type
use TemperatureType , only : temperature_type
Expand Down Expand Up @@ -100,20 +101,6 @@ module SnowHydrologyMod
integer, parameter, public :: LoTmpDnsSlater2017 = 2 ! For temperature below -15C use equation from Slater 2017
integer, parameter, public :: LoTmpDnsTruncatedAnderson1976 = 1 ! Truncate low temp. snow density from the Anderson-1976 version at -15C

! Definition of snow pack vertical structure
! Hardcoded maximum of 12 snowlayers, this is checked elsewhere (controlMod.F90)
! The bottom layer has no limit on thickness, hence the last element of the dzmax_*
! arrays is 'huge'.
real(r8), parameter :: dzmin(12) = & ! minimum of top snow layer
(/ 0.010_r8, 0.015_r8, 0.025_r8, 0.055_r8, 0.115_r8, 0.235_r8, &
0.475_r8, 0.955_r8, 1.915_r8, 3.835_r8, 7.675_r8, 15.355_r8 /)
real(r8), parameter :: dzmax_l(12) = & ! maximum thickness of layer when no layers beneath
(/ 0.03_r8, 0.07_r8, 0.18_r8, 0.41_r8, 0.88_r8, 1.83_r8, &
3.74_r8, 7.57_r8, 15.24_r8, 30.59_r8, 61.3_r8, huge(1._r8) /)
real(r8), parameter :: dzmax_u(12) = & ! maximum thickness of layer when layers beneath
(/ 0.02_r8, 0.05_r8, 0.11_r8, 0.23_r8, 0.47_r8, 0.95_r8, &
1.91_r8, 3.83_r8, 7.67_r8, 15.35_r8, 30.71_r8, huge(1._r8) /)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In reference to this comment, the original code assigned huge(1._r8) to dzmax_l(12) and dzmax_u(12). So to match the original behavior, I think I need to do the following:

if (nlevsno >= 12) then
dzmax_l = huge(1._r8)
dzmax_u = huge(1._r8)
end if

Is this what you were suggesting @billsacks ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More precisely...
if (j >= 12) then
dzmax_l(j) = ...
dzmax_u(j) = ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing mind:
I prefer the first if statement with these corrections:
dzmax_l(nlevsno) = ...
dzmax_u(nlevsno) = ...

This replicates the current behavior for nlevsno = 12. If some day we allow nlevsno > 12, this just sets the bottom layer to huge.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the last comment is what I was thinking (where the ... should be huge(1._r8)).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i.e., the bottom layer should be set to huge, regardless of the number of snow layers. I now realize that that isn't exactly what the original code did, but I think that was the intent.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it would be good to verify that that does the right thing. Either (a) make that change and verify that it doesn't change answers for, say, a 5-layer case (a CLM45 case), or (b) don't make that change, and ensure that having dzmax_l and dzmax_u NOT set to huge for nlevsno gives the same answers as setting it to huge for a 12-layer case. I prefer (a), but don't feel strongly about it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also prefer (a).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it would be good to verify that that does the right thing. Either (a) make that change and verify that it doesn't change answers for, say, a 5-layer case (a CLM45 case), or [...]

The hobart test-suite includes a few clm45 cases and they all PASS.


!
! !PRIVATE DATA MEMBERS:

Expand All @@ -123,6 +110,7 @@ module SnowHydrologyMod
! If true, the density of new snow depends on wind speed, and there is also
! wind-dependent snow compaction
logical :: wind_dependent_snow_density ! If snow density depends on wind or not
real(r8) :: dzmin_1, dzmax_l_1, dzmax_u_1 ! namelist-defined top snow layer information
integer :: overburden_compaction_method = -1
integer :: new_snow_density = LoTmpDnsSlater2017 ! Snow density type
real(r8) :: upplim_destruct_metamorph = 100.0_r8 ! Upper Limit on Destructive Metamorphism Compaction [kg/m3]
Expand Down Expand Up @@ -187,11 +175,15 @@ subroutine SnowHydrology_readnl( NLFilename)
wind_dependent_snow_density, snow_overburden_compaction_method, &
lotmp_snowdensity_method, upplim_destruct_metamorph, &
overburden_compress_Tfactor, min_wind_snowcompact, &
reset_snow, reset_snow_glc, reset_snow_glc_ela
reset_snow, reset_snow_glc, reset_snow_glc_ela, dzmin_1, dzmax_l_1, &
dzmax_u_1

! Initialize options to default values, in case they are not specified in the namelist
wind_dependent_snow_density = .false.
snow_overburden_compaction_method = ' '
dzmin_1 = 0.01_r8
dzmax_l_1 = 0.03_r8
dzmax_u_1 = 0.02_r8

if (masterproc) then
unitn = getavu()
Expand All @@ -218,6 +210,9 @@ subroutine SnowHydrology_readnl( NLFilename)
call shr_mpi_bcast (reset_snow , mpicom)
call shr_mpi_bcast (reset_snow_glc , mpicom)
call shr_mpi_bcast (reset_snow_glc_ela , mpicom)
call shr_mpi_bcast (dzmin_1, mpicom)
call shr_mpi_bcast (dzmax_l_1, mpicom)
call shr_mpi_bcast (dzmax_u_1, mpicom)

if (masterproc) then
write(iulog,*) ' '
Expand All @@ -243,6 +238,19 @@ subroutine SnowHydrology_readnl( NLFilename)
errMsg(sourcefile, __LINE__))
end if

if (dzmin_1 < 0.01_r8) then
call endrun(msg="ERROR dzmin_1 cannot be less than 0.01"// &
errMsg(sourcefile, __LINE__))
end if
if (dzmax_l_1 < 0.03_r8) then
call endrun(msg="ERROR dzmax_l_1 cannot be less than 0.03"// &
errMsg(sourcefile, __LINE__))
end if
if (dzmax_u_1 < 0.02_r8) then
call endrun(msg="ERROR dzmax_2_1 cannot be less than 0.02"// &
errMsg(sourcefile, __LINE__))
end if

end subroutine SnowHydrology_readnl

!-----------------------------------------------------------------------
Expand Down Expand Up @@ -800,12 +808,12 @@ subroutine BuildFilter_SnowpackInitialized(bounds, num_c, filter_c, &
! maintain answers as before.
snowpack_initialized(c) = ( &
snl(c) == 0 .and. &
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@billsacks I think the next four 0.01 values are what you wanted me to look for, right?

More generally speaking, I think this code is ready for review. Also I would like to know your preference in this case between new Unit Test and new standard test-suite test. I will be working on that next.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, these are the four places where I know this value is hard-coded. If you haven't already, thenI also thought it would be worth grepping the code for 0.01 to see if there are any other references that I'm not aware of.

As for testing: With some upcoming suggestions that I'm going to make (in which you don't have hard-coded defaults for these values, but rather have them always explicitly set in the namelist), your new code will be sufficiently covered by existing system tests, so no need to add additional tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked for all the instances of 0.01 at the time, and there were some more that looked unrelated, so I left them alone.

frac_sno_eff(c)*snow_depth(c) >= (0.01_r8 + lsadz) .and. &
frac_sno_eff(c)*snow_depth(c) >= (dzmin(1) + lsadz) .and. &
qflx_snow_grnd(c) > 0.0_r8)
else
snowpack_initialized(c) = ( &
snl(c) == 0 .and. &
frac_sno_eff(c)*snow_depth(c) >= 0.01_r8)
frac_sno_eff(c)*snow_depth(c) >= dzmin(1))
end if

end do
Expand Down Expand Up @@ -1496,7 +1504,7 @@ subroutine CombineSnowLayers(bounds, num_snowc, filter_snowc, &
real(r8):: h2osno_total(bounds%begc:bounds%endc) ! total snow water (mm H2O)
real(r8):: zwice(bounds%begc:bounds%endc) ! total ice mass in snow
real(r8):: zwliq (bounds%begc:bounds%endc) ! total liquid water in snow
real(r8):: dzminloc(size(dzmin)) ! minimum of top snow layer (local)
real(r8):: dzminloc(nlevsno) ! minimum of top snow layer (local)
real(r8):: dtime !land model time step (sec)

!-----------------------------------------------------------------------
Expand Down Expand Up @@ -1680,8 +1688,8 @@ subroutine CombineSnowLayers(bounds, num_snowc, filter_snowc, &
c = filter_snowc(fc)
l = col%landunit(c)
if (snow_depth(c) > 0._r8) then
if ((ltype(l) == istdlak .and. snow_depth(c) < 0.01_r8 + lsadz ) .or. &
((ltype(l) /= istdlak) .and. ((frac_sno_eff(c)*snow_depth(c) < 0.01_r8) &
if ((ltype(l) == istdlak .and. snow_depth(c) < dzmin(1) + lsadz ) .or. &
((ltype(l) /= istdlak) .and. ((frac_sno_eff(c)*snow_depth(c) < dzmin(1)) &
.or. (h2osno_total(c)/(frac_sno_eff(c)*snow_depth(c)) < 50._r8)))) then

snl(c) = 0
Expand Down Expand Up @@ -2234,6 +2242,26 @@ subroutine InitSnowLayers (bounds, snow_depth)
zi => col%zi & ! Output: [real(r8) (:,:) ] interface level below a "z" level (m) (-nlevsno+0:nlevgrnd)
)

! These three variables determine the vertical structure of the snow pack:
! dzmin: minimum of top snow layer
! dzmax_l: maximum thickness of layer when no layers beneath
! dzmax_u: maximum thickness of layer when layers beneath
dzmin(1) = dzmin_1 ! set in the namelist with default or user-defined value
dzmax_l(1) = dzmax_l_1 ! same comment
dzmax_u(1) = dzmax_u_1 ! same comment
do j = 2, nlevsno
dzmin(j) = dzmax_u(j-1) * 0.5_r8
if (dzmin(j) <= dzmin(j-1)) then
dzmin(j) = (dzmin(j-1) + dzmax_u(j-1)) * 0.5_r8
end if
dzmax_u(j) = 2._r8 * dzmax_u(j-1) + 0.01_r8
if (j == 2) then
dzmax_l(j) = dzmax_u(j) + dzmax_l(j-1) - 0.01_r8
else
dzmax_l(j) = dzmax_u(j) + dzmax_l(j-1)
end if
end do

loop_columns: do c = bounds%begc,bounds%endc
l = col%landunit(c)

Expand Down
8 changes: 7 additions & 1 deletion src/main/clm_varcon.F90
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,9 @@ module clm_varcon
! The values for the following arrays are set in routine iniTimeConst
!------------------------------------------------------------------

real(r8), public, allocatable :: dzmin(:) !min of top snow layer
real(r8), public, allocatable :: dzmax_l(:) !max snow thickness of layer when no layers beneath
real(r8), public, allocatable :: dzmax_u(:) !max snow thickness of layer when layers beneath
real(r8), public, allocatable :: zlak(:) !lake z (layers)
real(r8), public, allocatable :: dzlak(:) !lake dz (thickness)
real(r8), public, allocatable :: zsoi(:) !soil z (layers)
Expand Down Expand Up @@ -266,7 +269,7 @@ subroutine clm_varcon_init( is_simple_buildtemp )
! MUST be called after clm_varpar_init.
!
! !USES:
use clm_varpar, only: nlevgrnd, nlevlak, nlevdecomp_full, nlevsoifl, nlayer
use clm_varpar, only: nlevsno, nlevgrnd, nlevlak, nlevdecomp_full, nlevsoifl, nlayer
!
! !ARGUMENTS:
implicit none
Expand All @@ -276,6 +279,9 @@ subroutine clm_varcon_init( is_simple_buildtemp )
! Created by E. Kluzek
!------------------------------------------------------------------------------

allocate( dzmin(1:nlevsno ))
allocate( dzmax_l(1:nlevsno ))
allocate( dzmax_u(1:nlevsno ))
allocate( zlak(1:nlevlak ))
allocate( dzlak(1:nlevlak ))
allocate( zsoi(1:nlevgrnd ))
Expand Down