Skip to content

Commit

Permalink
Move water fluxes back into waterflux_type
Browse files Browse the repository at this point in the history
I had hoped that most variables computed by a given module could be
owned by the type defined in that module - i.e., moving more towards
"textbook" object-orientation. However, I realized that this won't work
well for water fluxes due to the requirements of isotopes and other
water tracers: We need multiple instances of each type that contains any
fluxes for which we need separate instances for isotopes / tracers, but
we don't want separate instances for all variables. Mariana Vertenstein
and I considered a number of solutions to this problem, but in the end
decided it's best to just move these water fluxes out of the science
modules into waterflux_type, because other solutions we could think of
had bigger downsides. I feel like there was some value in the idea of
having the science modules own these fluxes, but not enough value to
warrant the extra complexity it will cause in the face of water isotopes
/ tracers. I'd still like to keep some variables local to the science
modules – especially variables that can be made private to those
modules. But the fluxes were never handled in a completely (textbook)
object-oriented fashion anyway, since they were accessed publicly by
other modules, so I'm not too sad to move these public flux variables
back out to the generic flux types. And while this has the disadvantage
of losing the attachment of a variable to the module that computes that
variable, it has some advantages, too, in terms of understanding the
suite of water flux variables, and reducing the number of instances that
need to get passed all around (e.g., we won't need to pass
irrigation_inst to as many places). (See
ESCOMP#395 (comment) for more
details.)

I'm even moving fluxes that could otherwise be private to the module in
which they're set. This is partly because these private fluxes may at
some point need multiple instances for isotopes / tracers (this is the
case for the fluxes in GlacierSurfaceMassBalanceMod - the separate melt
and freeze fluxes - which seem like they may need to be tracked
separately for isotopes... actually, I could envision needing track the
melt flux, qflx_glcice_melt, separately by layer for the sake of
isotopes), and partly because I don't want to have to change the home of
a variable just because some other module now needs it and so it now
needs to be public.

It's possible that not everything that is called a flux really needs to
go in waterflux_type. I'm particularly thinking about partial fluxes -
e.g., if we add foo + bar to get the full flux from state1 to state2,
then it's possible that we only need to store that sum in
waterflux_type, and could keep foo and bar in the science modules that
set them. (Specific example: I'm wondering if qflx_sat_excess_surf and
qflx_infl_excess should really be considered fluxes, or if these
"partial fluxes" can continue to live in their defining science
modules.) But I'm not sure about that (it may be that we'll need tracer
versions of even these partial fluxes), and even if it's true, a
downside would be that this could increase the volatility of where
things are declared (e.g., if it used to be that foo was the only flux,
and then we added bar later - would we then move foo out of
waterflux_type to its defining science module?) So at least for now,
I'll move these "partial fluxes" to waterflux_type; I can always move
them back once I understand the needs for tracers / isotopes
better. Another example of something that may not need to be in
waterflux_type is qflx_glcice_dyn_water_flux_col - but it seems possible
that we'll need separate isotope versions for this, so I'll move it for
now, and we could move it back later once we understand what's needed
for isotopes better.

I'm leaving non-flux variables in the module that computes them, even if
they're public - e.g., qinmax_col and fsat_col. I can see arguments
either way for these. For now I'll keep them in place, partly because
that's easier than moving them. But I can see a general principle:
Fluxes should be contained in waterflux_type, but auxiliary variables
(public or private) that are computed by a module should be owned by
that module. One justification for this is: For non-water-flux variables
that are only referenced by that module, it makes sense to make them
owned by that module and private. And we don't want to have to move things
around just because now someone else needs them. So where a variable
lives should be based less on public/private and more on whether it's a
water flux (which needs to be in the water flux type) or something else
(which can follow "better" object-oriented design).

For water fluxes computed by a routine, I considered passing them in
directly as intent(out) arrays rather than passing the whole
waterflux_type in, to make it more clear what the effect of the routine
is. But the problem I see with passing computed fluxes directly as
arrays is the potential for aliasing - i.e., if both waterflux_inst and
waterflux_inst%foo are passed to a routine, the latter as an output, and
then inside the routine, waterflux_inst%foo is used as an input, in
addition to setting foo. A solution could be to pass everything as bare
arrays, but this goes against what we have in place right now, and I
don't love that idea because, if we have two polymorphic
implementations, it makes it harder for them to have different input
requirements. So for now I'm sticking with passing in the whole
waterflux_type and documenting outputs in the associate block.
  • Loading branch information
billsacks committed Jul 10, 2018
1 parent 9280387 commit b699dd6
Show file tree
Hide file tree
Showing 15 changed files with 166 additions and 234 deletions.
10 changes: 3 additions & 7 deletions src/biogeophys/BalanceCheckMod.F90
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ module BalanceCheckMod
use WaterDiagnosticBulkType , only : waterdiagnosticbulk_type
use WaterBalanceType , only : waterbalance_type
use WaterfluxType , only : waterflux_type
use IrrigationMod , only : irrigation_type
use GlacierSurfaceMassBalanceMod, only : glacier_smb_type
use TotalWaterAndHeatMod, only : ComputeWaterMassNonLake, ComputeWaterMassLake
use GridcellType , only : grc
use LandunitType , only : lun
Expand Down Expand Up @@ -101,7 +99,7 @@ end subroutine BeginWaterBalance
!-----------------------------------------------------------------------
subroutine BalanceCheck( bounds, &
atm2lnd_inst, solarabs_inst, waterflux_inst, waterstatebulk_inst, waterdiagnosticbulk_inst, waterbalance_inst, &
irrigation_inst, glacier_smb_inst, energyflux_inst, canopystate_inst)
energyflux_inst, canopystate_inst)
!
! !DESCRIPTION:
! This subroutine accumulates the numerical truncation errors of the water
Expand Down Expand Up @@ -133,8 +131,6 @@ subroutine BalanceCheck( bounds, &
type(waterstatebulk_type) , intent(inout) :: waterstatebulk_inst
type(waterdiagnosticbulk_type) , intent(inout) :: waterdiagnosticbulk_inst
type(waterbalance_type) , intent(inout) :: waterbalance_inst
type(irrigation_type) , intent(in) :: irrigation_inst
type(glacier_smb_type), intent(in) :: glacier_smb_inst
type(energyflux_type) , intent(inout) :: energyflux_inst
type(canopystate_type), intent(inout) :: canopystate_inst
!
Expand Down Expand Up @@ -200,9 +196,9 @@ subroutine BalanceCheck( bounds, &
snow_sources => waterflux_inst%snow_sources_col , & ! Output: [real(r8) (:) ] snow sources (mm H2O /s)
snow_sinks => waterflux_inst%snow_sinks_col , & ! Output: [real(r8) (:) ] snow sinks (mm H2O /s)

qflx_irrig => irrigation_inst%qflx_irrig_col , & ! Input: [real(r8) (:) ] irrigation flux (mm H2O /s)
qflx_irrig => waterflux_inst%qflx_irrig_col , & ! Input: [real(r8) (:) ] irrigation flux (mm H2O /s)

qflx_glcice_dyn_water_flux => glacier_smb_inst%qflx_glcice_dyn_water_flux_col, & ! Input: [real(r8) (:)] water flux needed for balance check due to glc_dyn_runoff_routing (mm H2O/s) (positive means addition of water to the system)
qflx_glcice_dyn_water_flux => waterflux_inst%qflx_glcice_dyn_water_flux_col, & ! Input: [real(r8) (:)] water flux needed for balance check due to glc_dyn_runoff_routing (mm H2O/s) (positive means addition of water to the system)

eflx_lwrad_out => energyflux_inst%eflx_lwrad_out_patch , & ! Input: [real(r8) (:) ] emitted infrared (longwave) radiation (W/m**2)
eflx_lwrad_net => energyflux_inst%eflx_lwrad_net_patch , & ! Input: [real(r8) (:) ] net infrared (longwave) rad (W/m**2) [+ = to atm]
Expand Down
7 changes: 2 additions & 5 deletions src/biogeophys/CanopyHydrologyMod.F90
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ module CanopyHydrologyMod
use WaterfluxType , only : waterflux_type
use WaterStateBulkType , only : waterstatebulk_type
use WaterDiagnosticBulkType , only : waterdiagnosticbulk_type
use IrrigationMod , only : irrigation_type
use ColumnType , only : col
use PatchType , only : patch
!
Expand Down Expand Up @@ -143,7 +142,7 @@ end subroutine CanopyHydrology_readnl
subroutine CanopyHydrology(bounds, &
num_nolakec, filter_nolakec, num_nolakep, filter_nolakep, &
atm2lnd_inst, canopystate_inst, temperature_inst, &
aerosol_inst, waterstatebulk_inst, waterdiagnosticbulk_inst, waterflux_inst, irrigation_inst)
aerosol_inst, waterstatebulk_inst, waterdiagnosticbulk_inst, waterflux_inst)
!
! !DESCRIPTION:
! Calculation of
Expand Down Expand Up @@ -178,7 +177,6 @@ subroutine CanopyHydrology(bounds, &
type(waterstatebulk_type) , intent(inout) :: waterstatebulk_inst
type(waterdiagnosticbulk_type) , intent(inout) :: waterdiagnosticbulk_inst
type(waterflux_type) , intent(inout) :: waterflux_inst
type(irrigation_type) , intent(in) :: irrigation_inst
!
! !LOCAL VARIABLES:
integer :: f ! filter index
Expand Down Expand Up @@ -266,8 +264,7 @@ subroutine CanopyHydrology(bounds, &
qflx_prec_intr => waterflux_inst%qflx_prec_intr_patch , & ! Output: [real(r8) (:) ] interception of precipitation [mm/s]
qflx_prec_grnd => waterflux_inst%qflx_prec_grnd_patch , & ! Output: [real(r8) (:) ] water onto ground including canopy runoff [kg/(m2 s)]
qflx_rain_grnd => waterflux_inst%qflx_rain_grnd_patch , & ! Output: [real(r8) (:) ] rain on ground after interception (mm H2O/s) [+]

qflx_irrig => irrigation_inst%qflx_irrig_patch , & ! Input: [real(r8) (:) ] irrigation amount (mm/s)
qflx_irrig => waterflux_inst%qflx_irrig_patch , & ! Input: [real(r8) (:) ] irrigation amount (mm/s)
qflx_snowindunload => waterflux_inst%qflx_snowindunload_patch , & ! Output: [real(r8) (:) ] canopy snow unloading from wind [mm/s]
qflx_snotempunload => waterflux_inst%qflx_snotempunload_patch & ! Output: [real(r8) (:) ] canopy snow unloading from temp. [mm/s]
)
Expand Down
132 changes: 20 additions & 112 deletions src/biogeophys/GlacierSurfaceMassBalanceMod.F90
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@ module GlacierSurfaceMassBalanceMod
#include "shr_assert.h"
use shr_kind_mod , only : r8 => shr_kind_r8
use shr_log_mod , only : errMsg => shr_log_errMsg
use shr_infnan_mod , only : nan => shr_infnan_nan, assignment(=)
use decompMod , only : bounds_type
use clm_varcon , only : spval, secspday
use clm_varcon , only : secspday
use clm_varpar , only : nlevgrnd
use clm_varctl , only : glc_snow_persistence_max_days
use clm_time_manager, only : get_step_size
Expand All @@ -29,20 +28,6 @@ module GlacierSurfaceMassBalanceMod
type, public :: glacier_smb_type
private

! ------------------------------------------------------------------------
! Public data
! ------------------------------------------------------------------------

real(r8), pointer, public :: qflx_glcice_col(:) ! col net flux of new glacial ice (growth - melt) (mm H2O/s), passed to GLC; only valid inside the do_smb_c filter
real(r8), pointer, public :: qflx_glcice_dyn_water_flux_col(:) ! col water flux needed for balance check due to glc_dyn_runoff_routing (mm H2O/s) (positive means addition of water to the system); valid for all columns

! ------------------------------------------------------------------------
! Private data
! ------------------------------------------------------------------------

real(r8), pointer :: qflx_glcice_frz_col (:) ! col ice growth (positive definite) (mm H2O/s); only valid inside the do_smb_c filter
real(r8), pointer :: qflx_glcice_melt_col(:) ! col ice melt (positive definite) (mm H2O/s); only valid inside the do_smb_c filter

contains

! ------------------------------------------------------------------------
Expand All @@ -57,14 +42,6 @@ module GlacierSurfaceMassBalanceMod
procedure, public :: ComputeSurfaceMassBalance ! compute fluxes other than ice melt
procedure, public :: AdjustRunoffTerms ! adjust liquid and ice runoff fluxes due to glacier fluxes

! ------------------------------------------------------------------------
! Private routines
! ------------------------------------------------------------------------

procedure, private :: InitAllocate
procedure, private :: InitHistory
procedure, private :: InitCold

end type glacier_smb_type

character(len=*), parameter, private :: sourcefile = &
Expand All @@ -82,86 +59,16 @@ subroutine Init(this, bounds)
type(bounds_type), intent(in) :: bounds
!-----------------------------------------------------------------------

call this%InitAllocate(bounds)
call this%InitHistory(bounds)
call this%InitCold(bounds)
! Nothing to do for now
end subroutine Init

!-----------------------------------------------------------------------
subroutine InitAllocate(this, bounds)
class(glacier_smb_type), intent(inout) :: this
type(bounds_type), intent(in) :: bounds

integer :: begc, endc
!-----------------------------------------------------------------------

begc = bounds%begc; endc = bounds%endc

allocate(this%qflx_glcice_col (begc:endc)) ; this%qflx_glcice_col (:) = nan
allocate(this%qflx_glcice_dyn_water_flux_col(begc:endc)) ; this%qflx_glcice_dyn_water_flux_col (:) = nan
allocate(this%qflx_glcice_frz_col (begc:endc)) ; this%qflx_glcice_frz_col (:) = nan
allocate(this%qflx_glcice_melt_col (begc:endc)) ; this%qflx_glcice_melt_col (:) = nan
end subroutine InitAllocate

!-----------------------------------------------------------------------
subroutine InitHistory(this, bounds)
!
! !USES:
use histFileMod , only : hist_addfld1d
!
! !ARGUMENTS:
class(glacier_smb_type), intent(inout) :: this
type(bounds_type), intent(in) :: bounds
!
! !LOCAL VARIABLES:
integer :: begc, endc
!-----------------------------------------------------------------------

begc = bounds%begc; endc = bounds%endc

this%qflx_glcice_col(begc:endc) = spval
call hist_addfld1d (fname='QICE', units='mm/s', &
avgflag='A', long_name='ice growth/melt', &
ptr_col=this%qflx_glcice_col, l2g_scale_type='ice')

this%qflx_glcice_frz_col(begc:endc) = spval
call hist_addfld1d (fname='QICE_FRZ', units='mm/s', &
avgflag='A', long_name='ice growth', &
ptr_col=this%qflx_glcice_frz_col, l2g_scale_type='ice')

this%qflx_glcice_melt_col(begc:endc) = spval
call hist_addfld1d (fname='QICE_MELT', units='mm/s', &
avgflag='A', long_name='ice melt', &
ptr_col=this%qflx_glcice_melt_col, l2g_scale_type='ice')

end subroutine InitHistory

!-----------------------------------------------------------------------
subroutine InitCold(this, bounds)
class(glacier_smb_type), intent(inout) :: this
type(bounds_type), intent(in) :: bounds

integer :: c
!-----------------------------------------------------------------------

! Initialize qflx_glcice_dyn_water_flux_col to 0 for all columns because we want this
! flux to remain 0 for columns where is is never set, including non-glacier columns.
!
! Other fluxes intentionally remain unset (spval) outside the do_smb filter, so that
! they are flagged as missing value outside that filter.
do c = bounds%begc, bounds%endc
this%qflx_glcice_dyn_water_flux_col(c) = 0._r8
end do

end subroutine InitCold

! ========================================================================
! Science routines
! ========================================================================

!-----------------------------------------------------------------------
subroutine HandleIceMelt(this, bounds, num_do_smb_c, filter_do_smb_c, &
waterstatebulk_inst)
waterstatebulk_inst, waterflux_inst)
!
! !DESCRIPTION:
! Compute ice melt in glacier columns, and convert liquid back to ice
Expand All @@ -184,11 +91,12 @@ subroutine HandleIceMelt(this, bounds, num_do_smb_c, filter_do_smb_c, &
! may be best to keep this separate.
!
! !ARGUMENTS:
class(glacier_smb_type), intent(inout) :: this
class(glacier_smb_type), intent(in) :: this
type(bounds_type), intent(in) :: bounds
integer, intent(in) :: num_do_smb_c ! number of column points in filter_do_smb_c
integer, intent(in) :: filter_do_smb_c(:) ! column filter for points where SMB is calculated
type(waterstatebulk_type), intent(inout) :: waterstatebulk_inst
type(waterflux_type), intent(inout) :: waterflux_inst
!
! !LOCAL VARIABLES:
integer :: j
Expand All @@ -199,9 +107,9 @@ subroutine HandleIceMelt(this, bounds, num_do_smb_c, filter_do_smb_c, &
!-----------------------------------------------------------------------

associate( &
qflx_glcice_melt => this%qflx_glcice_melt_col , & ! Output: [real(r8) (:) ] ice melt (positive definite) (mm H2O/s)
h2osoi_liq => waterstatebulk_inst%h2osoi_liq_col , & ! Output: [real(r8) (:,:) ] liquid water (kg/m2)
h2osoi_ice => waterstatebulk_inst%h2osoi_ice_col & ! Output: [real(r8) (:,:) ] ice lens (kg/m2)
h2osoi_ice => waterstatebulk_inst%h2osoi_ice_col , & ! Output: [real(r8) (:,:) ] ice lens (kg/m2)
qflx_glcice_melt => waterflux_inst%qflx_glcice_melt_col & ! Output: [real(r8) (:) ] ice melt (positive definite) (mm H2O/s)
)

dtime = get_step_size()
Expand Down Expand Up @@ -243,22 +151,21 @@ subroutine ComputeSurfaceMassBalance(this, bounds, num_allc, filter_allc, &
! !DESCRIPTION:
! Compute glacier fluxes other than ice melt.
!
! This sets the public fields qflx_glcice_col and qflx_glcice_dyn_water_flux_col to
! their final values.
! This sets qflx_glcice_col and qflx_glcice_dyn_water_flux_col.
!
! Should be called after HandleIceMelt, and after waterflux_inst%qflx_snwcp_ice_col is
! computed
!
! !ARGUMENTS:
class(glacier_smb_type), intent(inout) :: this
class(glacier_smb_type), intent(in) :: this
type(bounds_type), intent(in) :: bounds
integer, intent(in) :: num_allc ! number of column points in filter_allc
integer, intent(in) :: filter_allc(:) ! column filter for all points
integer, intent(in) :: num_do_smb_c ! number of column points in filter_do_smb_c
integer, intent(in) :: filter_do_smb_c(:) ! column filter for points where SMB is calculated
type(glc2lnd_type), intent(in) :: glc2lnd_inst
type(waterstatebulk_type), intent(in) :: waterstatebulk_inst
type(waterflux_type), intent(in) :: waterflux_inst
type(waterflux_type), intent(inout) :: waterflux_inst
!
! !LOCAL VARIABLES:
integer :: fc, c, l, g
Expand All @@ -267,13 +174,13 @@ subroutine ComputeSurfaceMassBalance(this, bounds, num_allc, filter_allc, &
!-----------------------------------------------------------------------

associate( &
qflx_glcice => this%qflx_glcice_col , & ! Output: [real(r8) (:)] net flux of new glacial ice (growth - melt) (mm H2O/s)
qflx_glcice_frz => this%qflx_glcice_frz_col , & ! Output: [real(r8) (:)] ice growth (positive definite) (mm H2O/s)
qflx_glcice_dyn_water_flux => this%qflx_glcice_dyn_water_flux_col , & ! Output: [real(r8) (:)] water flux needed for balance check due to glc_dyn_runoff_routing (mm H2O/s) (positive means addition of water to the system)
qflx_glcice_melt => this%qflx_glcice_melt_col , & ! Input: [real(r8) (:)] ice melt (positive definite) (mm H2O/s)
glc_dyn_runoff_routing => glc2lnd_inst%glc_dyn_runoff_routing_grc , & ! Input: [real(r8) (:)] whether we're doing runoff routing appropriate for having a dynamic icesheet
snow_persistence => waterstatebulk_inst%snow_persistence_col , & ! Input: [real(r8) (:)] counter for length of time snow-covered
qflx_snwcp_ice => waterflux_inst%qflx_snwcp_ice_col & ! Input: [real(r8) (:)] excess solid h2o due to snow capping (outgoing) (mm H2O /s) [+]
snow_persistence => waterstatebulk_inst%snow_persistence_col, & ! Input: [real(r8) (:)] counter for length of time snow-covered
qflx_snwcp_ice => waterflux_inst%qflx_snwcp_ice_col , & ! Input: [real(r8) (:)] excess solid h2o due to snow capping (outgoing) (mm H2O /s) [+]
qflx_glcice_melt => waterflux_inst%qflx_glcice_melt_col , & ! Input: [real(r8) (:)] ice melt (positive definite) (mm H2O/s)
qflx_glcice => waterflux_inst%qflx_glcice_col , & ! Output: [real(r8) (:)] net flux of new glacial ice (growth - melt) (mm H2O/s)
qflx_glcice_frz => waterflux_inst%qflx_glcice_frz_col , & ! Output: [real(r8) (:)] ice growth (positive definite) (mm H2O/s)
qflx_glcice_dyn_water_flux => waterflux_inst%qflx_glcice_dyn_water_flux_col & ! Output: [real(r8) (:)] water flux needed for balance check due to glc_dyn_runoff_routing (mm H2O/s) (positive means addition of water to the system)
)

! NOTE(wjs, 2016-06-29) The following initialization is done in case the columns
Expand Down Expand Up @@ -340,7 +247,7 @@ end subroutine ComputeSurfaceMassBalance

!-----------------------------------------------------------------------
subroutine AdjustRunoffTerms(this, bounds, num_do_smb_c, filter_do_smb_c, &
glc2lnd_inst, qflx_qrgwl, qflx_ice_runoff_snwcp)
waterflux_inst, glc2lnd_inst, qflx_qrgwl, qflx_ice_runoff_snwcp)
!
! !DESCRIPTION:
! Adjust liquid and ice runoff fluxes due to glacier fluxes
Expand All @@ -355,6 +262,7 @@ subroutine AdjustRunoffTerms(this, bounds, num_do_smb_c, filter_do_smb_c, &
type(bounds_type), intent(in) :: bounds
integer, intent(in) :: num_do_smb_c ! number of column points in filter_do_smb_c
integer, intent(in) :: filter_do_smb_c(:) ! column filter for points where SMB is calculated
type(waterflux_type), intent(in) :: waterflux_inst
type(glc2lnd_type), intent(in) :: glc2lnd_inst
real(r8), intent(inout) :: qflx_qrgwl( bounds%begc: ) ! col qflx_surf at glaciers, wetlands, lakes
real(r8), intent(inout) :: qflx_ice_runoff_snwcp( bounds%begc: ) ! col solid runoff from snow capping (mm H2O /s)
Expand All @@ -369,8 +277,8 @@ subroutine AdjustRunoffTerms(this, bounds, num_do_smb_c, filter_do_smb_c, &
SHR_ASSERT_ALL((ubound(qflx_ice_runoff_snwcp) == (/bounds%endc/)), errMsg(sourcefile, __LINE__))

associate( &
qflx_glcice_frz => this%qflx_glcice_frz_col , & ! Input: [real(r8) (:)] ice growth (positive definite) (mm H2O/s)
qflx_glcice_melt => this%qflx_glcice_melt_col , & ! Input: [real(r8) (:)] ice melt (positive definite) (mm H2O/s)
qflx_glcice_frz => waterflux_inst%qflx_glcice_frz_col , & ! Input: [real(r8) (:)] ice growth (positive definite) (mm H2O/s)
qflx_glcice_melt => waterflux_inst%qflx_glcice_melt_col , & ! Input: [real(r8) (:)] ice melt (positive definite) (mm H2O/s)
glc_dyn_runoff_routing => glc2lnd_inst%glc_dyn_runoff_routing_grc & ! Input: [real(r8) (:)] gridcell fraction coupled to dynamic ice sheet
)

Expand Down
Loading

0 comments on commit b699dd6

Please sign in to comment.