diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm
index f105491d18..a172ac445b 100755
--- a/bld/CLMBuildNamelist.pm
+++ b/bld/CLMBuildNamelist.pm
@@ -2455,6 +2455,7 @@ sub setup_logic_dynamic_subgrid {
setup_logic_do_transient_pfts($opts, $nl_flags, $definition, $defaults, $nl);
setup_logic_do_transient_crops($opts, $nl_flags, $definition, $defaults, $nl);
setup_logic_do_transient_lakes($opts, $nl_flags, $definition, $defaults, $nl);
+ setup_logic_do_transient_urban($opts, $nl_flags, $definition, $defaults, $nl);
setup_logic_do_harvest($opts, $nl_flags, $definition, $defaults, $nl);
add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, 'reset_dynbal_baselines');
@@ -2683,6 +2684,69 @@ sub setup_logic_do_transient_lakes {
}
}
+sub setup_logic_do_transient_urban {
+ #
+ # Set do_transient_urban default value, and perform error checking on do_transient_urban
+ #
+ # Assumes the following are already set in the namelist (although it's okay
+ # for them to be unset if that will be their final state):
+ # - flanduse_timeseries
+ #
+ # NOTE(kwo, 2021-08-11) I based this function on setup_logic_do_transient_lakes.
+ # As in NOTE(wjs, 2020-08-23) I'm not sure if all of the checks here are truly important
+ # for transient urban (in particular, my guess is that collapse_urban could probably be done with transient
+ # urban - as well as transient pfts and transient crops for that matter), but some of
+ # the checks probably are needed, and it seems best to keep transient urban consistent
+ # with other transient areas in this respect.
+ my ($opts, $nl_flags, $definition, $defaults, $nl) = @_;
+
+ my $var = 'do_transient_urban';
+
+ # cannot_be_true will be set to a non-empty string in any case where
+ # do_transient_urban should not be true; if it turns out that
+ # do_transient_urban IS true in any of these cases, a fatal error will be
+ # generated
+ my $cannot_be_true = "";
+
+ my $n_dom_pfts = $nl->get_value( 'n_dom_pfts' );
+ my $n_dom_landunits = $nl->get_value( 'n_dom_landunits' );
+ my $toosmall_soil = $nl->get_value( 'toosmall_soil' );
+ my $toosmall_crop = $nl->get_value( 'toosmall_crop' );
+ my $toosmall_glacier = $nl->get_value( 'toosmall_glacier' );
+ my $toosmall_lake = $nl->get_value( 'toosmall_lake' );
+ my $toosmall_wetland = $nl->get_value( 'toosmall_wetland' );
+ my $toosmall_urban = $nl->get_value( 'toosmall_urban' );
+
+ if (string_is_undef_or_empty($nl->get_value('flanduse_timeseries'))) {
+ $cannot_be_true = "$var can only be set to true when running a transient case (flanduse_timeseries non-blank)";
+ }
+
+ if (!$cannot_be_true) {
+ # Note that, if the variable cannot be true, we don't call add_default
+ # - so that we don't clutter up the namelist with variables that don't
+ # matter for this case
+ add_default($opts, $nl_flags->{'inputdata_rootdir'}, $definition, $defaults, $nl, $var);
+ }
+
+ # Make sure the value is false when it needs to be false - i.e., that the
+ # user hasn't tried to set a true value at an inappropriate time.
+
+ if (&value_is_true($nl->get_value($var)) && $cannot_be_true) {
+ $log->fatal_error($cannot_be_true);
+ }
+
+ # if do_transient_urban is .true. and any of these (n_dom_* or toosmall_*)
+ # are > 0 or collapse_urban = .true., then give fatal error
+ if (&value_is_true($nl->get_value($var))) {
+ if (&value_is_true($nl->get_value('collapse_urban'))) {
+ $log->fatal_error("$var cannot be combined with collapse_urban");
+ }
+ if ($n_dom_pfts > 0 || $n_dom_landunits > 0 || $toosmall_soil > 0 || $toosmall_crop > 0 || $toosmall_glacier > 0 || $toosmall_lake > 0 || $toosmall_wetland > 0 || $toosmall_urban > 0) {
+ $log->fatal_error("$var cannot be combined with any of the of the following > 0: n_dom_pfts > 0, n_dom_landunit > 0, toosmall_soil > 0._r8, toosmall_crop > 0._r8, toosmall_glacier > 0._r8, toosmall_lake > 0._r8, toosmall_wetland > 0._r8, toosmall_urban > 0._r8");
+ }
+ }
+}
+
sub setup_logic_do_harvest {
#
# Set do_harvest default value, and perform error checking on do_harvest
diff --git a/bld/namelist_files/namelist_defaults_ctsm.xml b/bld/namelist_files/namelist_defaults_ctsm.xml
index a61d66360b..1ee46abecb 100644
--- a/bld/namelist_files/namelist_defaults_ctsm.xml
+++ b/bld/namelist_files/namelist_defaults_ctsm.xml
@@ -2569,6 +2569,7 @@ lnd/clm2/surfdata_map/release-clm5.0.30/surfdata_ne0np4.CONUS.ne30x8_hist_78pfts
.false.
+.false..false.
diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml
index 8eb9f5c588..a79991efd8 100644
--- a/bld/namelist_files/namelist_definition_ctsm.xml
+++ b/bld/namelist_files/namelist_definition_ctsm.xml
@@ -2484,6 +2484,12 @@ If TRUE, apply transient lakes from flanduse_timeseries file.
(Only valid for transient runs, where there is a flanduse_timeseries file.)
+
+If TRUE, apply transient urban from flanduse_timeseries file.
+(Only valid for transient runs, where there is a flanduse_timeseries file.)
+
+
If TRUE, apply harvest from flanduse_timeseries file.
diff --git a/cime_config/testdefs/testlist_clm.xml b/cime_config/testdefs/testlist_clm.xml
index 5435ad246b..bbdf3cbd85 100644
--- a/cime_config/testdefs/testlist_clm.xml
+++ b/cime_config/testdefs/testlist_clm.xml
@@ -1669,6 +1669,16 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/cime_config/testdefs/testmods_dirs/clm/smallville_dynurban_monthly/include_user_mods b/cime_config/testdefs/testmods_dirs/clm/smallville_dynurban_monthly/include_user_mods
new file mode 100644
index 0000000000..399579f425
--- /dev/null
+++ b/cime_config/testdefs/testmods_dirs/clm/smallville_dynurban_monthly/include_user_mods
@@ -0,0 +1 @@
+../monthly
diff --git a/cime_config/testdefs/testmods_dirs/clm/smallville_dynurban_monthly/modify_smallville_with_dynurban.ncl b/cime_config/testdefs/testmods_dirs/clm/smallville_dynurban_monthly/modify_smallville_with_dynurban.ncl
new file mode 100644
index 0000000000..a70d5e9641
--- /dev/null
+++ b/cime_config/testdefs/testmods_dirs/clm/smallville_dynurban_monthly/modify_smallville_with_dynurban.ncl
@@ -0,0 +1,63 @@
+; NCL script
+; modify_smallville_with_dynurban.ncl
+; Keith Oleson, Dec 2021
+; Purpose is to create a dynamic urban file for the smallville grid for test
+; ERS_Lm25.1x1_smallvilleIA.IHistClm50BgcCropQianRs.cheyenne_gnu.clm-smallville_dynurban_monthly
+;**************************************
+
+load "$NCARG_ROOT/lib/ncarg/nclscripts/csm/gsn_code.ncl"
+load "$NCARG_ROOT/lib/ncarg/nclscripts/csm/gsn_csm.ncl"
+load "$NCARG_ROOT/lib/ncarg/nclscripts/csm/contributed.ncl"
+load "$NCARG_ROOT/lib/ncarg/nclscripts/csm/shea_util.ncl"
+
+begin
+
+ print ("=========================================")
+ print ("Start Time: "+systemfunc("date") )
+ print ("=========================================")
+
+ infile = "/glade/p/cgd/tss/people/oleson/modify_surfdata/landuse.timeseries_1x1_smallvilleIA_hist_78pfts_simyr1850-1855_c160127.nc"
+ outfile = "/glade/p/cgd/tss/people/oleson/modify_surfdata/landuse.timeseries_1x1_smallvilleIA_hist_78pfts_simyr1850-1855_dynUrban_c211206.nc"
+
+ system("cp " + infile + " " + outfile)
+
+ outf = addfile(outfile,"w")
+
+ numurbl = 3
+
+ pct_crop = outf->PCT_CROP
+ printVarSummary(pct_crop)
+ pct_urban = new((/dimsizes(pct_crop(:,0,0)),numurbl,dimsizes(pct_crop(0,:,0)),dimsizes(pct_crop(0,0,:))/),double,"No_FillValue")
+ pct_urban!0 = "time"
+ pct_urban&time = pct_crop&time
+ pct_urban!1 = "numurbl"
+ pct_urban!2 = pct_crop!1
+ pct_urban!3 = pct_crop!2
+ pct_urban@long_name = "percent urban for each density type (tbd, hd, md)"
+ pct_urban@units = "unitless"
+ printVarSummary(pct_urban)
+
+ hasurban = new((/numurbl,dimsizes(pct_crop(0,:,0)),dimsizes(pct_crop(0,0,:))/),double,"No_FillValue")
+ hasurban!0 = pct_urban!1
+ hasurban!1 = pct_urban!2
+ hasurban!2 = pct_urban!3
+ hasurban = 1.d
+ printVarSummary(hasurban)
+
+ pct_urban(:,0,0,0) = (/0.d,20.d,10.d,10.d,10.d,10.d/)
+ pct_urban(:,1,0,0) = (/0.d,15.d, 8.d, 8.d, 8.d, 8.d/)
+ pct_urban(:,2,0,0) = (/0.d,10.d, 5.d, 5.d, 5.d, 5.d/)
+
+ pct_crop(:,0,0) = (/0.,25.,12.,12.,12.,12./)
+
+ outf->HASURBAN = hasurban
+ outf->PCT_URBAN = pct_urban
+ outf->PCT_CROP = pct_crop
+
+ outf@history = "This file was created with the following NCL script: /glade/p/cgd/tss/people/oleson/modify_surfdata/modify_smallville_with_dynurban.ncl. The file used as a template is: /glade/p/cesm/cseg/inputdata/lnd/clm2/surfdata_map/landuse.timeseries_1x1_smallvilleIA_hist_78pfts_simyr1850-1855_c160127.nc. Key points are that urban area starts as 0, increases after the first year, then decreases after the second year. PCT_CROP is also changed so that PCT_URBAN + PCT_CROP <= 100. (Here, PCT_CROP increases and decreases at the same time as PCT_URBAN in order to exercise the simultaneous increase or decrease of two landunits, but that isn't a critical part of this test.). Note that the use of this file means that this testmod can only be used with the 1x1_smallvilleIA grid."
+
+ print ("=========================================")
+ print ("Finish Time: "+systemfunc("date") )
+ print ("=========================================")
+
+end
diff --git a/cime_config/testdefs/testmods_dirs/clm/smallville_dynurban_monthly/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/smallville_dynurban_monthly/user_nl_clm
new file mode 100644
index 0000000000..ebda6ab408
--- /dev/null
+++ b/cime_config/testdefs/testmods_dirs/clm/smallville_dynurban_monthly/user_nl_clm
@@ -0,0 +1,12 @@
+do_transient_urban = .true.
+!KO The following run_zero_weight_urban setting is temporary until the HASURBAN methdology is implemented.
+run_zero_weight_urban = .true.
+
+! This file was created with the following NCL script:
+! /glade/p/cgd/tss/people/oleson/modify_surfdata/modify_smallville_with_dynurban.ncl
+! The file used as a template is:
+! /glade/p/cesm/cseg/inputdata/lnd/clm2/surfdata_map/landuse.timeseries_1x1_smallvilleIA_hist_78pfts_simyr1850-1855_c160127.nc
+! Key points are that urban area starts as 0, increases after the first year, then decreases after the second year.
+! PCT_CROP is also changed so that PCT_URBAN + PCT_CROP <= 100. (Here, PCT_CROP increases and decreases at the same time as PCT_URBAN in order to exercise the simultaneous increase or decrease of two landunits, but that isn't a critical part of this test.)
+! Note that the use of this file means that this testmod can only be used with the 1x1_smallvilleIA grid.
+flanduse_timeseries = '$DIN_LOC_ROOT/lnd/clm2/surfdata_map/landuse.timeseries_1x1_smallvilleIA_hist_78pfts_simyr1850-1855_dynUrban_c211206.nc'
diff --git a/doc/ChangeLog b/doc/ChangeLog
index 6edb2b6bf2..7df67169fe 100644
--- a/doc/ChangeLog
+++ b/doc/ChangeLog
@@ -1,4 +1,94 @@
===============================================================
+Tag name: ctsm5.1.dev069
+Originator(s): fang-bowen (Bowen Fang) / oleson (Keith Oleson,UCAR/TSS,303-497-1332)
+ / Face2sea (Lei Zhao) / keerzhang1 (Keer Zhang) / sacks (Bill Sacks)
+Date: Wed Dec 15 13:01:19 MST 2021
+One-line Summary: Implement dynamic (transient) urban capability
+
+Purpose and description of changes
+----------------------------------
+
+This implements the capability to run transient simulations with changes
+in urban density types. However, see Caveats for users section below.
+If transient urban is activated (do_transient_urban=.true.) and an
+appropriate landuse dataset is provided, the model will read in
+yearly PCT_URBAN for three density types from the landuse dataset and
+adjust other landunits accordingly.
+Water and energy are conserved for these transitions. Urban landunits
+do not account for carbon and nitrogen, however, see the dynamic urban
+design document (doc/design/dynamic_urban.rst) for a discussion of this.
+
+Significant changes to scientifically-supported configurations
+--------------------------------------------------------------
+
+Does this tag change answers significantly for any of the following physics configurations?
+(Details of any changes will be given in the "Answer changes" section below.)
+
+ [Put an [X] in the box for any configuration with significant answer changes.]
+
+[ ] clm5_1
+
+[ ] clm5_0
+
+[ ] ctsm5_0-nwp
+
+[ ] clm4_5
+
+
+Bugs fixed or introduced
+------------------------
+
+Issues fixed (include CTSM Issue #): #1445 (partially, work on mksurfdata and reducing
+computational cost is in progress)
+
+Notes of particular relevance for users
+---------------------------------------
+
+Caveats for users (e.g., need to interpolate initial conditions):
+Note that this capability is off by default because landuse datasets
+with transient urban on them are still being developed.
+Note also that if this capability is activated (e.g., for testing) then
+run_zero_weight_urban must be set to .true.
+
+Changes to CTSM's user interface (e.g., new/renamed XML or namelist variables):
+Added do_transient_urban namelist variable
+
+Changes to the datasets (e.g., parameter, surface or initial files):
+None.
+New datasets that include transient urban data will be provided in a future tag.
+
+Notes of particular relevance for developers:
+---------------------------------------------
+NOTE: Be sure to review the steps in README.CHECKLIST.master_tags as well as the coding style in the Developers Guide
+
+Changes to tests or testing:
+Added ERS_Lm25.1x1_smallvilleIA.IHistClm50BgcCropQianRs.cheyenne_gnu.clm-smallville_dynurban_monthly to aux_clm
+
+Testing summary:
+----------------
+
+ build-namelist tests (if CLMBuildNamelist.pm has changed):
+
+ cheyenne - PASS
+
+ regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing):
+
+ cheyenne ---- PASS
+ izumi ------- PASS
+
+Answer changes
+--------------
+
+Changes answers relative to baseline: NO
+
+Other details
+-------------
+
+Pull Requests that document the changes (include PR ids):
+https://github.com/ESCOMP/CTSM/pull/1546
+
+===============================================================
+===============================================================
Tag name: ctsm5.1.dev068
Originator(s): slevis (Samuel Levis,SLevis Consulting,303-665-1310), sacks (Bill Sacks)
Date: Mon Dec 13 15:42:07 MST 2021
diff --git a/doc/ChangeSum b/doc/ChangeSum
index d7eaf29969..b6172d6779 100644
--- a/doc/ChangeSum
+++ b/doc/ChangeSum
@@ -1,5 +1,6 @@
Tag Who Date Summary
============================================================================================================================
+ ctsm5.1.dev069 oleson 12/15/2021 Implement dynamic (transient) urban capability
ctsm5.1.dev068 multiple 12/13/2021 Adding fsurdat_modifier tool
ctsm5.1.dev067 jedwards 12/13/2021 NEON UI update, externals updates, small miscellanouse fixes
ctsm5.1.dev066 rgknox 12/04/2021 API change with FATES to enable running means inside fates, includes passing in of model timestep
diff --git a/doc/design/dynamic_urban.rst b/doc/design/dynamic_urban.rst
new file mode 100644
index 0000000000..0ca5d488f4
--- /dev/null
+++ b/doc/design/dynamic_urban.rst
@@ -0,0 +1,45 @@
+.. sectnum::
+
+.. contents::
+
+==================================
+ Overview of this design document
+==================================
+
+This documents some of the high-level design decisions made during implementation of
+dynamic urban landunits.
+
+============================================================================
+ The use of dzsoi_decomp for urban landunits to calculate totcolch4 in ch4Mod.F90
+============================================================================
+During the first test simulation for dynamic urban, we encountered a methane conservation
+error the first time PCT_URBAN changed. The dynamic adjustments for conc_ch4_sat_col and
+conc_ch4_unsat_col (the column_state_updater in subroutine DynamicColumnAdjustments within
+ch4Mod.F90) were distributing non-zero values for roof and walls for layers 1,nlevsoi.
+When the total column ch4 is summed over the soil layers (or in this case, urban layers), the
+summation is done over nlevsoi, not nlevurb, using dz. dz is 1.e36 for roof/wall layers
+that are greater than nlevurb, thus creating an imbalance.
+
+Rather than trying to keep the BGC variables physically meaningful in urban landunits,
+we will just pack these variables in a way that should conserve these variables, even if
+the values in each of the urban columns is somewhat nonsensical. Specifically: we'll take
+col%wtgcell at face value in urban columns in dynColumnStateUpdaterMod - i.e., for the sake
+of storing / conserving these BGC variables, we'll act as if that gives the true column
+weight on the grid cell. This way we'll end up storing all of the C & N from the vegetated
+column in the urban columns, and there shouldn't be any that is lost from the system. If that
+urban landunit later shrinks, the stored C & N should be restored symmetrically. It shouldn't
+really matter that it was stored in a non-physical way (e.g., with some C & N stored in urban
+walls), since the BGC variables are irrelevant over the urban areas and we just want to be able
+to restore the amount that was originally stored if an urban landunit grows and then later shrinks.
+But for this to work right, we need to treat the relevant BGC variables as having the same dz over
+all urban columns as over the soil column. Note that there already seems to be an implicit assumption
+that dz is the same for all columns in the dynamic column state updates, in that dz doesn't enter
+into the conservation equations. In terms of what needs to change, we think that the only relevant
+code is the code that sums up total C / N / CH4 for the sake of balance checks: these balance checks
+need to be consistent with the assumptions made in the conservation code. The C and N summations
+already use dzsoi_decomp, which is the same for all columns, so this is already what we want.
+The only thing that needs to change is the use of dz in totcolch4 in ch4Mod.F90: we've changed that to now use
+dzsoi_decomp over urban columns. (This begs the question of why this isn't already using
+dzsoi_decomp for consistency with the C & N code; we're not sure about this.)
+
+See issue #1445 for the original discussion on this topic.
diff --git a/src/biogeochem/ch4Mod.F90 b/src/biogeochem/ch4Mod.F90
index d9fbe1ae68..8190a97bdb 100644
--- a/src/biogeochem/ch4Mod.F90
+++ b/src/biogeochem/ch4Mod.F90
@@ -4371,7 +4371,9 @@ subroutine ch4_totcolch4(bounds, num_nolakec, filter_nolakec, num_lakec, filter_
! its original values
!
! !USES:
- use ch4varcon , only : allowlakeprod
+ use ch4varcon , only : allowlakeprod
+ use clm_varcon , only : dzsoi_decomp
+ use landunit_varcon , only : isturb_tbd, isturb_hd, isturb_md
!
! !ARGUMENTS:
type(bounds_type) , intent(in) :: bounds
@@ -4384,7 +4386,7 @@ subroutine ch4_totcolch4(bounds, num_nolakec, filter_nolakec, num_lakec, filter_
!
! !LOCAL VARIABLES:
integer :: fc, c
- integer :: j
+ integer :: j, l
character(len=*), parameter :: subname = 'ch4_totcolch4'
!-----------------------------------------------------------------------
@@ -4411,9 +4413,18 @@ subroutine ch4_totcolch4(bounds, num_nolakec, filter_nolakec, num_lakec, filter_
do j = 1, nlevsoi
do fc = 1, num_nolakec
c = filter_nolakec(fc)
- totcolch4(c) = totcolch4(c) + &
- (finundated(c)*conc_ch4_sat(c,j) + (1._r8-finundated(c))*conc_ch4_unsat(c,j)) * &
- dz(c,j)*catomw
+ l = col%landunit(c)
+ ! See doc/design/dynamic_urban.rst for an explanation of why we use dzsoi_decomp instead of dz for
+ ! urban landunits.
+ if (lun%itype(l) .eq. isturb_tbd .or. lun%itype(l) .eq. isturb_hd .or. lun%itype(l) .eq. isturb_md) then
+ totcolch4(c) = totcolch4(c) + &
+ (finundated(c)*conc_ch4_sat(c,j) + (1._r8-finundated(c))*conc_ch4_unsat(c,j)) * &
+ dzsoi_decomp(j)*catomw
+ else
+ totcolch4(c) = totcolch4(c) + &
+ (finundated(c)*conc_ch4_sat(c,j) + (1._r8-finundated(c))*conc_ch4_unsat(c,j)) * &
+ dz(c,j)*catomw
+ end if
! mol CH4 --> g C
end do
diff --git a/src/biogeophys/UrbanParamsType.F90 b/src/biogeophys/UrbanParamsType.F90
index 9a27601187..c490ad27cc 100644
--- a/src/biogeophys/UrbanParamsType.F90
+++ b/src/biogeophys/UrbanParamsType.F90
@@ -357,9 +357,8 @@ subroutine Init(this, bounds)
end if
end do
- ! Deallocate memory for urbinp datatype
-
- call UrbanInput(bounds%begg, bounds%endg, mode='finalize')
+ ! Note that we don't deallocate memory for urbinp datatype (call UrbanInput with
+ ! mode='finalize') because the arrays are needed for dynamic urban landunits.
end subroutine Init
diff --git a/src/dyn_subgrid/dynConsBiogeophysMod.F90 b/src/dyn_subgrid/dynConsBiogeophysMod.F90
index bcbcc97c4b..9ef474d401 100644
--- a/src/dyn_subgrid/dynConsBiogeophysMod.F90
+++ b/src/dyn_subgrid/dynConsBiogeophysMod.F90
@@ -382,9 +382,7 @@ subroutine set_glacier_baselines(bounds, num_icec, filter_icec, &
! all we really need are averages for the natural veg landunit. But since this
! subroutine is only called in initialization, code reuse/simplicity is more important
! than performance.
- !
- ! No thought has been given to c2l_scale_type here. This may need to be fixed if we
- ! ever start caring about urban averages in this routine.
+
call c2l(bounds = bounds, &
carr = vals_col(bounds%begc:bounds%endc), &
larr = vals_lun(bounds%begl:bounds%endl), &
diff --git a/src/dyn_subgrid/dynSubgridControlMod.F90 b/src/dyn_subgrid/dynSubgridControlMod.F90
index b4da85be73..c408f1a038 100644
--- a/src/dyn_subgrid/dynSubgridControlMod.F90
+++ b/src/dyn_subgrid/dynSubgridControlMod.F90
@@ -25,6 +25,7 @@ module dynSubgridControlMod
public :: get_do_transient_pfts ! return the value of the do_transient_pfts control flag
public :: get_do_transient_crops ! return the value of the do_transient_crops control flag
public :: get_do_transient_lakes ! return the value of the do_transient_lakes control flag
+ public :: get_do_transient_urban ! return the value of the do_transient_urban control flag
public :: run_has_transient_landcover ! returns true if any aspects of prescribed transient landcover are enabled
public :: get_do_harvest ! return the value of the do_harvest control flag
public :: get_reset_dynbal_baselines ! return the value of the reset_dynbal_baselines control flag
@@ -42,6 +43,7 @@ module dynSubgridControlMod
logical :: do_transient_pfts = .false. ! whether to apply transient natural PFTs from dataset
logical :: do_transient_crops = .false. ! whether to apply transient crops from dataset
logical :: do_transient_lakes = .false. ! whether to apply transient lakes from dataset
+ logical :: do_transient_urban = .false. ! whether to apply transient urban from dataset
logical :: do_harvest = .false. ! whether to apply harvest from dataset
logical :: reset_dynbal_baselines = .false. ! whether to reset baseline values of total column water and energy in the first step of the run
@@ -119,6 +121,7 @@ subroutine read_namelist( NLFilename )
logical :: do_transient_pfts
logical :: do_transient_crops
logical :: do_transient_lakes
+ logical :: do_transient_urban
logical :: do_harvest
logical :: reset_dynbal_baselines
logical :: for_testing_allow_non_annual_changes
@@ -135,6 +138,7 @@ subroutine read_namelist( NLFilename )
do_transient_pfts, &
do_transient_crops, &
do_transient_lakes, &
+ do_transient_urban, &
do_harvest, &
reset_dynbal_baselines, &
for_testing_allow_non_annual_changes, &
@@ -145,6 +149,7 @@ subroutine read_namelist( NLFilename )
do_transient_pfts = .false.
do_transient_crops = .false.
do_transient_lakes = .false.
+ do_transient_urban = .false.
do_harvest = .false.
reset_dynbal_baselines = .false.
for_testing_allow_non_annual_changes = .false.
@@ -170,6 +175,7 @@ subroutine read_namelist( NLFilename )
call shr_mpi_bcast (do_transient_pfts, mpicom)
call shr_mpi_bcast (do_transient_crops, mpicom)
call shr_mpi_bcast (do_transient_lakes, mpicom)
+ call shr_mpi_bcast (do_transient_urban, mpicom)
call shr_mpi_bcast (do_harvest, mpicom)
call shr_mpi_bcast (reset_dynbal_baselines, mpicom)
call shr_mpi_bcast (for_testing_allow_non_annual_changes, mpicom)
@@ -180,6 +186,7 @@ subroutine read_namelist( NLFilename )
do_transient_pfts = do_transient_pfts, &
do_transient_crops = do_transient_crops, &
do_transient_lakes = do_transient_lakes, &
+ do_transient_urban = do_transient_urban, &
do_harvest = do_harvest, &
reset_dynbal_baselines = reset_dynbal_baselines, &
for_testing_allow_non_annual_changes = for_testing_allow_non_annual_changes, &
@@ -230,6 +237,11 @@ subroutine check_namelist_consistency
write(iulog,*) 'a flanduse_timeseries file (currently flanduse_timeseries is blank)'
call endrun(msg=errMsg(sourcefile, __LINE__))
end if
+ if (dyn_subgrid_control_inst%do_transient_urban) then
+ write(iulog,*) 'ERROR: do_transient_urban can only be true if you are running with'
+ write(iulog,*) 'a flanduse_timeseries file (currently flanduse_timeseries is blank)'
+ call endrun(msg=errMsg(sourcefile, __LINE__))
+ end if
if (dyn_subgrid_control_inst%do_harvest) then
write(iulog,*) 'ERROR: do_harvest can only be true if you are running with'
write(iulog,*) 'a flanduse_timeseries file (currently flanduse_timeseries is blank)'
@@ -257,18 +269,19 @@ subroutine check_namelist_consistency
! areas in this respect.
if (dyn_subgrid_control_inst%do_transient_pfts .or. &
dyn_subgrid_control_inst%do_transient_crops .or. &
- dyn_subgrid_control_inst%do_transient_lakes) then
+ dyn_subgrid_control_inst%do_transient_lakes .or. &
+ dyn_subgrid_control_inst%do_transient_urban) then
if (collapse_urban) then
- write(iulog,*) 'ERROR: do_transient_pfts, do_transient_crops and do_transient_lakes are &
- incompatible with collapse_urban = .true.'
+ write(iulog,*) 'ERROR: do_transient_pfts, do_transient_crops, do_transient_lakes and&
+ do_transient_urban are incompatible with collapse_urban = .true.'
call endrun(msg=errMsg(sourcefile, __LINE__))
end if
if (n_dom_pfts > 0 .or. n_dom_landunits > 0 &
.or. toosmall_soil > 0._r8 .or. toosmall_crop > 0._r8 &
.or. toosmall_glacier > 0._r8 .or. toosmall_lake > 0._r8 &
.or. toosmall_wetland > 0._r8 .or. toosmall_urban > 0._r8) then
- write(iulog,*) 'ERROR: do_transient_pfts, do_transient_crops and do_transient_lakes are &
- incompatible with any of the following set to > 0: &
+ write(iulog,*) 'ERROR: do_transient_pfts, do_transient_crops and do_transient_lakes and &
+ do_transient_urban are incompatible with any of the following set to > 0: &
n_dom_pfts > 0, n_dom_landunits > 0, &
toosmall_soil > 0._r8, toosmall_crop > 0._r8, &
toosmall_glacier > 0._r8, toosmall_lake > 0._r8, &
@@ -346,6 +359,18 @@ logical function get_do_transient_lakes()
end function get_do_transient_lakes
+ !-----------------------------------------------------------------------
+ logical function get_do_transient_urban()
+ ! !DESCRIPTION:
+ ! Return the value of the do_transient_urban control flag
+ !-----------------------------------------------------------------------
+
+ SHR_ASSERT_FL(dyn_subgrid_control_inst%initialized, sourcefile, __LINE__)
+
+ get_do_transient_urban = dyn_subgrid_control_inst%do_transient_urban
+
+ end function get_do_transient_urban
+
!-----------------------------------------------------------------------
logical function run_has_transient_landcover()
! !DESCRIPTION:
@@ -354,7 +379,8 @@ logical function run_has_transient_landcover()
run_has_transient_landcover = &
(get_do_transient_pfts() .or. &
- get_do_transient_crops())
+ get_do_transient_crops() .or. &
+ get_do_transient_urban())
end function run_has_transient_landcover
!-----------------------------------------------------------------------
diff --git a/src/dyn_subgrid/dynSubgridDriverMod.F90 b/src/dyn_subgrid/dynSubgridDriverMod.F90
index a1063032bb..2a660498e4 100644
--- a/src/dyn_subgrid/dynSubgridDriverMod.F90
+++ b/src/dyn_subgrid/dynSubgridDriverMod.F90
@@ -12,7 +12,8 @@ module dynSubgridDriverMod
use decompMod , only : bounds_type, bounds_level_proc, bounds_level_clump
use decompMod , only : get_proc_clumps, get_clump_bounds
use dynSubgridControlMod , only : get_flanduse_timeseries
- use dynSubgridControlMod , only : get_do_transient_pfts, get_do_transient_crops, get_do_transient_lakes
+ use dynSubgridControlMod , only : get_do_transient_pfts, get_do_transient_crops, get_do_transient_lakes, &
+ get_do_transient_urban
use dynSubgridControlMod , only : get_do_harvest
use dynPriorWeightsMod , only : prior_weights_type
use dynPatchStateUpdaterMod , only : patch_state_updater_type
@@ -21,6 +22,7 @@ module dynSubgridDriverMod
use dyncropFileMod , only : dyncrop_init, dyncrop_interp
use dynHarvestMod , only : dynHarvest_init, dynHarvest_interp
use dynlakeFileMod , only : dynlake_init, dynlake_interp
+ use dynurbanFileMod , only : dynurban_init, dynurban_interp
use dynLandunitAreaMod , only : update_landunit_weights
use subgridWeightsMod , only : compute_higher_order_weights, set_subgrid_diagnostic_fields
use reweightMod , only : reweight_wrapup
@@ -128,6 +130,11 @@ subroutine dynSubgrid_init(bounds_proc, glc_behavior, crop_inst)
call dynlake_init(bounds_proc, dynlake_filename=get_flanduse_timeseries())
end if
+ ! Initialize stuff for prescribed transient urban
+ if (get_do_transient_urban()) then
+ call dynurban_init(bounds_proc, dynurban_filename=get_flanduse_timeseries())
+ end if
+
! ------------------------------------------------------------------------
! Set initial subgrid weights for aspects that are read from file. This is relevant
! for cold start and use_init_interp-based initialization.
@@ -145,6 +152,9 @@ subroutine dynSubgrid_init(bounds_proc, glc_behavior, crop_inst)
call dynlake_interp(bounds_proc)
end if
+ if (get_do_transient_urban()) then
+ call dynurban_interp(bounds_proc)
+ end if
! (We don't bother calling dynHarvest_interp, because the harvest information isn't
! needed until the run loop. Harvest has nothing to do with subgrid weights, and in
@@ -262,6 +272,10 @@ subroutine dynSubgrid_driver(bounds_proc,
if (get_do_transient_lakes()) then
call dynlake_interp(bounds_proc)
end if
+
+ if (get_do_transient_urban()) then
+ call dynurban_interp(bounds_proc)
+ end if
! ==========================================================================
! Do land cover change that does not require I/O
! ==========================================================================
diff --git a/src/dyn_subgrid/dynurbanFileMod.F90 b/src/dyn_subgrid/dynurbanFileMod.F90
new file mode 100644
index 0000000000..be2f60b052
--- /dev/null
+++ b/src/dyn_subgrid/dynurbanFileMod.F90
@@ -0,0 +1,143 @@
+module dynurbanFileMod
+
+ !---------------------------------------------------------------------------
+ ! !DESCRIPTION:
+ ! Handle reading of the dataset that specifies transient areas of the urban landunits
+ !
+ ! !USES:
+
+#include "shr_assert.h"
+ use shr_log_mod , only : errMsg => shr_log_errMsg
+ use shr_kind_mod , only : r8 => shr_kind_r8
+ use decompMod , only : bounds_type, bounds_level_proc
+ use dynFileMod , only : dyn_file_type
+ use dynVarTimeUninterpMod , only : dyn_var_time_uninterp_type
+ use clm_varctl , only : iulog
+ use clm_varcon , only : grlnd
+ use abortutils , only : endrun
+ use spmdMod , only : masterproc
+ use landunit_varcon , only : numurbl
+ use UrbanParamsType , only : CheckUrban
+
+ ! !PUBLIC MEMBER FUNCTIONS:
+ implicit none
+ private
+ save
+ public :: dynurban_init ! initialize information read from landuse.timeseries dataset
+ public :: dynurban_interp ! get landuse data for the current time step, if needed
+ !
+ ! ! PRIVATE TYPES
+ type(dyn_file_type), target :: dynurban_file ! information for the file containing transient urban data
+ type(dyn_var_time_uninterp_type) :: wturban ! weight of the urban landunits
+
+ ! Names of variables on file
+ character(len=*), parameter :: urban_varname = 'PCT_URBAN'
+
+ character(len=*), parameter, private :: sourcefile = &
+ __FILE__
+ !---------------------------------------------------------------------------
+
+contains
+
+ !-----------------------------------------------------------------------
+ subroutine dynurban_init(bounds, dynurban_filename)
+ !
+ ! !DESCRIPTION:
+ ! Initialize dataset containing transient urban info (position it to the right time
+ ! samples that bound the initial model date)
+ !
+ ! !USES:
+ use dynTimeInfoMod , only : YEAR_POSITION_START_OF_TIMESTEP
+ use ncdio_pio , only : check_dim_size
+ !
+ ! !ARGUMENTS:
+ type(bounds_type) , intent(in) :: bounds ! proc-level bounds
+ character(len=*) , intent(in) :: dynurban_filename ! name of file containing transient urban information
+ !
+ ! !LOCAL VARIABLES:
+ integer :: num_points ! number of spatial points
+ integer :: wturb_shape(2) ! shape of the wturb data
+
+ character(len=*), parameter :: subname = 'dynurban_init'
+ !-----------------------------------------------------------------------
+
+ SHR_ASSERT(bounds%level == bounds_level_proc, subname // ': argument must be PROC-level bounds')
+
+ if (masterproc) then
+ write(iulog,*) 'Attempting to read urban dynamic landuse data .....'
+ end if
+
+ ! Get the year from the START of the timestep; this way, we'll update urban areas
+ ! starting after the year boundary. This is consistent with the timing of glacier
+ ! updates, and will likely be consistent with the timing of urban updates determined
+ ! prognostically, if urban areas are ever determined prognostically rather than
+ ! prescribed ahead of time.
+ dynurban_file = dyn_file_type(dynurban_filename, YEAR_POSITION_START_OF_TIMESTEP)
+ call check_dim_size(dynurban_file, 'numurbl', numurbl)
+
+ ! read data PCT_URBAN
+ !
+ ! Note: if you want to change transient urban landunits so that they are interpolated, rather
+ ! than jumping to each year's value on Jan 1 of that year, simply change wturban
+ ! to be of type dyn_var_time_interp_type (rather than
+ ! dyn_var_time_uninterp_type), and change the following constructors to construct
+ ! variables of dyn_var_time_interp_type. That's all you need to do.
+ num_points = (bounds%endg - bounds%begg + 1)
+ wturb_shape = [num_points, numurbl]
+ wturban = dyn_var_time_uninterp_type( &
+ dyn_file = dynurban_file, varname=urban_varname, &
+ dim1name=grlnd, conversion_factor=100._r8, &
+ do_check_sums_equal_1=.false., data_shape=wturb_shape)
+
+
+ end subroutine dynurban_init
+
+
+ !-----------------------------------------------------------------------
+ subroutine dynurban_interp(bounds)
+ !
+ ! !DESCRIPTION:
+ ! Get urban cover for model time, when needed.
+ !
+ ! Sets lun%wtgcell for urban landunits.
+ !
+ ! Note that urban cover currently jumps to its new value at the start of the year.
+ ! However, as mentioned above, this behavior can be changed to time interpolation
+ ! simply by making wturban and wtcft dyn_var_time_interp_type variables rather than
+ ! dyn_var_time_uninterp_type.
+ !
+ ! !USES:
+ use landunit_varcon , only : isturb_tbd, isturb_hd, isturb_md
+ use subgridWeightsMod , only : set_landunit_weight
+ !
+ ! !ARGUMENTS:
+ type(bounds_type), intent(in) :: bounds ! proc-level bounds
+ !
+ ! !LOCAL VARIABLES:
+ integer :: g ! indices
+ real(r8), allocatable :: wturban_cur(:,:) ! current weight of the urban landunits
+
+ character(len=*), parameter :: subname = 'dynurban_interp'
+ !-----------------------------------------------------------------------
+
+ SHR_ASSERT(bounds%level == bounds_level_proc, subname // ': argument must be PROC-level bounds')
+
+ call dynurban_file%time_info%set_current_year()
+
+ ! Set new landunit areas
+ allocate(wturban_cur(bounds%begg:bounds%endg, numurbl))
+ call wturban%get_current_data(wturban_cur)
+ do g = bounds%begg, bounds%endg
+ call set_landunit_weight(g, isturb_tbd, wturban_cur(g,1))
+ call set_landunit_weight(g, isturb_hd, wturban_cur(g,2))
+ call set_landunit_weight(g, isturb_md, wturban_cur(g,3))
+ end do
+
+ ! Check that urban data is valid
+ call CheckUrban(bounds%begg, bounds%endg, wturban_cur(bounds%begg:bounds%endg,:), subname)
+
+ deallocate(wturban_cur)
+
+ end subroutine dynurban_interp
+
+end module dynurbanFileMod
\ No newline at end of file