diff --git a/.gitmodules b/.gitmodules
index 71aab550ff..73b0434577 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -27,8 +27,11 @@
#
[submodule "fates"]
path = src/fates
-url = https://github.com/NGEET/fates
-fxtag = sci.1.87.2_api.41.0.0
+#url = https://github.com/NGEET/fates
+url = https://github.com/ekluzek/fates
+#fxtag = sci.1.87.2_api.41.0.0
+#fxtag = add_file_to_cmake_unittest_list
+fxtag = fa4016d1b59ffdb509a7833da113a88872a8a56c
fxrequired = AlwaysRequired
# Standard Fork to compare to with "git fleximod test" to ensure personal forks aren't committed
fxDONOTUSEurl = https://github.com/NGEET/fates
diff --git a/bld/CLMBuildNamelist.pm b/bld/CLMBuildNamelist.pm
index 9562cca9bd..c88af453b5 100755
--- a/bld/CLMBuildNamelist.pm
+++ b/bld/CLMBuildNamelist.pm
@@ -5315,6 +5315,7 @@ sub write_output_files {
push @groups, "clm_canopy_inparm";
push @groups, "prigentroughness";
push @groups, "zendersoilerod";
+ push @groups, "for_testing_options";
if (remove_leading_and_trailing_quotes($nl->get_value('snow_cover_fraction_method')) eq 'SwensonLawrence2012') {
push @groups, "scf_swenson_lawrence_2012_inparm";
}
diff --git a/bld/namelist_files/namelist_definition_ctsm.xml b/bld/namelist_files/namelist_definition_ctsm.xml
index 69a243bd27..2b5b4f2e99 100644
--- a/bld/namelist_files/namelist_definition_ctsm.xml
+++ b/bld/namelist_files/namelist_definition_ctsm.xml
@@ -1259,12 +1259,38 @@ Whether to use subgrid fluxes for snow
Whether snow on the vegetation canopy affects the radiation/albedo calculations
+
+
+
+
+
+
+For testing whether to bypass the rest of the initialization after the self test driver is run
+
+
+
+For testing whether to bypass most of the run phase other than the clock advance
+
+
+
+Whether to exit early after the initialization self tests are run. This is typically only used in automated tests.
+
+
+ group="for_testing_options" >
Whether to run some tests of ncdio_pio as part of the model run. This is
typically only used in automated tests.
+
+Whether to run some tests of decompInit (to get the gridcell to MPI task decomposition) as part of the model run. This is
+typically only used in automated tests.
+
+
If true, allocate memory for and use a second crop grain pool. This is
diff --git a/cime_config/buildlib b/cime_config/buildlib
index a4b853924e..3ce5080dc4 100755
--- a/cime_config/buildlib
+++ b/cime_config/buildlib
@@ -135,6 +135,7 @@ def _main_func():
os.path.join(lnd_root, "src", "dyn_subgrid"),
os.path.join(lnd_root, "src", "init_interp"),
os.path.join(lnd_root, "src", "self_tests"),
+ os.path.join(lnd_root, "src", "unit_test_shr"),
os.path.join(lnd_root, "src", "fates"),
os.path.join(lnd_root, "src", "fates", "main"),
os.path.join(lnd_root, "src", "fates", "biogeophys"),
diff --git a/cime_config/testdefs/ExpectedTestFails.xml b/cime_config/testdefs/ExpectedTestFails.xml
index f8f05bf5f1..34c3729b18 100644
--- a/cime_config/testdefs/ExpectedTestFails.xml
+++ b/cime_config/testdefs/ExpectedTestFails.xml
@@ -381,13 +381,6 @@
-
-
- FAIL
- #3316
-
-
-
diff --git a/cime_config/testdefs/testmods_dirs/clm/for_testing_fastsetup_bypassrun/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/for_testing_fastsetup_bypassrun/user_nl_clm
index c2a2d14793..573df5c02e 100644
--- a/cime_config/testdefs/testmods_dirs/clm/for_testing_fastsetup_bypassrun/user_nl_clm
+++ b/cime_config/testdefs/testmods_dirs/clm/for_testing_fastsetup_bypassrun/user_nl_clm
@@ -1,3 +1,6 @@
+! Exit early and bypass the run phase
+for_testing_exit_after_self_tests = .true.
+
! Turn off history, restarts, and output
hist_empty_htapes = .true.
use_noio = .true.
diff --git a/cime_config/testdefs/testmods_dirs/clm/run_self_tests/shell_commands b/cime_config/testdefs/testmods_dirs/clm/run_self_tests/shell_commands
new file mode 100755
index 0000000000..9383f70de0
--- /dev/null
+++ b/cime_config/testdefs/testmods_dirs/clm/run_self_tests/shell_commands
@@ -0,0 +1,9 @@
+#!/bin/bash
+./xmlchange CLM_FORCE_COLDSTART="on"
+
+# We use this testmod in a _Ln1 test; this requires forcing the ROF coupling frequency to every time step
+./xmlchange ROF_NCPL=48
+
+# Restarts aren't allowed for these tests, and turn off CPL history
+./xmlchange REST_OPTION="never"
+./xmlchange HIST_OPTION="never"
diff --git a/cime_config/testdefs/testmods_dirs/clm/run_self_tests/user_nl_clm b/cime_config/testdefs/testmods_dirs/clm/run_self_tests/user_nl_clm
index 9e8e0fcd04..c1ac6a7174 100644
--- a/cime_config/testdefs/testmods_dirs/clm/run_self_tests/user_nl_clm
+++ b/cime_config/testdefs/testmods_dirs/clm/run_self_tests/user_nl_clm
@@ -1,5 +1,15 @@
+! Bypass as much of the init phase as can be done
+! Bypassing the run phase already was inherited from the for_testing_fastsetup_bypassrun testmod
+for_testing_bypass_init = .true.
+
+! Turn on some of the self tests
for_testing_run_ncdiopio_tests = .true.
+for_testing_run_decomp_init_tests = .true.
! Turn off history, restarts, and output
hist_empty_htapes = .true.
use_noio = .true.
+for_testing_run_decomp_init_tests = .true.
+
+! Exit initialization phase after the self tests
+for_testing_bypass_init = .true.
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 2682775ca5..ada073cba8 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -59,41 +59,29 @@ add_subdirectory(${CLM_ROOT}/src/main clm_main)
add_subdirectory(${CLM_ROOT}/src/init_interp clm_init_interp)
add_subdirectory(${CLM_ROOT}/src/self_tests clm_self_tests)
+# Add FATES source directories
+add_subdirectory(${CLM_ROOT}/src/fates/main fates_main)
+add_subdirectory(${CLM_ROOT}/src/fates/biogeochem fates_biogeochem)
+add_subdirectory(${CLM_ROOT}/src/fates/biogeophys fates_biogeophys)
+add_subdirectory(${CLM_ROOT}/src/fates/parteh fates_parteh)
+add_subdirectory(${CLM_ROOT}/src/fates/fire fates_fire)
+add_subdirectory(${CLM_ROOT}/src/fates/radiation fates_radiation)
+
# Add general unit test directories (stubbed out files, etc.)
add_subdirectory(unit_test_stubs)
add_subdirectory(unit_test_shr)
-# Remove some things from share_sources
-#
-# TODO: this should be moved into a general-purpose function in Sourcelist_utils.
-# Then each removal could be replaced with a single call, like:
-# remove_source_file(${share_sources} "shr_mpi_mod.F90")
-foreach (sourcefile ${share_sources})
- # Remove shr_mpi_mod from share_sources.
- # This is needed because we want to use the mock shr_mpi_mod in place of the real one
- string(REGEX MATCH "shr_mpi_mod.F90" match_found ${sourcefile})
- if(match_found)
- list(REMOVE_ITEM share_sources ${sourcefile})
- endif()
-
- # Remove shr_pio_mod from share_sources. This is needed to avoid an explicit dependency
- # on PIO. This removal is needed on some systems but not on others: the unit test build
- # works without this removal on a Mac with a pre-built PIO library, but failed (with
- # error message, "Cannot open module file 'pio.mod'") on a Mac without a pre-built PIO
- # (where ESMF was built with its internal PIO).
- string(REGEX MATCH "shr_pio_mod.F90" match_found ${sourcefile})
- if(match_found)
- list(REMOVE_ITEM share_sources ${sourcefile})
- endif()
-endforeach()
-
# Build libraries containing stuff needed for the unit tests.
# Eventually, these add_library calls should probably be distributed into the correct location, rather than being in this top-level CMakeLists.txt file.
add_library(csm_share ${share_sources} ${drv_sources_needed})
declare_generated_dependencies(csm_share "${share_genf90_sources}")
add_library(clm ${clm_sources})
+add_library(fates ${fates_sources})
declare_generated_dependencies(clm "${clm_genf90_sources}")
-add_dependencies(clm csm_share esmf)
+# Add explicit dependencies to lower level libraries, for each library
+add_dependencies(csm_share esmf)
+add_dependencies(clm fates csm_share esmf)
+add_dependencies(fates csm_share esmf)
# We need to look for header files here, in order to pick up shr_assert.h
include_directories(${CLM_ROOT}/share/include)
diff --git a/src/cpl/nuopc/lnd_comp_nuopc.F90 b/src/cpl/nuopc/lnd_comp_nuopc.F90
index 3db987f2fa..3e0fc3c2f7 100644
--- a/src/cpl/nuopc/lnd_comp_nuopc.F90
+++ b/src/cpl/nuopc/lnd_comp_nuopc.F90
@@ -49,6 +49,7 @@ module lnd_comp_nuopc
use lnd_import_export , only : advertise_fields, realize_fields, import_fields, export_fields
use lnd_comp_shr , only : mesh, model_meshfile, model_clock
use perf_mod , only : t_startf, t_stopf, t_barrierf
+ use SelfTestDriver , only : for_testing_exit_after_self_tests
implicit none
private ! except
@@ -351,6 +352,8 @@ subroutine InitializeRealize(gcomp, importState, exportState, clock, rc)
use lnd_set_decomp_and_domain , only : lnd_set_decomp_and_domain_from_readmesh
use lnd_set_decomp_and_domain , only : lnd_set_mesh_for_single_column
use lnd_set_decomp_and_domain , only : lnd_set_decomp_and_domain_for_single_column
+ use SelfTestDriver , only : for_testing_bypass_init_after_self_tests, &
+ for_testing_exit_after_self_tests
! input/output variables
type(ESMF_GridComp) :: gcomp
@@ -500,6 +503,12 @@ subroutine InitializeRealize(gcomp, importState, exportState, clock, rc)
else
single_column = .false.
end if
+ !if ( for_testing_exit_after_self_tests) then
+ ! *******************
+ ! *** RETURN HERE ***
+ ! *******************
+ !RETURN
+ !end if
!----------------------------------------------------------------------------
! Reset shr logging to my log file
@@ -676,14 +685,19 @@ subroutine InitializeRealize(gcomp, importState, exportState, clock, rc)
call t_startf('clm_init2')
call initialize2(ni, nj, currtime)
call t_stopf('clm_init2')
+ if (for_testing_exit_after_self_tests) then
+ RETURN
+ end if
!--------------------------------
! Create land export state
!--------------------------------
+ if ( .not. for_testing_bypass_init_after_self_tests() ) then
call get_proc_bounds(bounds)
call export_fields(gcomp, bounds, glc_present, rof_prognostic, &
water_inst%waterlnd2atmbulk_inst, lnd2atm_inst, lnd2glc_inst, rc)
if (ChkErr(rc,__LINE__,u_FILE_u)) return
+ end if
! Set scalars in export state
call State_SetScalar(dble(ldomain%ni), flds_scalar_index_nx, exportState, &
@@ -731,6 +745,7 @@ subroutine ModelAdvance(gcomp, rc)
use clm_instMod , only : water_inst, atm2lnd_inst, glc2lnd_inst, lnd2atm_inst, lnd2glc_inst
use decompMod , only : bounds_type, get_proc_bounds
use clm_driver , only : clm_drv
+ use SelfTestDriver, only : for_testing_bypass_init_after_self_tests
! input/output variables
type(ESMF_GridComp) :: gcomp
@@ -786,6 +801,9 @@ subroutine ModelAdvance(gcomp, rc)
if (single_column .and. .not. scol_valid) then
RETURN
end if
+ !if (for_testing_exit_after_self_tests) then
+ ! RETURN
+ !end if
!$ call omp_set_num_threads(nthrds)
@@ -818,16 +836,20 @@ subroutine ModelAdvance(gcomp, rc)
flds_scalar_index_nextsw_cday, nextsw_cday, &
flds_scalar_name, flds_scalar_num, rc)
- ! Get proc bounds
- call get_proc_bounds(bounds)
-
!--------------------------------
! Unpack import state
!--------------------------------
+ if ( .not. for_testing_bypass_init_after_self_tests() ) then
+ ! Get proc bounds for both import and export
+ call get_proc_bounds(bounds)
+
+ call t_startf ('lc_lnd_import')
call import_fields( gcomp, bounds, glc_present, rof_prognostic, &
atm2lnd_inst, glc2lnd_inst, water_inst%wateratm2lndbulk_inst, rc )
if (ChkErr(rc,__LINE__,u_FILE_u)) return
+ call t_stopf ('lc_lnd_import')
+ end if
!--------------------------------
! Run model
@@ -917,9 +939,13 @@ subroutine ModelAdvance(gcomp, rc)
! Pack export state
!--------------------------------
+ if ( .not. for_testing_bypass_init_after_self_tests() ) then
+ call t_startf ('lc_lnd_export')
call export_fields(gcomp, bounds, glc_present, rof_prognostic, &
water_inst%waterlnd2atmbulk_inst, lnd2atm_inst, lnd2glc_inst, rc)
if (ChkErr(rc,__LINE__,u_FILE_u)) return
+ call t_stopf ('lc_lnd_export')
+ end if
!--------------------------------
! Advance ctsm time step
@@ -1009,6 +1035,7 @@ subroutine ModelSetRunClock(gcomp, rc)
rc = ESMF_SUCCESS
call ESMF_LogWrite(subname//' called', ESMF_LOGMSG_INFO)
if (.not. scol_valid) return
+ !if (for_testing_exit_after_self_tests) return
! query the Component for its clocks
call NUOPC_ModelGet(gcomp, driverClock=dclock, modelClock=mclock, rc=rc)
@@ -1320,6 +1347,9 @@ subroutine CheckImport(gcomp, rc)
if (single_column .and. .not. scol_valid) then
RETURN
end if
+ !if (for_testing_exit_after_self_tests) then
+ !RETURN
+ !end if
! The remander of this should be equivalent to the NUOPC internal routine
! from NUOPC_ModeBase.F90
diff --git a/src/fates b/src/fates
index c1dfc21c50..fa4016d1b5 160000
--- a/src/fates
+++ b/src/fates
@@ -1 +1 @@
-Subproject commit c1dfc21c505b5c8b29d4592b7d4a5e058239f6fb
+Subproject commit fa4016d1b59ffdb509a7833da113a88872a8a56c
diff --git a/src/main/CMakeLists.txt b/src/main/CMakeLists.txt
index fc324efeb9..3884e0ba36 100644
--- a/src/main/CMakeLists.txt
+++ b/src/main/CMakeLists.txt
@@ -19,6 +19,7 @@ list(APPEND clm_sources
clm_varsur.F90
column_varcon.F90
decompMod.F90
+ decompInitMod.F90
filterColMod.F90
FireMethodType.F90
glc2lndMod.F90
@@ -30,6 +31,7 @@ list(APPEND clm_sources
ncdio_utils.F90
organicFileMod.F90
paramUtilMod.F90
+ subgridMod.F90
subgridAveMod.F90
subgridWeightsMod.F90
surfrdUtilsMod.F90
diff --git a/src/main/clm_driver.F90 b/src/main/clm_driver.F90
index 3a47a7eed3..279154f52c 100644
--- a/src/main/clm_driver.F90
+++ b/src/main/clm_driver.F90
@@ -85,6 +85,7 @@ module clm_driver
use clm_instMod
use SoilMoistureStreamMod , only : PrescribedSoilMoistureInterp, PrescribedSoilMoistureAdvance
use SoilBiogeochemDecompCascadeConType , only : no_soil_decomp, decomp_method
+ use SelfTestDriver , only : for_testing_bypass_run_except_clock_advance
!
! !PUBLIC TYPES:
implicit none
@@ -165,6 +166,7 @@ subroutine clm_drv(doalb, nextsw_cday, declinp1, declin, rstwr, nlend, rdate, ro
! CalcIrrigationNeeded. Simply declaring this variable makes the ICE go away.
real(r8), allocatable :: dummy1_to_make_pgi_happy(:)
!-----------------------------------------------------------------------
+ if ( for_testing_bypass_run_except_clock_advance() ) return
! Determine processor bounds and clumps for this processor
@@ -1576,6 +1578,8 @@ subroutine clm_drv_init(bounds, &
integer :: fp, fc ! filter indices
!-----------------------------------------------------------------------
+ if ( for_testing_bypass_run_except_clock_advance() ) return
+
associate( &
snl => col%snl , & ! Input: [integer (:) ] number of snow layers
@@ -1657,6 +1661,7 @@ subroutine clm_drv_patch2col (bounds, &
! !LOCAL VARIABLES:
integer :: c,fc ! indices
! -----------------------------------------------------------------
+ if ( for_testing_bypass_run_except_clock_advance() ) return
! Note: lake points are excluded from many of the following
! averages. For some fields, this is because the field doesn't
@@ -1752,6 +1757,8 @@ subroutine write_diagnostic (bounds, nstep, lnd2atm_inst)
integer :: status(MPI_STATUS_SIZE) ! mpi status
!------------------------------------------------------------------------
+ if ( for_testing_bypass_run_except_clock_advance() ) return
+
call get_proc_global(ng=numg)
if (masterproc) then
diff --git a/src/main/clm_initializeMod.F90 b/src/main/clm_initializeMod.F90
index 1c26b55cfd..0ffa7737a8 100644
--- a/src/main/clm_initializeMod.F90
+++ b/src/main/clm_initializeMod.F90
@@ -30,7 +30,7 @@ module clm_initializeMod
use CLMFatesInterfaceMod , only : CLMFatesGlobals1,CLMFatesGlobals2
use CLMFatesInterfaceMod , only : CLMFatesTimesteps
use dynSubgridControlMod , only : dynSubgridControl_init, get_reset_dynbal_baselines
- use SelfTestDriver , only : self_test_driver
+ use SelfTestDriver , only : self_test_driver, for_testing_bypass_init_after_self_tests
use SoilMoistureStreamMod , only : PrescribedSoilMoistureInit
use clm_instMod
!
@@ -67,6 +67,7 @@ subroutine initialize1(dtime)
use SoilBiogeochemDecompCascadeConType , only : decomp_cascade_par_init
use CropReprPoolsMod , only: crop_repr_pools_init
use HillslopeHydrologyMod, only: hillslope_properties_init
+ use SelfTestDriver , only: self_test_readnml
!
! !ARGUMENTS
integer, intent(in) :: dtime ! model time step (seconds)
@@ -102,6 +103,8 @@ subroutine initialize1(dtime)
call surfrd_get_num_patches(fsurdat, actual_maxsoil_patches, actual_numpft, actual_numcft)
call surfrd_get_nlevurb(fsurdat, actual_nlevurb)
+ call self_test_readnml( NLFilename )
+
! If fates is on, we override actual_maxsoil_patches. FATES dictates the
! number of patches per column. We still use numcft from the surface
! file though...
@@ -182,6 +185,7 @@ subroutine initialize2(ni,nj, currtime)
use FATESFireFactoryMod , only : scalar_lightning
use dynFATESLandUseChangeMod , only : dynFatesLandUseInit
use HillslopeHydrologyMod , only : InitHillslope
+ use SelfTestDriver , only : for_testing_bypass_init_after_self_tests
!
! !ARGUMENTS
integer, intent(in) :: ni, nj ! global grid sizes
@@ -333,6 +337,7 @@ subroutine initialize2(ni,nj, currtime)
! Run any requested self-tests
call self_test_driver(bounds_proc)
+ if ( .not. for_testing_bypass_init_after_self_tests() )then
! Deallocate surface grid dynamic memory for variables that aren't needed elsewhere.
! Some things are kept until the end of initialize2; urban_valid is kept through the
! end of the run for error checking, pct_urban_max is kept through the end of the run
@@ -349,8 +354,9 @@ subroutine initialize2(ni,nj, currtime)
allocate(nutrient_competition_method, &
source=create_nutrient_competition_method(bounds_proc))
call readParameters(photosyns_inst)
-
+ end if ! End of bypass
+ ! Self test skipping should still do the time manager initialization
! Initialize time manager
if (nsrest == nsrStartup) then
call timemgr_init()
@@ -376,6 +382,7 @@ subroutine initialize2(ni,nj, currtime)
call t_stopf('clm_init2_part2')
call t_startf('clm_init2_part3')
+ if ( .not. for_testing_bypass_init_after_self_tests() )then
! Initialize Balance checking (after time-manager)
call BalanceCheckInit()
@@ -423,7 +430,9 @@ subroutine initialize2(ni,nj, currtime)
call SnowAge_init( ) ! SNICAR aging parameters:
! Print history field info to standard out
- call hist_printflds()
+ if ( .not. use_noio )then
+ call hist_printflds()
+ end if
! Initializate dynamic subgrid weights (for prescribed transient Patches, CNDV
! and/or dynamic landunits); note that these will be overwritten in a restart run
@@ -508,6 +517,7 @@ subroutine initialize2(ni,nj, currtime)
if (nsrest == nsrContinue ) then
call htapes_fieldlist()
end if
+ end if ! End of bypass
! Read restart/initial info
is_cold_start = .false.
@@ -601,6 +611,8 @@ subroutine initialize2(ni,nj, currtime)
call t_stopf('clm_init2_init_interp')
end if
+ if ( .not. for_testing_bypass_init_after_self_tests() )then
+
! If requested, reset dynbal baselines
! This needs to happen after reading the restart file (including after reading the
! interpolated restart file, if applicable).
@@ -759,6 +771,7 @@ subroutine initialize2(ni,nj, currtime)
water_inst%waterdiagnosticbulk_inst, canopystate_inst, &
soilstate_inst, soilbiogeochem_carbonflux_inst)
end if
+ end if ! end of bypass
! topo_glc_mec was allocated in initialize1, but needed to be kept around through
! initialize2 because it is used to initialize other variables; now it can be deallocated
diff --git a/src/main/controlMod.F90 b/src/main/controlMod.F90
index 089503dc8b..4a956e33b2 100644
--- a/src/main/controlMod.F90
+++ b/src/main/controlMod.F90
@@ -210,7 +210,7 @@ subroutine control_init(dtime)
snow_thermal_cond_method, snow_thermal_cond_glc_method, &
snow_thermal_cond_lake_method, snow_cover_fraction_method, &
irrigate, run_zero_weight_urban, all_active, &
- crop_fsat_equals_zero, for_testing_run_ncdiopio_tests, &
+ crop_fsat_equals_zero, &
for_testing_use_second_grain_pool, for_testing_use_repr_structure_pool, &
for_testing_no_crop_seed_replenishment, &
z0param_method, use_z0m_snowmelt
@@ -766,9 +766,6 @@ subroutine control_spmd()
! Crop saturated excess runoff
call mpi_bcast(crop_fsat_equals_zero, 1, MPI_LOGICAL, 0, mpicom, ier)
- ! Whether to run tests of ncdio_pio
- call mpi_bcast(for_testing_run_ncdiopio_tests, 1, MPI_LOGICAL, 0, mpicom, ier)
-
! Various flags used for testing infrastructure for having multiple crop reproductive pools
call mpi_bcast(for_testing_use_second_grain_pool, 1, MPI_LOGICAL, 0, mpicom, ier)
call mpi_bcast(for_testing_use_repr_structure_pool, 1, MPI_LOGICAL, 0, mpicom, ier)
diff --git a/src/self_tests/Assertions.F90.in b/src/self_tests/Assertions.F90.in
index 2a4c8cccc6..4a86929a8a 100644
--- a/src/self_tests/Assertions.F90.in
+++ b/src/self_tests/Assertions.F90.in
@@ -17,6 +17,12 @@ module Assertions
public :: assert_equal
interface assert_equal
+ !TYPE double,int,logical
+ module procedure assert_equal_0d_{TYPE}
+
+ !TYPE text
+ module procedure assert_equal_0d_{TYPE}
+
!TYPE double,int,logical
module procedure assert_equal_1d_{TYPE}
@@ -30,6 +36,8 @@ module Assertions
interface vals_are_equal
!TYPE double,int,logical
module procedure vals_are_equal_{TYPE}
+ !TYPE text
+ module procedure vals_are_equal_{TYPE}
end interface vals_are_equal
contains
@@ -75,6 +83,60 @@ contains
end subroutine assert_equal_1d_{TYPE}
+ !-----------------------------------------------------------------------
+ !TYPE double,int,logical
+ subroutine assert_equal_0d_{TYPE}(expected, actual, msg, abs_tol)
+ !
+ ! !DESCRIPTION:
+ ! Assert scalar values are equal
+ !
+ ! !ARGUMENTS:
+ {VTYPE}, intent(in) :: expected
+ {VTYPE}, intent(in) :: actual
+ character(len=*), intent(in) :: msg
+
+ ! absolute tolerance; if not specified, require exact equality; ignored for logicals
+ real(r8), intent(in), optional :: abs_tol
+ !
+ ! !LOCAL VARIABLES:
+ integer :: i
+
+ character(len=*), parameter :: subname = 'assert_equal_0d_{TYPE}'
+ !-----------------------------------------------------------------------
+
+ if (.not. vals_are_equal(actual, expected, abs_tol)) then
+ write(iulog,*) 'ERROR in assert_equal: ', msg
+ write(iulog,*) 'Actual : ', actual
+ write(iulog,*) 'Expected: ', expected
+ call endrun('ERROR in assert_equal')
+ end if
+
+ end subroutine assert_equal_0d_{TYPE}
+
+ !-----------------------------------------------------------------------
+ !TYPE text
+ subroutine assert_equal_0d_{TYPE}(expected, actual, msg)
+ !
+ ! !DESCRIPTION:
+ ! Assert scalar values are equal
+ !
+ ! !ARGUMENTS:
+ {VTYPE}, intent(in) :: expected
+ {VTYPE}, intent(in) :: actual
+ character(len=*), intent(in) :: msg
+ !
+ ! !LOCAL VARIABLES:
+ !-----------------------------------------------------------------------
+
+ if (.not. vals_are_equal(actual, expected)) then
+ write(iulog,*) 'ERROR in assert_equal: ', msg
+ write(iulog,*) 'Actual : ', actual
+ write(iulog,*) 'Expected: ', expected
+ call endrun('ERROR in assert_equal')
+ end if
+
+ end subroutine assert_equal_0d_{TYPE}
+
!-----------------------------------------------------------------------
!TYPE double,int,logical
subroutine assert_equal_2d_{TYPE}(expected, actual, msg, abs_tol)
@@ -198,4 +260,23 @@ contains
end function vals_are_equal_{TYPE}
+ !-----------------------------------------------------------------------
+ !TYPE text
+ function vals_are_equal_{TYPE}(actual, expected) result(vals_equal)
+ !
+ ! !DESCRIPTION:
+ ! Returns true if actual is the same as expected, false otherwise
+ !
+ ! !ARGUMENTS:
+ logical :: vals_equal ! function result
+ {VTYPE}, intent(in) :: actual
+ {VTYPE}, intent(in) :: expected
+ !
+ ! !LOCAL VARIABLES:
+ !-----------------------------------------------------------------------
+
+ vals_equal = actual == expected
+
+ end function vals_are_equal_{TYPE}
+
end module Assertions
diff --git a/src/self_tests/CMakeLists.txt b/src/self_tests/CMakeLists.txt
index 3a454aab2b..fc78041c6b 100644
--- a/src/self_tests/CMakeLists.txt
+++ b/src/self_tests/CMakeLists.txt
@@ -3,6 +3,9 @@
set(genf90_files
Assertions.F90.in
+)
+list(APPEND clm_sources
+ TestDecompInit.F90
)
process_genf90_source_list("${genf90_files}" ${CMAKE_CURRENT_BINARY_DIR} clm_genf90_sources)
diff --git a/src/self_tests/SelfTestDriver.F90 b/src/self_tests/SelfTestDriver.F90
index d109a27827..eb391c5ced 100644
--- a/src/self_tests/SelfTestDriver.F90
+++ b/src/self_tests/SelfTestDriver.F90
@@ -6,9 +6,10 @@ module SelfTestDriver
!
! See the README file in this directory for a high-level overview of these self-tests.
- use clm_varctl, only : for_testing_run_ncdiopio_tests
use decompMod, only : bounds_type
use TestNcdioPio, only : test_ncdio_pio
+ use abortutils, only : endrun
+ use clm_varctl, only : iulog
implicit none
private
@@ -16,7 +17,17 @@ module SelfTestDriver
! Public routines
- public :: self_test_driver
+ public :: self_test_driver ! Run the self-tests asked for
+ public :: self_test_readnml ! Read in the general self testing options for overall code flow
+ public :: for_testing_bypass_init_after_self_tests ! For testing bypass the rest of the initialization after the self test driver was run
+ public :: for_testing_bypass_run_except_clock_advance ! For testing bypass most of the run phase other than the clock advance
+
+ ! Private module data
+ logical :: for_testing_bypass_init ! For testing bypass the initialization phase after the self-test driver
+ logical :: for_testing_bypass_run ! For testing bypass most of the run phase except the time advance
+ logical :: for_testing_run_ncdiopio_tests ! true => run tests of ncdio_pio
+ logical :: for_testing_run_decomp_init_tests ! true => run tests of decompInit
+ logical, public :: for_testing_exit_after_self_tests ! true => exit after running self tests
character(len=*), parameter, private :: sourcefile = &
__FILE__
@@ -32,18 +43,144 @@ subroutine self_test_driver(bounds)
! This subroutine should be called all the time, but each set of self tests is only
! run if the appropriate flag is set.
!
+ ! !USES:
+ use decompMod, only : bounds_type
+ use TestNcdioPio, only : test_ncdio_pio
+ use ESMF, only : ESMF_LogWrite, ESMF_LOGMSG_INFO, ESMF_Finalize
+ use shr_sys_mod, only : shr_sys_flush
+ use spmdMod, only : masterproc
! !ARGUMENTS:
type(bounds_type), intent(in) :: bounds
!
! !LOCAL VARIABLES:
character(len=*), parameter :: subname = 'self_test_driver'
+ integer :: ntests = 0
!-----------------------------------------------------------------------
+ ! Just return if no tests are asked for...
+ if (.not. for_testing_run_ncdiopio_tests .and .not. for_testing_run_decomp_init_tests) then
+ RETURN
+ end if
+
+ if ( masterproc ) then
+ write(iulog,*) '-------------------------------'
+ write(iulog,*) '----- Starting self tests -----'
+ write(iulog,*) '-------------------------------'
+ call shr_sys_flush(iulog)
+ end if
if (for_testing_run_ncdiopio_tests) then
+ ntests = ntests + 1
call test_ncdio_pio(bounds)
end if
+ if (for_testing_run_decomp_init_tests) then
+ ntests = ntests + 1
+ call test_decomp_init()
+ end if
+ if ( masterproc ) then
+ write(iulog,*) '-------------------------------'
+ write(iulog,*) '----- Ending self tests -------'
+ write(iulog,*) '-------------------------------'
+ call shr_sys_flush(iulog)
+ end if
+ if (for_testing_exit_after_self_tests) then
+ ! Print out some messaging if we are exiting after self tests.
+ if ( masterproc ) then
+ if ( ntests == 0 )then
+ write(iulog,*) 'WARNING: You are exiting after self tests were run -- but no self tests were run.'
+ else
+ write(iulog,*) 'Exiting after running ', ntests, ' suite of self tests.'
+ end if
+ call shr_sys_flush(iulog)
+ end if
+ end if
end subroutine self_test_driver
+ !-----------------------------------------------------------------------
+ subroutine self_test_readnml(NLFileName)
+ !
+ ! !DESCRIPTION:
+ ! Namelist read for the self-test driver. This includes bypass options
+ ! that will be used in other parts of the code to bypass bits of the code
+ ! for testing purposes.
+ !
+ ! !USES:
+ use shr_nl_mod , only : shr_nl_find_group_name
+ use spmdMod, only : masterproc, mpicom
+ use shr_mpi_mod, only : shr_mpi_bcast
+ use clm_varctl, only : iulog
+ !
+ ! !ARGUMENTS:
+ character(len=*), intent(in) :: NLFilename ! Namelist filename
+ !
+ ! !LOCAL VARIABLES:
+ integer :: ierr ! error code
+ integer :: unitn ! unit for namelist file
+
+ ! Namelist name: this has to be matched with the name in the read stqatement
+ character(len=*), parameter :: nmlname = 'for_testing_options'
+ !-----------------------------------------------------------------------
+
+ namelist /for_testing_options/ for_testing_bypass_init, for_testing_bypass_run, &
+ for_testing_run_ncdiopio_tests, for_testing_run_decomp_init_tests, &
+ for_testing_exit_after_self_tests
+
+ ! Initialize options to default values, in case they are not specified in
+ ! the namelist
+
+ if (masterproc) then
+ write(iulog,*) 'Read in '//nmlname//' namelist'
+ open(newunit=unitn, status='old', file=NLFilename)
+ call shr_nl_find_group_name(unitn, nmlname, status=ierr)
+ if (ierr == 0) then
+ read(unit=unitn, nml=for_testing_options, iostat=ierr)
+ if (ierr /= 0) then
+ call endrun(msg="ERROR reading "//nmlname//"namelist", file=sourcefile, line=__LINE__)
+ end if
+ else
+ call endrun(msg="ERROR finding "//nmlname//"namelist", file=sourcefile, line=__LINE__)
+ end if
+ close(unitn)
+ end if
+
+ call shr_mpi_bcast (for_testing_bypass_init, mpicom)
+ call shr_mpi_bcast (for_testing_bypass_run, mpicom)
+ call shr_mpi_bcast(for_testing_run_ncdiopio_tests, mpicom)
+ call shr_mpi_bcast(for_testing_run_decomp_init_tests, mpicom)
+ call shr_mpi_bcast(for_testing_exit_after_self_tests, mpicom)
+
+ if (masterproc) then
+ write(iulog,*) ' '
+ write(iulog,*) nmlname//' settings:'
+ write(iulog,nml=for_testing_options)
+ write(iulog,*) ' '
+ end if
+
+ end subroutine self_test_readnml
+
+ !-----------------------------------------------------------------------
+
+ logical function for_testing_bypass_init_after_self_tests()
+ ! Determine if should exit initialization early after having run the self tests
+ if ( for_testing_bypass_init ) then
+ for_testing_bypass_init_after_self_tests = .true.
+ else
+ for_testing_bypass_init_after_self_tests = .false.
+ end if
+ end function for_testing_bypass_init_after_self_tests
+
+ !-----------------------------------------------------------------------
+
+ logical function for_testing_bypass_run_except_clock_advance()
+ ! Determine if should skip most of the run phase other than the clock advance
+ if ( for_testing_bypass_init ) then
+ for_testing_bypass_run_except_clock_advance = .true.
+ else
+ for_testing_bypass_run_except_clock_advance = .false.
+ end if
+ end function for_testing_bypass_run_except_clock_advance
+
+ !-----------------------------------------------------------------------
+
end module SelfTestDriver
diff --git a/src/self_tests/TestDecompInit.F90 b/src/self_tests/TestDecompInit.F90
new file mode 100644
index 0000000000..80cd089067
--- /dev/null
+++ b/src/self_tests/TestDecompInit.F90
@@ -0,0 +1,264 @@
+module TestDecompInit
+
+ ! ------------------------------------------------------------------------
+ ! !DESCRIPTION:
+ ! This module contains tests of decomp_init
+
+#include "shr_assert.h"
+ use shr_kind_mod, only : r8 => shr_kind_r8, CX => shr_kind_cx
+ use Assertions, only : assert_equal
+ use clm_varctl, only : iulog
+ use abortutils, only : endrun
+ use spmdMod, only : masterproc, npes, iam
+ use decompInitMod, only : decompInit_lnd, clump_pproc, decompInit_clumps
+ use decompMod
+ use glcBehaviorMod, only : glc_behavior_type
+
+ implicit none
+ private
+ save
+
+ ! Public routines
+
+ public :: test_decomp_init
+ public :: setup
+ public :: clean
+
+ ! Module data used in various tests
+
+ ! Make the size of the test grid 384 so that it can be divided by 128 or 48
+ ! for the number of tasks per node on Derecho or Izumi.
+ integer, public, parameter :: ni = 16, nj = 24
+ integer, public :: amask(ni*nj)
+
+ integer :: default_npes
+ integer :: default_clump_pproc
+
+ type(glc_behavior_type), target, public :: glc_behavior
+
+ character(len=*), parameter, private :: sourcefile = &
+ __FILE__
+
+contains
+
+ !-----------------------------------------------------------------------
+ subroutine test_decomp_init()
+ !
+ ! !DESCRIPTION:
+ ! Drive tests of decomp_init
+ !
+ ! NOTE(wjs, 2020-10-15) Currently, endrun is called when any test assertion fails. I
+ ! thought about changing this so that, instead, a counter is incremented for each
+ ! failure, then at the end of the testing (in the higher-level self-test driver),
+ ! endrun is called if this counter is greater than 0. The benefit of this is that we'd
+ ! see all test failures, not just the first failure. To do that, we'd need to change
+ ! the assertions here to increment a counter rather than aborting. However, I'm not
+ ! spending the time to make this change for now because (1) I'm not sure how much
+ ! value we'd get from it; (2) even if we made that change, it's still very possible
+ ! for test code to abort for reasons other than assertions, if something goes wrong
+ ! inside decomp_init or pio; and (3) some tests here are dependent on earlier tests (for
+ ! example, the reads depend on the writes having worked), so a failure in an early
+ ! phase could really muck things up for later testing phases. Migrating to a
+ ! pFUnit-based unit test would solve this problem, since each pFUnit test is
+ ! independent, though would prevent us from being able to have dependent tests the
+ ! way we do here (where reads depend on earlier writes), for better or for worse.
+ !
+ ! !USERS:
+ use decompInitMod, only : decompInit_clumps, decompInit_glcp
+ use domainMod, only : ldomain
+ ! !ARGUMENTS:
+ !
+ ! !LOCAL VARIABLES:
+ integer, allocatable :: model_amask(:)
+ !-----------------------------------------------------------------------
+
+ default_npes = npes
+ default_clump_pproc = clump_pproc
+ call write_to_log('start_test_decomp_init')
+
+ call write_to_log('test_check_nclumps')
+ call test_check_nclumps()
+ call write_to_log('test_decompInit_lnd_check_sizes')
+ call test_decompInit_lnd_check_sizes()
+ call write_to_log('test_decompInit_clump_gcell_info_correct')
+ call test_decompInit_clump_gcell_info_correct()
+
+ !
+ ! THIS DESCRIBES WHAT WOULD NEED TO BE DONE TO CONTINUE A SIMULATION AFTER THIS:
+ ! SINCE THE CODE EXITS AFTER THE SELF TESTS THIS IS NOT NEEDED:
+ ! Call the decompInit initialization series a last time so that decompMod data can still be used
+ !
+ !allocate( model_amask(ldomain%ni*ldomain%nj) )
+ !model_amask(:) = 1
+ !call decompInit_lnd( ldomain%ni, ldomain%nj, model_amask )
+ !call decompInit_clumps(ldomain%ni, ldomain%nj, glc_behavior)
+ !call decompInit_glcp(ldomain%ni, ldomain%nj, glc_behavior)
+ !deallocate( model_amask )
+ ! END DESCRIBE:
+
+ end subroutine test_decomp_init
+
+ !-----------------------------------------------------------------------
+ subroutine setup()
+ use clm_varctl, only : nsegspc
+
+ clump_pproc = default_clump_pproc
+ nsegspc = 20
+ npes = default_npes
+ amask(:) = 1 ! Set all to land
+
+ end subroutine setup
+
+ !-----------------------------------------------------------------------
+ subroutine test_decompInit_lnd_check_sizes()
+ use decompMod, only : get_proc_bounds
+ type(bounds_type) :: bounds
+
+ integer :: expected_endg, expected_numg
+
+ call setup()
+ expected_numg = ni*nj
+ if ( expected_numg < npes )then
+ call endrun( msg="npes is too large for this test", file=sourcefile, line=__LINE__ )
+ end if
+ if ( modulo( expected_numg, npes ) /= 0 )then
+ call endrun( msg="npes does not evenly divide into numg so this test will not work", file=sourcefile, line=__LINE__ )
+ end if
+ expected_endg = ni*nj / npes
+ amask(:) = 1 ! Set all to land
+ call decompInit_lnd( ni, nj, amask )
+ call get_proc_bounds(bounds)
+ call assert_equal( bounds%begg, 1, msg='begg is not as expected' )
+ call assert_equal( bounds%endg, expected_endg, msg='endg is not as expected' )
+ call clean()
+ end subroutine test_decompInit_lnd_check_sizes
+
+ !-----------------------------------------------------------------------
+ subroutine test_check_nclumps()
+ integer :: expected_nclumps
+
+ call setup()
+ expected_nclumps = npes / clump_pproc
+ call assert_equal(expected=expected_nclumps, actual=nclumps, &
+ msg='nclumps are not as expected')
+ call clean()
+ end subroutine test_check_nclumps
+
+!-----------------------------------------------------------------------
+ subroutine test_decompMod_get_clump_bounds_correct()
+ ! Some testing for get_clump_bounds
+ use decompMod, only : get_clump_bounds, bounds_type
+ use unittestSimpleSubgridSetupsMod, only : setup_ncells_single_veg_patch
+ use unittestSubgridMod, only : unittest_subgrid_teardown
+ use pftconMod, only : noveg
+ type(bounds_type) :: bounds
+ integer :: expected_begg, expected_endg, expected_numg, gcell_per_task
+ integer :: iclump
+
+ call setup()
+ ! Now setup a singple grid that's just the full test with every point a single baresoil patch
+ call setup_ncells_single_veg_patch( ncells=ni*nj, pft_type=noveg )
+ clump_pproc = 1 ! Ensure we are just doing this for one clump per proc for now
+ expected_numg = ni*nj
+ if ( expected_numg < npes )then
+ call endrun( msg="npes is too large for this test", file=sourcefile, line=__LINE__ )
+ end if
+ if ( modulo( expected_numg, npes ) /= 0 )then
+ call endrun( msg="npes does not evenly divide into numg so this test will not work", file=sourcefile, line=__LINE__ )
+ end if
+ gcell_per_task = expected_numg / npes
+ expected_begg = gcell_per_task * iam + 1
+ expected_endg = expected_begg + gcell_per_task
+ amask(:) = 1 ! Set all to land
+ call decompInit_lnd( ni, nj, amask )
+ call decompInit_clumps( ni, nj, glc_behavior )
+ iclump = 1 ! Clump is just 1 since there's only one clump per task
+ call get_clump_bounds(iclump, bounds)
+ call assert_equal( bounds%begg, expected_begg, msg='begg is not as expected' )
+ call assert_equal( bounds%endg, expected_endg, msg='endg is not as expected' )
+ ! Other subgrtid level information will be the same -- since there's only one landunit, column, and patch per gridcell
+ call assert_equal( bounds%begl, expected_begg, msg='begl is not as expected' )
+ call assert_equal( bounds%endl, expected_endg, msg='endl is not as expected' )
+ call assert_equal( bounds%begc, expected_begg, msg='begc is not as expected' )
+ call assert_equal( bounds%endc, expected_endg, msg='endc is not as expected' )
+ call assert_equal( bounds%begp, expected_begg, msg='begp is not as expected' )
+ call assert_equal( bounds%endp, expected_endg, msg='endp is not as expected' )
+ call unittest_subgrid_teardown( )
+ call clean()
+ end subroutine test_decompMod_get_clump_bounds_correct
+
+ !-----------------------------------------------------------------------
+ subroutine test_decompInit_clump_gcell_info_correct()
+ ! Some testing for get_clump_bounds
+ use decompMod, only : clumps
+ use decompMod, only : get_proc_bounds
+ type(bounds_type) :: bounds
+ integer :: expected_gcells, iclump, g, beg_global_index, gcell_per_task
+ integer :: expected_begg, expected_endg, lc
+
+ call setup()
+ expected_gcells = ni*nj
+ if ( expected_gcells < npes )then
+ call endrun( msg="npes is too large for this test", file=sourcefile, line=__LINE__ )
+ end if
+ if ( modulo( expected_gcells, npes ) /= 0 )then
+ call endrun( msg="npes does not evenly divide into gcell so this test will not work", file=sourcefile, line=__LINE__ )
+ end if
+ gcell_per_task = expected_gcells / npes
+ expected_begg = gcell_per_task * iam + 1
+ expected_endg = expected_begg + gcell_per_task
+ amask(:) = 1 ! Set all to land
+ call decompInit_lnd( ni, nj, amask )
+ ! When clump_pproc is one clumps will be the same as PE
+ if ( clump_pproc == 1 ) then
+ call assert_equal( nclumps, npes, msg='nclumps should match number of processors when clump_pproc is 1' )
+ else
+ call assert_equal( nclumps/clump_pproc, npes, msg='nclumps divided by clump_pproc should match number of processors when clump_pproc > 1' )
+ end if
+ ! Just test over the local clumps
+ do lc = 1, clump_pproc
+ iclump = procinfo%cid(lc)
+ call assert_equal( clumps(iclump)%owner, iam, msg='clumps owner is not correct' )
+ call assert_equal( clumps(iclump)%ncells, gcell_per_task, msg='clumps ncells is not correct' )
+ end do
+ call clean()
+ end subroutine test_decompInit_clump_gcell_info_correct
+
+ !-----------------------------------------------------------------------
+ subroutine write_to_log(msg)
+ !
+ ! !DESCRIPTION:
+ ! Write a message to the log file, just from the masterproc
+ !
+ use shr_sys_mod, only : shr_sys_flush
+ ! !ARGUMENTS:
+ character(len=*), intent(in) :: msg
+ !
+ ! !LOCAL VARIABLES:
+
+ character(len=*), parameter :: subname = 'write_to_log'
+ !-----------------------------------------------------------------------
+
+ if (masterproc) then
+ write(iulog,'(a)') msg
+ call shr_sys_flush(iulog) ! Flush the I/O buffers always
+ end if
+
+ end subroutine write_to_log
+
+ !-----------------------------------------------------------------------
+ subroutine clean
+ !
+ ! !DESCRIPTION:
+ ! Do end-of-testing cleanup after each test
+ !
+ ! !ARGUMENTS:
+ !
+ ! !LOCAL VARIABLES:
+ !-----------------------------------------------------------------------
+ call decompmod_clean()
+
+ end subroutine clean
+
+
+end module TestDecompInit
diff --git a/src/self_tests/test/CMakeLists.txt b/src/self_tests/test/CMakeLists.txt
index d616310bcd..5e58bc6043 100644
--- a/src/self_tests/test/CMakeLists.txt
+++ b/src/self_tests/test/CMakeLists.txt
@@ -1 +1,2 @@
add_subdirectory(assertions_test)
+add_subdirectory(decomp_init_error_test)
diff --git a/src/self_tests/test/decomp_init_error_test/CMakeLists.txt b/src/self_tests/test/decomp_init_error_test/CMakeLists.txt
new file mode 100644
index 0000000000..caedcd9c61
--- /dev/null
+++ b/src/self_tests/test/decomp_init_error_test/CMakeLists.txt
@@ -0,0 +1,8 @@
+set (pfunit_sources
+ test_decomp_init_errors.pf)
+
+add_pfunit_ctest(test_decomp_init_error
+ TEST_SOURCES "${pfunit_sources}"
+ LINK_LIBRARIES clm csm_share esmf fates
+ EXTRA_FINALIZE unittest_finalize_esmf
+ EXTRA_USE unittestInitializeAndFinalize)
diff --git a/src/self_tests/test/decomp_init_error_test/test_decomp_init_errors.pf b/src/self_tests/test/decomp_init_error_test/test_decomp_init_errors.pf
new file mode 100644
index 0000000000..54be8e327c
--- /dev/null
+++ b/src/self_tests/test/decomp_init_error_test/test_decomp_init_errors.pf
@@ -0,0 +1,81 @@
+module test_decomp_init_errors
+
+ ! Tests of decompInitMod error handling
+
+ use funit
+ use shr_kind_mod, only : r8 => shr_kind_r8, CX => shr_kind_cx
+ use unittestUtils, only : endrun_msg
+ use clm_varctl, only : iulog
+ use spmdMod, only : masterproc, npes, iam
+ use decompInitMod, only : decompInit_lnd, clump_pproc
+ use TestDecompInit, only : ni, nj, amask
+
+ implicit none
+
+ @TestCase
+ type, extends(TestCase) :: TestDecompInitErrors
+ contains
+ procedure :: setUp
+ procedure :: tearDown
+ end type TestDecompInitErrors
+
+contains
+
+ subroutine setUp(this)
+ use TestDecompInit, only : DecompInitTestSetup => setup
+ class(TestDecompInitErrors), intent(inout) :: this
+
+ call DecompInitTestSetup()
+
+ end subroutine setUp
+
+ subroutine tearDown(this)
+ use TestDecompInit, only : clean
+ class(TestDecompInitErrors), intent(inout) :: this
+
+ call clean()
+
+ end subroutine tearDown
+
+ @Test
+ subroutine test_decompInit_lnd_abort_on_bad_clump_pproc(this)
+ class(TestDecompInitErrors), intent(inout) :: this
+ character(len=:), allocatable :: expected_msg
+
+ clump_pproc = 0
+ call decompInit_lnd( ni, nj, amask )
+ expected_msg = endrun_msg( &
+ 'clump_pproc must be greater than 0')
+ @assertExceptionRaised(expected_msg)
+
+ end subroutine test_decompInit_lnd_abort_on_bad_clump_pproc
+
+ @Test
+ subroutine test_decompInit_lnd_abort_when_npes_too_large(this)
+ class(TestDecompInitErrors), intent(inout) :: this
+ character(len=:), allocatable :: expected_msg
+
+ npes = ni*nj + 1
+
+ amask(:) = 1 ! Set all to land
+ call decompInit_lnd( ni, nj, amask )
+ expected_msg = endrun_msg( &
+ 'Number of processes exceeds number of land grid cells')
+ @assertExceptionRaised(expected_msg)
+ end subroutine test_decompInit_lnd_abort_when_npes_too_large
+
+ @Test
+ subroutine test_decompInit_lnd_abort_on_too_small_nsegspc(this)
+ use clm_varctl, only : nsegspc
+ class(TestDecompInitErrors), intent(inout) :: this
+ character(len=:), allocatable :: expected_msg
+
+ amask(:) = 1 ! Set all to land
+ nsegspc = 0
+ call decompInit_lnd( ni, nj, amask )
+ expected_msg = endrun_msg( &
+ 'Number of segments per clump (nsegspc) is less than 1 and can NOT be')
+ @assertExceptionRaised(expected_msg)
+ end subroutine test_decompInit_lnd_abort_on_too_small_nsegspc
+
+end module test_decomp_init_errors
diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt
index 04ad683517..9038b6dbca 100644
--- a/src/utils/CMakeLists.txt
+++ b/src/utils/CMakeLists.txt
@@ -22,6 +22,7 @@ list(APPEND clm_sources
SparseMatrixMultiplyMod.F90
IssueFixedMetadataHandler.F90
NumericsMod.F90
+ spmdMod.F90
)
sourcelist_to_parent(clm_sources)