Skip to content

Commit

Permalink
Move different drainPond calculation options to their own methods
Browse files Browse the repository at this point in the history
Main point is to decrease the clutter in the main QflxH2osfcDrain
routine - trying to keep the focus more on the science than on the
alternative numerical solutions.
  • Loading branch information
billsacks committed Dec 28, 2017
1 parent b872d21 commit ea15490
Showing 1 changed file with 49 additions and 27 deletions.
76 changes: 49 additions & 27 deletions src/biogeophys/SoilHydrologyMod.F90
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,15 @@ module SoilHydrologyMod
private :: QflxH2osfcDrain ! Compute qflx_h2osfc_drain

type, extends(computeFlux_type) :: drainPond_type
real(r8) :: smoothScale
real(r8) :: drainMax
private
real(r8), public :: drainMax ! maximum drainage rate of ponded water
real(r8), public :: dtime ! model time step
real(r8) :: smoothScale = 0.05_r8 ! smoothing scale

This comment has been minimized.

Copy link
@billsacks

billsacks Dec 28, 2017

Author Member

Comment from @billsacks Sep 11, 2017

smoothScale can't be a parameter because you can't have a parameter in a derived type. We could pull it out to module-level, though keeping it in the derived type provides a path to making it namelist-settable

contains
procedure :: getFlux => drainPondFlux
procedure :: getFlux => drainPondFlux ! required method to get the current flux estimate
procedure :: drainPondExplicitEuler ! compute the drainPond flux using an explicit euler method
procedure :: drainPondImplicitEuler ! compute the drainPond flux using an implicit euler method
procedure :: drainPondAnalytical ! compute the drainPond flux using an analytical solution
end type drainPond_type

!-----------------------------------------------------------------------
Expand Down Expand Up @@ -595,11 +600,6 @@ subroutine QflxH2osfcDrain(bounds, num_hydrologyc, filter_hydrologyc, &
type(drainPond_type) :: drainPond_inst
integer :: fc, c
real(r8) :: dtime ! land model time step (sec)
real(r8) :: h2osfc1 ! h2osfc at the end of the time step, given qflx_h2osfc_drain only
real(r8) :: drainMax ! maximum drainage rate of ponded water
real(r8) :: const ! constant in analytical integral
real(r8) :: uFunc ! analytical integral
real(r8), parameter :: smoothScale=0.05_r8 ! smoothing scale

character(len=*), parameter :: subname = 'QflxH2osfcDrain'
!-----------------------------------------------------------------------
Expand All @@ -611,6 +611,7 @@ subroutine QflxH2osfcDrain(bounds, num_hydrologyc, filter_hydrologyc, &
SHR_ASSERT_ALL((ubound(truncate_h2osfc_to_zero) == (/bounds%endc/)), errMsg(sourcefile, __LINE__))

dtime = get_step_size()
drainPond_inst%dtime = dtime

do fc = 1, num_hydrologyc
c = filter_hydrologyc(fc)
Expand All @@ -622,32 +623,18 @@ subroutine QflxH2osfcDrain(bounds, num_hydrologyc, filter_hydrologyc, &

! define maximum drainage
! NOTE: check fraction
drainMax = frac_h2osfc(c)*qinmax(c)
drainPond_inst%drainMax = frac_h2osfc(c)*qinmax(c)

This comment has been minimized.

Copy link
@billsacks

billsacks Dec 28, 2017

Author Member

Comment from @billsacks Sep 11, 2017

We could replace this select case statement with a function pointer that is set up in initialization - i.e., moving the select case to initialization, where it is used to point a function pointer to the right place. I'm not suggesting this for performance reasons (since I'd guess performance doesn't change much between these two approaches... and if it does, I don't know which would be better), but rather for reasons of clarity. I could imagine the code being clearer if the select case were removed... though on the other hand, I could also imagine the code being more obscure in that case. I don't have strong feelings as to which is better.


! switch between different numerical solutions
select case(ixSolution)

! constrained Explicit Euler solution with operator splitting (original)
case(ixExplicitEuler)
qflx_h2osfc_drain(c)=min(drainMax,h2osfc(c)/dtime)

! implicit Euler solution with operator splitting
call drainPond_inst%drainPondExplicitEuler(h2osfc(c), qflx_h2osfc_drain(c))
case(ixImplicitEuler)
! The next two lines look like extra overhead, but in a real implementation
! (1) smoothScale could probably be local to the drainPond type; (2)
! drainMax would probably also be local to the drainPond type, and if not,
! it would be an array that is just set once outside a loop, rather than
! being set for each loop iteration.
drainPond_inst%smoothScale = smoothScale
drainPond_inst%drainMax = drainMax
call implicitEuler(drainPond_inst, dtime, h2osfc(c), qflx_h2osfc_drain(c))

call drainPond_inst%drainPondImplicitEuler(h2osfc(c), qflx_h2osfc_drain(c))
! analytical solution with operator splitting
case(ixAnalytical)
const = 1._r8 - 1._r8/(1._r8 - exp(-h2osfc(c)/smoothScale))
uFunc = -1._r8/(const*exp(drainMax*dtime/smoothScale) - 1._r8)
h2osfc1 = -log(1._r8 - uFunc)*smoothScale
qflx_h2osfc_drain(c)= (h2osfc1 - h2osfc(c))/dtime
call drainPond_inst%drainPondAnalytical(h2osfc(c), qflx_h2osfc_drain(c))
end select

if(h2osfcflag==0) then
Expand All @@ -674,8 +661,43 @@ subroutine drainPondFlux(this, x, f, dfdx)
if(present(dfdx)) dfdx = -this%drainMax*arg/this%smoothScale
end subroutine drainPondFlux

subroutine drainPondExplicitEuler(this, h2osfc, qflx_h2osfc_drain)
! Compute the drainPond flux using an explicit euler method
class(drainPond_type), intent(in) :: this
real(r8) , intent(in) :: h2osfc ! drainPond storage
real(r8) , intent(out) :: qflx_h2osfc_drain ! drainage flux

!-----------------------------------------------------------------------
! constrained Explicit Euler solution with operator splitting (original)
qflx_h2osfc_drain = min(this%drainMax, h2osfc/this%dtime)
end subroutine drainPondExplicitEuler

subroutine drainPondImplicitEuler(this, h2osfc, qflx_h2osfc_drain)

This comment has been minimized.

Copy link
@billsacks

billsacks Dec 28, 2017

Author Member

Comment from @billsacks Sep 11, 2017

The drainPondImplicitEuler wrapper doesn't provide a lot of benefit, but is there for consistency with the other methods, and is necessary for my plans for the next commit

! Compute the drainPond flux using an implicit euler method
class(drainPond_type), intent(in) :: this
real(r8) , intent(in) :: h2osfc ! drainPond storage
real(r8) , intent(out) :: qflx_h2osfc_drain ! drainage flux

! implicit Euler solution with operator splitting
call implicitEuler(this, this%dtime, h2osfc, qflx_h2osfc_drain)
end subroutine drainPondImplicitEuler

subroutine drainPondAnalytical(this, h2osfc, qflx_h2osfc_drain)
! Compute the drainPond flux using an analytical solution
class(drainPond_type), intent(in) :: this
real(r8) , intent(in) :: h2osfc ! drainPond storage
real(r8) , intent(out) :: qflx_h2osfc_drain ! drainage flux

real(r8) :: h2osfc1 ! h2osfc at the end of the time step, given qflx_h2osfc_drain only
real(r8) :: const ! constant in analytical integral
real(r8) :: uFunc ! analytical integral

const = 1._r8 - 1._r8/(1._r8 - exp(-h2osfc/this%smoothScale))
uFunc = -1._r8/(const*exp(this%drainMax*this%dtime/this%smoothScale) - 1._r8)
h2osfc1 = -log(1._r8 - uFunc)*this%smoothScale
qflx_h2osfc_drain= (h2osfc1 - h2osfc)/this%dtime
end subroutine drainPondAnalytical

!-----------------------------------------------------------------------
subroutine TotalSurfaceRunoff(bounds, num_hydrologyc, filter_hydrologyc, &
num_urbanc, filter_urbanc, &
waterflux_inst, soilhydrology_inst, saturated_excess_runoff_inst, waterstate_inst)
Expand Down

0 comments on commit ea15490

Please sign in to comment.