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