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

Need some changes to avoid negative h2osoi_ice in UpdateState_TopLayerFluxes #1979

Closed
billsacks opened this issue Apr 2, 2023 · 5 comments · Fixed by #1987
Closed

Need some changes to avoid negative h2osoi_ice in UpdateState_TopLayerFluxes #1979

billsacks opened this issue Apr 2, 2023 · 5 comments · Fixed by #1987
Assignees
Labels
bug something is working incorrectly

Comments

@billsacks
Copy link
Member

Brief summary of bug

In UpdateState_TopLayerFluxes, there is a somewhat arbitrary epsilon used to determine if h2osoi_ice and h2osoi_liq are close enough to zero that they should be truncated to zero. If the state remains negative after this truncation, we deem this to be a problem. It seems that this tolerance is occasionally exceeded, leading runs to abort. Since this tolerance is somewhat arbitrary, we will loosen it by an order of magnitude.

General bug information

CTSM version you are using: Recent versions

Does this bug cause significantly incorrect results in the model's science? No

Configurations affected: Unknown

Details of bug

For details, see comments in #1253 starting with #1253 (comment)

See also #988

@billsacks billsacks self-assigned this Apr 2, 2023
@billsacks billsacks added the bug something is working incorrectly label Apr 2, 2023
billsacks added a commit to billsacks/ctsm that referenced this issue Apr 2, 2023
In UpdateState_TopLayerFluxes, there is a somewhat arbitrary epsilon
used to determine if h2osoi_ice and h2osoi_liq are close enough to zero
that they should be truncated to zero. If the state remains negative
after this truncation, we deem this to be a problem. It seems that this
tolerance is occasionally exceeded, leading runs to abort. Since this
tolerance is somewhat arbitrary, we will loosen it by an order of
magnitude.

Resolves ESCOMP#1979

Note that, although the issue was only reported for h2osoi_ice, I am
changing the tolerance for both liq and ice so they remain consistent.
@billsacks
Copy link
Member Author

From discussion with @olyson and @swensosc - We're going to loosen the tolerance further to 1e-10 in the hopes that we'll avoid this problem moving forward. If this problem crops up again, see code in

! Partition the evaporation from snow/soil surface into liquid evaporation,
! solid evaporation (sublimation), liquid dew, or solid dew. Note that the variables
! affected here are all related to the snow subgrid patch only because of the use of qflx_ev_snow.
! In the situations where there are snow layers or there is snow without an explicit snow layer,
! the partitioned variables will represent the components of snow evaporation
! (qflx_ev_snow = qflx_liqevap_from_top_layer + qflx_solidevap_from_top_layer
! - qflx_liqdew_to_top_layer - qflx_soliddew_to_top_layer).
! In the case of no snow, qflx_ev_snow has already been set equal to qflx_ev_soil (the evaporation
! from the subgrid soil patch) and the partitioned variables will then represent evaporation from the
! subgrid soil patch.
! In the case of urban columns (and lake columns - see LakeHydrologyMod), there are no subgrid
! patches and qflx_evap_soi is used. qflx_evap_soi = qflx_liqevap_from_top_layer + qflx_solidevap_from_top_layer
! - qflx_liqdew_to_top_layer - qflx_soliddew_to_top_layer.
if (.not. lun%urbpoi(l)) then
if (qflx_ev_snow(p) >= 0._r8) then
! for evaporation partitioning between liquid evap and ice sublimation,
! use the ratio of liquid to (liquid+ice) in the top layer to determine split
if ((h2osoi_liq(c,j)+h2osoi_ice(c,j)) > 0._r8) then
qflx_liqevap_from_top_layer(p) = max(qflx_ev_snow(p)*(h2osoi_liq(c,j)/ &
(h2osoi_liq(c,j)+h2osoi_ice(c,j))), 0._r8)
else
qflx_liqevap_from_top_layer(p) = 0._r8
end if
qflx_solidevap_from_top_layer(p) = qflx_ev_snow(p) - qflx_liqevap_from_top_layer(p)
else
if (t_grnd(c) < tfrz) then
qflx_soliddew_to_top_layer(p) = abs(qflx_ev_snow(p))
else
qflx_liqdew_to_top_layer(p) = abs(qflx_ev_snow(p))
end if
end if
else ! Urban columns
if (qflx_evap_soi(p) >= 0._r8) then
! for evaporation partitioning between liquid evap and ice sublimation,
! use the ratio of liquid to (liquid+ice) in the top layer to determine split
if ((h2osoi_liq(c,j)+h2osoi_ice(c,j)) > 0._r8) then
qflx_liqevap_from_top_layer(p) = max(qflx_evap_soi(p)*(h2osoi_liq(c,j)/ &
(h2osoi_liq(c,j)+h2osoi_ice(c,j))), 0._r8)
else
qflx_liqevap_from_top_layer(p) = 0._r8
end if
qflx_solidevap_from_top_layer(p) = qflx_evap_soi(p) - qflx_liqevap_from_top_layer(p)
else
if (t_grnd(c) < tfrz) then
qflx_soliddew_to_top_layer(p) = abs(qflx_evap_soi(p))
else
qflx_liqdew_to_top_layer(p) = abs(qflx_evap_soi(p))
end if
end if
end if
and see #1282 for some guidance on what might be needed. We do see some potential for issues if there are big differences between h2osoi_liq and h2osoi_ice (in which case the liquid or ice flux might have relatively large errors relative to the liquid or ice state – since relative errors depend on the largest term in the equation), so we may need to do some corrections to ensure that fluxes aren't leading us to exceed either the liq or ice state values, probably when these fluxes are computed in SoilFluxesMod.

@billsacks
Copy link
Member Author

@olyson and @swensosc - I woke up this morning wondering if I could find a relatively easy solution that would get more to the heart of the problem, and spent a bit of time looking into this - though didn't want to spend too long on it (analyzing, developing & testing), so where I currently stand is about where we were yesterday – that, for now, we should just loosen the tolerance. However, I'm inclined to loosen it even more than we suggested - to 1e-9 rather than 1e-10. Are you comfortable with that? I figure that a reduction in state by a factor of 1 billion is effectively reducing the state to 0, so we might as well set it to exactly 0.

That said, I'll document here the thoughts I had this morning in case we want to come back to this - or in case either of you think this is worth pursuing further here.

I started with the assumption that the problem is arising due to big differences in the original h2osoi_liq vs h2osoi_ice. (Before pursuing this further, it would probably be worth testing that assumption by printing the original h2osoi_liq as well as h2osoi_ice in the problem point.) If so, a solution could be to check if the intent is to bring the total state to 0, and if so, adjust each individual flux to accomplish that. Specifically, it looks like a solution could be: when we see that qflx_ev_snow (or qflx_evap_soi for urban) exceed evaporation_limit, set a logical flag on this patch; if this is set, then have a different code path that sets both liqevap and solidevap to take away the entire liquid or solid state (respectively).

I think that this solution would be relatively easy to implement, but then I noticed another possible reason why we might need a looser tolerance for urban: because the application of evaporation_limit that I think matters here is qflx_evap_soi(p) = qflx_evap_soi(p) - frac_sno_eff(c)*(evaporation_demand - evaporation_limit) rather than just qflx_ev_snow(p) = evaporation_limit.

So that realization led me to feel like maybe we just want to go ahead and loosen the tolerance like we discussed yesterday, since I can see multiple possible reasons for the old tolerance to be exceeded and I'm still not sure it's worth the time to dig into this further. But I wanted to run this by you to make sure you're still happy with this plan.

@olyson
Copy link
Contributor

olyson commented Apr 5, 2023

I printed out h2osoi_liq for the problematic point as part of the error message:

563: c, lev_top(c) = 96 0
563: h2osoi_ice_top_orig = 1.999323742483267E-006
563: h2osoi_ice = -2.100641709190665E-018
563: h2osoi_liq_top_orig = 1.807655353197003E-002
563: h2osoi_liq = 0.000000000000000E+000
563: frac_sno_eff = 1.00000000000000
563: qflx_soliddew_to_top_layerdtime = 0.000000000000000E+000
563: qflx_solidevap_from_top_layer
dtime = 1.999323742485367E-006

So, h2osoi_liq_top_orig is about four orders of magnitude larger than h2osoi_ice_top_orig.

@swensosc
Copy link
Contributor

swensosc commented Apr 6, 2023 via email

@billsacks
Copy link
Member Author

As I discussed with @olyson yesterday: based on the numbers he provided, I am going to go with the solution I mentioned in my earlier comment:

I started with the assumption that the problem is arising due to big differences in the original h2osoi_liq vs h2osoi_ice. (Before pursuing this further, it would probably be worth testing that assumption by printing the original h2osoi_liq as well as h2osoi_ice in the problem point.) If so, a solution could be to check if the intent is to bring the total state to 0, and if so, adjust each individual flux to accomplish that. Specifically, it looks like a solution could be: when we see that qflx_ev_snow (or qflx_evap_soi for urban) exceed evaporation_limit, set a logical flag on this patch; if this is set, then have a different code path that sets both liqevap and solidevap to take away the entire liquid or solid state (respectively).

because I think this will be a more robust fix that will help prevent this issue from coming up again in the future.

@billsacks billsacks changed the title Need to loosen tolerance on near-zero truncation of h2osoi_ice in UpdateState_TopLayerFluxes Need some changes to avoid negative h2osoi_ice in UpdateState_TopLayerFluxes Apr 7, 2023
billsacks added a commit to billsacks/ctsm that referenced this issue Apr 8, 2023
Occasionally, h2osoi_ice was going significantly negative in
UpdateState_TopLayerFluxes - see
ESCOMP#1979. As noted in that issue, this
seems to be due to h2osoi_ice having a very different magnitude from
h2osoi_liq, leading to greater-than-roundoff-level differences from zero
final state in a relative sense (i.e., relative to the magnitude of
h2osoi_ice) - I think because of the appearance of the sum (h2osoi_ice +
h2osoi_liq) in the equations that limit fluxes.

To try to deal with this, I have reworked the handling of the
evaporation constraint to directly limit both the liqevap and solidevap,
so that both of them should result in the equivalent liq or ice states
going to 0 within roundoff.

To do that, I needed to move the partitioning of the total flux into
liquid and solid to earlier in the subroutine and then recalculate those
partitioning fluxes in conditions where we're applying an evaporation
constraint.

Note that the deleted lines in this commit have just been moved as-is to
earlier in this subroutine.
billsacks added a commit to billsacks/ctsm that referenced this issue Apr 10, 2023
Occasionally, h2osoi_ice was going significantly negative in
UpdateState_TopLayerFluxes - see
ESCOMP#1979. As noted in that issue, this
seems to be due to h2osoi_ice having a very different magnitude from
h2osoi_liq, leading to greater-than-roundoff-level differences from zero
final state in a relative sense (i.e., relative to the magnitude of
h2osoi_ice) - I think because of the appearance of the sum (h2osoi_ice +
h2osoi_liq) in the equations that limit fluxes.

To try to deal with this, I have reworked the handling of the
evaporation constraint to directly limit both the liqevap and solidevap,
so that both of them should result in the equivalent liq or ice states
going to 0 within roundoff.

To do that, I needed to move the partitioning of the total flux into
liquid and solid to earlier in the subroutine and then recalculate those
partitioning fluxes in conditions where we're applying an evaporation
constraint.

Note that the deleted lines in this commit have just been moved as-is to
earlier in this subroutine.
ekluzek added a commit to TeaganKing/CTSM that referenced this issue Apr 27, 2023
Rework handling of evaporation constraint in SoilFluxes

Occasionally, h2osoi_ice was going significantly negative in
UpdateState_TopLayerFluxes - see
ESCOMP#1979. As noted in that issue, this
seems to be due to h2osoi_ice having a very different magnitude from
h2osoi_liq, leading to greater-than-roundoff-level differences from zero
final state in a relative sense (i.e., relative to the magnitude of
h2osoi_ice) - I think because of the appearance of the sum (h2osoi_ice +
h2osoi_liq) in the equations that limit fluxes.

To try to deal with this, I have reworked the handling of the
evaporation constraint to directly limit both the liqevap and solidevap,
so that both of them should result in the equivalent liq or ice states
going to 0 within roundoff.

To do that, I needed to move the partitioning of the total flux into
liquid and solid to earlier in the subroutine and then recalculate those
partitioning fluxes in conditions where we're applying an evaporation
constraint.

Note that I applied a max of 0 to the new fluxes because many initial
conditions files have roundoff-level negative H2OSOI_LIQ, so without
this limit, we were getting roundoff-level negative fluxes.

Resolves ESCOMP#1979
ekluzek added a commit to adamrher/CTSM that referenced this issue Apr 27, 2023
Rework handling of evaporation constraint in SoilFluxes

Occasionally, h2osoi_ice was going significantly negative in
UpdateState_TopLayerFluxes - see
ESCOMP#1979. As noted in that issue, this
seems to be due to h2osoi_ice having a very different magnitude from
h2osoi_liq, leading to greater-than-roundoff-level differences from zero
final state in a relative sense (i.e., relative to the magnitude of
h2osoi_ice) - I think because of the appearance of the sum (h2osoi_ice +
h2osoi_liq) in the equations that limit fluxes.

To try to deal with this, I have reworked the handling of the
evaporation constraint to directly limit both the liqevap and solidevap,
so that both of them should result in the equivalent liq or ice states
going to 0 within roundoff.

To do that, I needed to move the partitioning of the total flux into
liquid and solid to earlier in the subroutine and then recalculate those
partitioning fluxes in conditions where we're applying an evaporation
constraint.

Note that I applied a max of 0 to the new fluxes because many initial
conditions files have roundoff-level negative H2OSOI_LIQ, so without
this limit, we were getting roundoff-level negative fluxes.

Resolves ESCOMP#1979
ekluzek added a commit to dmleung/CTSM that referenced this issue Apr 28, 2023
Rework handling of evaporation constraint in SoilFluxes

Occasionally, h2osoi_ice was going significantly negative in
UpdateState_TopLayerFluxes - see
ESCOMP#1979. As noted in that issue, this
seems to be due to h2osoi_ice having a very different magnitude from
h2osoi_liq, leading to greater-than-roundoff-level differences from zero
final state in a relative sense (i.e., relative to the magnitude of
h2osoi_ice) - I think because of the appearance of the sum (h2osoi_ice +
h2osoi_liq) in the equations that limit fluxes.

To try to deal with this, I have reworked the handling of the
evaporation constraint to directly limit both the liqevap and solidevap,
so that both of them should result in the equivalent liq or ice states
going to 0 within roundoff.

To do that, I needed to move the partitioning of the total flux into
liquid and solid to earlier in the subroutine and then recalculate those
partitioning fluxes in conditions where we're applying an evaporation
constraint.

Note that I applied a max of 0 to the new fluxes because many initial
conditions files have roundoff-level negative H2OSOI_LIQ, so without
this limit, we were getting roundoff-level negative fluxes.

Resolves ESCOMP#1979
ekluzek added a commit to mvertens/ctsm that referenced this issue May 5, 2023
Rework handling of evaporation constraint in SoilFluxes

Occasionally, h2osoi_ice was going significantly negative in
UpdateState_TopLayerFluxes - see
ESCOMP#1979. As noted in that issue, this
seems to be due to h2osoi_ice having a very different magnitude from
h2osoi_liq, leading to greater-than-roundoff-level differences from zero
final state in a relative sense (i.e., relative to the magnitude of
h2osoi_ice) - I think because of the appearance of the sum (h2osoi_ice +
h2osoi_liq) in the equations that limit fluxes.

To try to deal with this, I have reworked the handling of the
evaporation constraint to directly limit both the liqevap and solidevap,
so that both of them should result in the equivalent liq or ice states
going to 0 within roundoff.

To do that, I needed to move the partitioning of the total flux into
liquid and solid to earlier in the subroutine and then recalculate those
partitioning fluxes in conditions where we're applying an evaporation
constraint.

Note that I applied a max of 0 to the new fluxes because many initial
conditions files have roundoff-level negative H2OSOI_LIQ, so without
this limit, we were getting roundoff-level negative fluxes.

Resolves ESCOMP#1979
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug something is working incorrectly
3 participants