diff --git a/doc/ChangeLog b/doc/ChangeLog index 034a0278c9..55b07fbe05 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,4 +1,67 @@ =============================================================== +Tag name: ctsm5.4.011 +Originator(s): slevis (Samuel Levis,UCAR/TSS,303-665-1310) +Date: Thu Jan 22 04:01:52 PM MST 2026 +One-line Summary: Merge b4b-dev to master + +Purpose and description of changes +---------------------------------- + Change some _U and _R history fields to be on by default PR #3667 by Keith Oleson + Update to cime version that fixes the check_input_data --download issue PR #3647 by Erik Kluzek; #3647 updated to cime6.1.145, while updating b4b-dev to master in this PR gets us to cime6.1.146. + Initial fixes to generate_gdd20_baseline PR #3543 + Decomp mod unittest PR #3699 + +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.] + +[ ] clm6_0 + +[ ] clm5_0 + +[ ] ctsm5_0-nwp + +[ ] clm4_5 + + +Bugs fixed +---------- +List of CTSM issues fixed (include CTSM Issue # and description) [one per line]: + No issues were opened for the PRs listed above. + +Testing summary: +---------------- + + [PASS means all tests PASS; OK means tests PASS other than expected fails.] + + build-namelist tests (if CLMBuildNamelist.pm has changed): + + derecho - OK + + python testing (if python code has changed; see instructions in python/README.md; document testing done): + + derecho - OK + + regular tests (aux_clm: https://github.com/ESCOMP/CTSM/wiki/System-Testing-Guide#pre-merge-system-testing): + + derecho ----- OK + izumi ------- OK + +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/3709 + +=============================================================== +=============================================================== Tag name: ctsm5.4.010 Originator(s): erik (Erik Kluzek,UCAR/TSS,303-497-1326) Date: Wed Jan 21 11:49:59 AM MST 2026 diff --git a/doc/ChangeSum b/doc/ChangeSum index 0c778259da..d97c686a6d 100644 --- a/doc/ChangeSum +++ b/doc/ChangeSum @@ -1,5 +1,6 @@ Tag Who Date Summary ============================================================================================================================ + ctsm5.4.011 slevis 01/22/2026 Merge b4b-dev to master ctsm5.4.010 erik 01/21/2026 Update cime to version that changes answers for ERI tests ctsm5.4.009 olyson 01/19/2026 Dewpoint Temperature check for bare ground ctsm5.4.008 swensosc 01/12/2026 Add a correction for oversaturated soil layers in SoilWaterMovement (erik) diff --git a/python/ctsm/crop_calendars/check_rx_obeyed.py b/python/ctsm/crop_calendars/check_rx_obeyed.py index c8c5410faf..68a37fb146 100644 --- a/python/ctsm/crop_calendars/check_rx_obeyed.py +++ b/python/ctsm/crop_calendars/check_rx_obeyed.py @@ -144,7 +144,7 @@ def check_rx_obeyed( continue ds_thisveg = dates_ds.isel(patch=thisveg_patches) - vegtype_int = utils.vegtype_str2int(vegtype_str)[0] + vegtype_int = utils.vegtype_str2int(vegtype_str) rx_da = rx_ds[f"gs1_{vegtype_int}"] rx_array = rx_da.values[ ds_thisveg.patches1d_jxy.values.astype(int) - 1, diff --git a/python/ctsm/crop_calendars/cropcal_utils.py b/python/ctsm/crop_calendars/cropcal_utils.py index c7e8b6ac52..4f2929de37 100644 --- a/python/ctsm/crop_calendars/cropcal_utils.py +++ b/python/ctsm/crop_calendars/cropcal_utils.py @@ -265,6 +265,8 @@ def vegtype_str2int(vegtype_str, vegtype_mainlist=None): convert_to_ndarray = not isinstance(vegtype_str, np.ndarray) if convert_to_ndarray: vegtype_str = np.array(vegtype_str) + was_0d = vegtype_str.ndim == 0 + vegtype_str = np.atleast_1d(vegtype_str) if isinstance(vegtype_mainlist, xr.Dataset): vegtype_mainlist = vegtype_mainlist.vegtype_str.values @@ -289,6 +291,10 @@ def vegtype_str2int(vegtype_str, vegtype_mainlist=None): indices[np.where(vegtype_str == vegtype_str_2)] = vegtype_mainlist.index(vegtype_str_2) if convert_to_ndarray: indices = [int(x) for x in indices] + + if was_0d: + indices = indices[0] + return indices diff --git a/python/ctsm/crop_calendars/generate_gdd20_baseline.py b/python/ctsm/crop_calendars/generate_gdd20_baseline.py index ee942eba21..fa42eb58b4 100644 --- a/python/ctsm/crop_calendars/generate_gdd20_baseline.py +++ b/python/ctsm/crop_calendars/generate_gdd20_baseline.py @@ -109,22 +109,7 @@ def _parse_args(): if not os.path.exists(filename): raise FileNotFoundError(f"Input file not found: {filename}") - # Process time slice - # Assumes CESM behavior where data for e.g. 1987 is saved as 1988-01-01. - # It would be more robust, accounting for upcoming behavior (where timestamp for a year is the - # middle of that year), to do slice("YEAR1-01-03", "YEARN-01-02"), but that's not compatible - # with ctsm_pylib as of the version using python 3.7.9. See safer_timeslice() in cropcal_utils. - if args.first_year is not None: - date_1 = f"{args.first_year+1}-01-01" - else: - date_1 = "0000-01-01" - if args.last_year is not None: - date_n = f"{args.last_year+1}-01-01" - else: - date_n = "9999-12-31" - time_slice = slice(date_1, date_n) - - return args, time_slice + return args def _get_cft_list(crop_list): @@ -182,7 +167,7 @@ def _get_gddn_for_cft(cft_str, variable): def _get_output_varname(cft_str): - cft_int = utils.vegtype_str2int(cft_str)[0] + cft_int = utils.vegtype_str2int(cft_str) return f"gdd20bl_{cft_int}" @@ -232,7 +217,23 @@ def setup_output_dataset(input_files, author, variable, year_args, ds_in): return ds_out -def generate_gdd20_baseline(input_files, output_file, author, time_slice, variable, year_args): +def _get_time_slice(year_args): + """ + Based on years from input arguments, return a time slice for selecting from dataset + """ + first_year = year_args[0] + last_year = year_args[1] + date_1 = f"{first_year}-01-01" + date_n = f"{last_year}-12-31" + if first_year is None: + date_1 = "0000-01-01" + if last_year is None: + date_n = "9999-12-31" + time_slice = slice(date_1, date_n) + return time_slice + + +def generate_gdd20_baseline(input_files, output_file, author, variable, year_args): """ Generate stream_fldFileName_gdd20_baseline file from CTSM outputs """ @@ -252,6 +253,9 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice, variab input_files = list(set(input_files)) input_files.sort() + # Process time slice + time_slice = _get_time_slice(year_args) + # Import history files and ensure they have lat/lon dims ds_in = import_ds(input_files, my_vars=var_list_in + GRIDDING_VAR_LIST, time_slice=time_slice) if not all(x in ds_in.dims for x in ["lat", "lon"]): @@ -275,7 +279,7 @@ def generate_gdd20_baseline(input_files, output_file, author, time_slice, variab # Process all crops encoding_dict = {} for cft_str in MGDCROP_LIST: - cft_int = utils.vegtype_str2int(cft_str)[0] + cft_int = utils.vegtype_str2int(cft_str) print(f"{cft_str} ({cft_int})") # Which GDDN history variable does this crop use? E.g., GDD0, GDD10 @@ -323,12 +327,11 @@ def main(): """ main() function for calling generate_gdd20_baseline.py from command line. """ - args, time_slice = _parse_args() + args = _parse_args() generate_gdd20_baseline( args.input_files, args.output_file, args.author, - time_slice, args.variable, [args.first_year, args.last_year], ) diff --git a/python/ctsm/crop_calendars/generate_gdds_functions.py b/python/ctsm/crop_calendars/generate_gdds_functions.py index be724659dd..ec60a85beb 100644 --- a/python/ctsm/crop_calendars/generate_gdds_functions.py +++ b/python/ctsm/crop_calendars/generate_gdds_functions.py @@ -591,7 +591,7 @@ def import_and_process_1yr( log(logger, f"SKIPPING {vegtype_str}") continue - vegtype_int = utils.vegtype_str2int(vegtype_str)[0] + vegtype_int = utils.vegtype_str2int(vegtype_str) this_crop_full_patchlist = list(xr_flexsel(h2_ds, vegtype=vegtype_str).patch.values) # Get time series for each patch of this type @@ -1152,7 +1152,7 @@ def make_figures( raise RuntimeError(f"If mapping {vegtype_str}, you must provide land use dataset") else: vegtypes_str = [x for x in incl_vegtypes_str if vegtype_str.lower() in x] - vegtypes_int = [utils.vegtype_str2int(x)[0] for x in vegtypes_str] + vegtypes_int = [utils.vegtype_str2int(x) for x in vegtypes_str] # Crop fraction map (for masking and weighting) if lu_ds: diff --git a/python/ctsm/crop_calendars/grid_one_variable.py b/python/ctsm/crop_calendars/grid_one_variable.py index d7c7126e54..c06ed50b16 100644 --- a/python/ctsm/crop_calendars/grid_one_variable.py +++ b/python/ctsm/crop_calendars/grid_one_variable.py @@ -87,6 +87,10 @@ def create_filled_array(this_ds, fill_value, thisvar_da, new_dims): """ Create a Numpy array to be filled with gridded data """ + + if fill_value is None: + fill_value = np.nan + dim_size_list = [] for dim in new_dims: if dim == "ivt_str": @@ -97,10 +101,7 @@ def create_filled_array(this_ds, fill_value, thisvar_da, new_dims): dim_size = this_ds.sizes[dim] dim_size_list = dim_size_list + [dim_size] thisvar_gridded = np.empty(dim_size_list) - if fill_value: - thisvar_gridded[:] = fill_value - else: - thisvar_gridded[:] = np.NaN + thisvar_gridded[:] = fill_value return thisvar_gridded @@ -160,7 +161,7 @@ def grid_one_variable(this_ds, var, fill_value=None, **kwargs): # Get DataArrays needed for gridding thisvar_da, vt_da, spatial_unit, ixy_da, jxy_da = get_ixy_jxy_das(this_ds, var) - if not fill_value and "_FillValue" in thisvar_da.attrs: + if fill_value is None and "_FillValue" in thisvar_da.attrs: fill_value = thisvar_da.attrs["_FillValue"] # Renumber vt_da to work as indices on new ivt dimension, if needed. diff --git a/python/ctsm/test/test_unit_cropcal_utils.py b/python/ctsm/test/test_unit_cropcal_utils.py new file mode 100755 index 0000000000..edbb81bd7b --- /dev/null +++ b/python/ctsm/test/test_unit_cropcal_utils.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 + +"""Unit tests for cropcal_utils.py""" + +import unittest + +from ctsm import unit_testing +from ctsm.crop_calendars import cropcal_utils as ccu + +# Allow names that pylint doesn't like, because otherwise I find it hard +# to make readable unit test names +# pylint: disable=invalid-name + + +class TestCropCalUtils(unittest.TestCase): + """Tests of cropcal_utils.py""" + + def setUp(self): + self.vegtype_mainlist = ["crop_1", "crop_2", "crop_3"] + + def test_vegtype_str2int_1string(self): + """ + Tests vegtype_str2int() for a single string. Result should be an int. + """ + result = ccu.vegtype_str2int("crop_1", vegtype_mainlist=self.vegtype_mainlist) + self.assertEqual(result, 0) + + def test_vegtype_str2int_2strings(self): + """ + Tests vegtype_str2int() for two strings. result should be a list of ints. + """ + result = ccu.vegtype_str2int(["crop_1", "crop_3"], vegtype_mainlist=self.vegtype_mainlist) + self.assertListEqual(result, [0, 2]) + + +if __name__ == "__main__": + unit_testing.setup_for_tests() + unittest.main() diff --git a/python/ctsm/test/test_unit_grid_one_variable.py b/python/ctsm/test/test_unit_grid_one_variable.py new file mode 100755 index 0000000000..400b4ede7e --- /dev/null +++ b/python/ctsm/test/test_unit_grid_one_variable.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 + +""" +Unit tests for grid_one_variable +""" + +import unittest + +import numpy as np +import xarray as xr + +from ctsm import unit_testing +from ctsm.crop_calendars import grid_one_variable as g1v + +# Allow test names that pylint doesn't like; otherwise hard to make them +# readable +# pylint: disable=invalid-name + +# pylint: disable=protected-access + +## Too many instant variables as part of the class (too many self. in the SetUp) +# pylint: disable=too-many-instance-attributes + + +class TestCreateFilledArray(unittest.TestCase): + """Unit tests for create_filled_array""" + + def setUp(self): + # Set up this_ds, which will provide us with sizes of dimensions in most cases + lat_vals = [55.0, 56.0, 57.0] + lat_da = xr.DataArray( + data=lat_vals, + dims=["lat"], + coords={"lat": lat_vals}, + ) + lon_vals = [255.0, 256.0, 257.0] + lon_da = xr.DataArray( + data=lon_vals, + dims=["lon"], + coords={"lon": lon_vals}, + ) + self.this_ds = xr.Dataset( + data_vars={ + "lat": lat_da, + "lon": lon_da, + } + ) + + def test_create_filled_array_fillNone(self): + """ + Test create_filled_array() with fill_value None: Should be filled with NaN + """ + + fill_value = None + thisvar_da_dummy = xr.DataArray() + new_dims = ["lat", "lon"] + + result = g1v.create_filled_array(self.this_ds, fill_value, thisvar_da_dummy, new_dims) + + self.assertTrue(np.all(np.isnan(result))) + + def test_create_filled_array_fill1(self): + """ + Test create_filled_array() with fill_value 1: Should be filled with 1 + """ + + fill_value = 1.0 + thisvar_da_dummy = xr.DataArray() + new_dims = ["lat", "lon"] + + result = g1v.create_filled_array(self.this_ds, fill_value, thisvar_da_dummy, new_dims) + + self.assertTrue(np.all(result == fill_value)) + + def test_create_filled_array_fill0(self): + """ + Test create_filled_array() with fill_value 0: Should be filled with 0 + """ + + fill_value = 0.0 + thisvar_da_dummy = xr.DataArray() + new_dims = ["lat", "lon"] + + result = g1v.create_filled_array(self.this_ds, fill_value, thisvar_da_dummy, new_dims) + + self.assertTrue(np.all(result == fill_value)) + + +if __name__ == "__main__": + unit_testing.setup_for_tests() + unittest.main() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bc925b7dd1..67a8b32308 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -13,18 +13,20 @@ project(clm_tests Fortran C) include(CIME_utils) - # find needed external packages # NetCDF is required -- because PIO and NetCDF are required by the standard default ESMF libraries find_package(NetCDF 4.7.4 REQUIRED Fortran) # The following - for finding ESMF - is copied from the share CMakeLists.txt -if (DEFINED ENV{ESMF_ROOT}) +if(DEFINED ENV{ESMF_ROOT}) list(APPEND CMAKE_MODULE_PATH $ENV{ESMF_ROOT}/cmake) endif() + find_package(ESMF REQUIRED) + # This adds include directories needed for ESMF set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${ESMF_F90COMPILEPATHS} ") + # This (which is *not* done in the share CMakeLists.txt) adds all directories and # libraries needed when linking ESMF, including any dependencies of ESMF. (But note that # this does *not* include the "-lesmf" itself). In particular, note that this includes any @@ -43,12 +45,12 @@ add_subdirectory(${SRCROOT}/share/src csm_share) add_subdirectory(${SRCROOT}/share/unit_test_stubs/util csm_share_stubs) # Add files needed from CMEPS -list ( APPEND drv_sources_needed +list(APPEND drv_sources_needed ${SRCROOT}/components/cmeps/cesm/nuopc_cap_share/glc_elevclass_mod.F90 ${SRCROOT}/components/cmeps/cesm/nuopc_cap_share/shr_dust_emis_mod.F90 ${SRCROOT}/components/cmeps/cesm/nuopc_cap_share/shr_expr_parser_mod.F90 ${SRCROOT}/components/cmeps/cesm/nuopc_cap_share/shr_fire_emis_mod.F90 - ) +) # Add CLM source directories add_subdirectory(${CLM_ROOT}/src/utils clm_utils) @@ -60,6 +62,18 @@ 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 +# Adding FATES directories is commented out -- because of the problem in: +# https://github.com/ESCOMP/CTSM/issues/3704 +# 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) +# NOTE: Need to add the fates library and dependencies below +# ### End add of FATES directories + # Add general unit test directories (stubbed out files, etc.) add_subdirectory(unit_test_stubs) add_subdirectory(unit_test_shr) @@ -69,10 +83,11 @@ add_subdirectory(unit_test_shr) # 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}) +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() @@ -83,6 +98,7 @@ foreach (sourcefile ${share_sources}) # 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() @@ -101,11 +117,11 @@ include_directories(${SRCROOT}/share/include) # Tell cmake to look for libraries & mod files here, because this is where we built libraries include_directories(${CMAKE_CURRENT_BINARY_DIR}) -include_directories (${NETCDF}/include) +include_directories(${NETCDF}/include) # Directories and libraries to include in the link step link_directories(${CMAKE_CURRENT_BINARY_DIR}) -link_libraries( netcdf esmf ) +link_libraries(netcdf esmf) # Add the test directories # Note: it's possible that these could be added by each source directory that @@ -125,4 +141,3 @@ add_subdirectory(${CLM_ROOT}/src/self_tests/test clm_self_tests_test) # Add driver unit test directories # (these should be moved to the appropriate submodule) add_subdirectory(${CLM_ROOT}/src/drv_test drv_test) - diff --git a/src/biogeophys/EnergyFluxType.F90 b/src/biogeophys/EnergyFluxType.F90 index 6a31293fa3..655a00d4d3 100644 --- a/src/biogeophys/EnergyFluxType.F90 +++ b/src/biogeophys/EnergyFluxType.F90 @@ -340,7 +340,7 @@ subroutine InitHistory(this, bounds, is_simple_buildtemp, is_prog_buildtemp) this%eflx_snomelt_u_col(begc:endc) = spval call hist_addfld1d (fname='FSM_U', units='W/m^2', & avgflag='A', long_name='Urban snow melt heat flux', & - ptr_col=this%eflx_snomelt_u_col, c2l_scale_type='urbanf', set_nourb=spval, default='inactive') + ptr_col=this%eflx_snomelt_u_col, c2l_scale_type='urbanf', set_nourb=spval) this%eflx_lwrad_net_patch(begp:endp) = spval call hist_addfld1d (fname='FIRA', units='W/m^2', & @@ -480,7 +480,7 @@ subroutine InitHistory(this, bounds, is_simple_buildtemp, is_prog_buildtemp) this%eflx_lwrad_net_u_patch(begp:endp) = spval call hist_addfld1d (fname='FIRA_U', units='W/m^2', & avgflag='A', long_name='Urban net infrared (longwave) radiation', & - ptr_patch=this%eflx_lwrad_net_u_patch, c2l_scale_type='urbanf', set_nourb=spval, default='inactive') + ptr_patch=this%eflx_lwrad_net_u_patch, c2l_scale_type='urbanf', set_nourb=spval) this%eflx_soil_grnd_patch(begp:endp) = spval call hist_addfld1d (fname='EFLX_SOIL_GRND', units='W/m^2', & @@ -490,12 +490,12 @@ subroutine InitHistory(this, bounds, is_simple_buildtemp, is_prog_buildtemp) this%eflx_lwrad_out_u_patch(begp:endp) = spval call hist_addfld1d (fname='FIRE_U', units='W/m^2', & avgflag='A', long_name='Urban emitted infrared (longwave) radiation', & - ptr_patch=this%eflx_lwrad_out_u_patch, c2l_scale_type='urbanf', set_nourb=spval, default='inactive') + ptr_patch=this%eflx_lwrad_out_u_patch, c2l_scale_type='urbanf', set_nourb=spval) this%eflx_sh_tot_u_patch(begp:endp) = spval call hist_addfld1d (fname='FSH_U', units='W/m^2', & avgflag='A', long_name='Urban sensible heat', & - ptr_patch=this%eflx_sh_tot_u_patch, c2l_scale_type='urbanf', set_nourb=spval, default='inactive') + ptr_patch=this%eflx_sh_tot_u_patch, c2l_scale_type='urbanf', set_nourb=spval) this%eflx_sh_precip_conversion_col(begc:endc) = spval call hist_addfld1d (fname = 'FSH_PRECIP_CONVERSION', units='W/m^2', & @@ -505,12 +505,12 @@ subroutine InitHistory(this, bounds, is_simple_buildtemp, is_prog_buildtemp) this%eflx_lh_tot_u_patch(begp:endp) = spval call hist_addfld1d (fname='EFLX_LH_TOT_U', units='W/m^2', & avgflag='A', long_name='Urban total evaporation', & - ptr_patch=this%eflx_lh_tot_u_patch, c2l_scale_type='urbanf', set_nourb=spval, default='inactive') + ptr_patch=this%eflx_lh_tot_u_patch, c2l_scale_type='urbanf', set_nourb=spval) this%eflx_soil_grnd_u_patch(begp:endp) = spval call hist_addfld1d (fname='FGR_U', units='W/m^2', & avgflag='A', long_name='Urban heat flux into soil/snow including snow melt', & - ptr_patch=this%eflx_soil_grnd_u_patch, c2l_scale_type='urbanf', set_nourb=spval, default='inactive') + ptr_patch=this%eflx_soil_grnd_u_patch, c2l_scale_type='urbanf', set_nourb=spval) this%netrad_patch(begp:endp) = spval call hist_addfld1d (fname='Rnet', units='W/m^2', & diff --git a/src/biogeophys/SoilStateType.F90 b/src/biogeophys/SoilStateType.F90 index 4b9ced3466..e491613ceb 100644 --- a/src/biogeophys/SoilStateType.F90 +++ b/src/biogeophys/SoilStateType.F90 @@ -280,7 +280,7 @@ subroutine InitHistory(this, bounds) this%soilalpha_u_col(begc:endc) = spval call hist_addfld1d (fname='SoilAlpha_U', units='unitless', & avgflag='A', long_name='urban factor limiting ground evap', & - ptr_col=this%soilalpha_u_col, set_nourb=spval, default='inactive') + ptr_col=this%soilalpha_u_col, set_nourb=spval) if (use_cn) then this%watsat_col(begc:endc,:) = spval diff --git a/src/biogeophys/SolarAbsorbedType.F90 b/src/biogeophys/SolarAbsorbedType.F90 index d1941f68cc..fa1de4a753 100644 --- a/src/biogeophys/SolarAbsorbedType.F90 +++ b/src/biogeophys/SolarAbsorbedType.F90 @@ -206,7 +206,7 @@ subroutine InitHistory(this, bounds) this%fsa_u_patch(begp:endp) = spval call hist_addfld1d (fname='FSA_U', units='W/m^2', & avgflag='A', long_name='Urban absorbed solar radiation', & - ptr_patch=this%fsa_u_patch, c2l_scale_type='urbanf', set_nourb=spval, default='inactive') + ptr_patch=this%fsa_u_patch, c2l_scale_type='urbanf', set_nourb=spval) this%fsr_patch(begp:endp) = spval call hist_addfld1d (fname='FSR', units='W/m^2', & diff --git a/src/biogeophys/TemperatureType.F90 b/src/biogeophys/TemperatureType.F90 index 58e4c93e7b..6fd8faf037 100644 --- a/src/biogeophys/TemperatureType.F90 +++ b/src/biogeophys/TemperatureType.F90 @@ -343,7 +343,7 @@ subroutine InitHistory(this, bounds, is_simple_buildtemp, is_prog_buildtemp ) this%t_grnd_u_col(begc:endc) = spval call hist_addfld1d (fname='TG_U', units='K', & avgflag='A', long_name='Urban ground temperature', & - ptr_col=this%t_grnd_u_col, set_nourb=spval, c2l_scale_type='urbans', default='inactive') + ptr_col=this%t_grnd_u_col, set_nourb=spval, c2l_scale_type='urbans') this%t_lake_col(begc:endc,:) = spval call hist_addfld2d (fname='TLAKE', units='K', type2d='levlak', & @@ -373,7 +373,7 @@ subroutine InitHistory(this, bounds, is_simple_buildtemp, is_prog_buildtemp ) this%t_ref2m_r_patch(begp:endp) = spval call hist_addfld1d (fname='TSA_R', units='K', & avgflag='A', long_name='Rural 2m air temperature', & - ptr_patch=this%t_ref2m_r_patch, set_spec=spval, default='inactive') + ptr_patch=this%t_ref2m_r_patch, set_spec=spval) this%t_ref2m_min_patch(begp:endp) = spval call hist_addfld1d (fname='TREFMNAV', units='K', & @@ -388,27 +388,27 @@ subroutine InitHistory(this, bounds, is_simple_buildtemp, is_prog_buildtemp ) this%t_ref2m_min_r_patch(begp:endp) = spval call hist_addfld1d (fname='TREFMNAV_R', units='K', & avgflag='A', long_name='Rural daily minimum of average 2-m temperature', & - ptr_patch=this%t_ref2m_min_r_patch, set_spec=spval, default='inactive') + ptr_patch=this%t_ref2m_min_r_patch, set_spec=spval) this%t_ref2m_max_r_patch(begp:endp) = spval call hist_addfld1d (fname='TREFMXAV_R', units='K', & avgflag='A', long_name='Rural daily maximum of average 2-m temperature', & - ptr_patch=this%t_ref2m_max_r_patch, set_spec=spval, default='inactive') + ptr_patch=this%t_ref2m_max_r_patch, set_spec=spval) this%t_ref2m_u_patch(begp:endp) = spval call hist_addfld1d (fname='TSA_U', units='K', & avgflag='A', long_name='Urban 2m air temperature', & - ptr_patch=this%t_ref2m_u_patch, set_nourb=spval, default='inactive') + ptr_patch=this%t_ref2m_u_patch, set_nourb=spval) this%t_ref2m_min_u_patch(begp:endp) = spval call hist_addfld1d (fname='TREFMNAV_U', units='K', & avgflag='A', long_name='Urban daily minimum of average 2-m temperature', & - ptr_patch=this%t_ref2m_min_u_patch, set_nourb=spval, default='inactive') + ptr_patch=this%t_ref2m_min_u_patch, set_nourb=spval) this%t_ref2m_max_u_patch(begp:endp) = spval call hist_addfld1d (fname='TREFMXAV_U', units='K', & avgflag='A', long_name='Urban daily maximum of average 2-m temperature', & - ptr_patch=this%t_ref2m_max_u_patch, set_nourb=spval, default='inactive') + ptr_patch=this%t_ref2m_max_u_patch, set_nourb=spval) if (use_biomass_heat_storage) then this%t_stem_patch(begp:endp) = spval diff --git a/src/biogeophys/WaterDiagnosticBulkType.F90 b/src/biogeophys/WaterDiagnosticBulkType.F90 index f91aaca761..48aeef73aa 100644 --- a/src/biogeophys/WaterDiagnosticBulkType.F90 +++ b/src/biogeophys/WaterDiagnosticBulkType.F90 @@ -347,7 +347,7 @@ subroutine InitBulkHistory(this, bounds) units='%', & avgflag='A', & long_name=this%info%lname('Rural 2m relative humidity'), & - ptr_patch=this%rh_ref2m_r_patch, set_spec=spval, default='inactive') + ptr_patch=this%rh_ref2m_r_patch, set_spec=spval) this%rh_ref2m_u_patch(begp:endp) = spval call hist_addfld1d ( & @@ -355,7 +355,7 @@ subroutine InitBulkHistory(this, bounds) units='%', & avgflag='A', & long_name=this%info%lname('Urban 2m relative humidity'), & - ptr_patch=this%rh_ref2m_u_patch, set_nourb=spval, default='inactive') + ptr_patch=this%rh_ref2m_u_patch, set_nourb=spval) this%rh_af_patch(begp:endp) = spval call hist_addfld1d ( & diff --git a/src/biogeophys/WaterFluxType.F90 b/src/biogeophys/WaterFluxType.F90 index 23980a21c9..88b1b3e5cf 100644 --- a/src/biogeophys/WaterFluxType.F90 +++ b/src/biogeophys/WaterFluxType.F90 @@ -578,7 +578,7 @@ subroutine InitHistory(this, bounds) units='mm/s', & avgflag='A', & long_name=this%info%lname('Urban total runoff'), & - ptr_col=this%qflx_runoff_u_col, set_nourb=spval, c2l_scale_type='urbanf', default='inactive') + ptr_col=this%qflx_runoff_u_col, set_nourb=spval, c2l_scale_type='urbanf') this%qflx_runoff_r_col(begc:endc) = spval call hist_addfld1d ( & diff --git a/src/main/test/CMakeLists.txt b/src/main/test/CMakeLists.txt index bf8c164260..b10abdbcba 100644 --- a/src/main/test/CMakeLists.txt +++ b/src/main/test/CMakeLists.txt @@ -8,4 +8,5 @@ add_subdirectory(filter_test) add_subdirectory(initVertical_test) add_subdirectory(ncdio_utils_test) add_subdirectory(topo_test) +add_subdirectory(decomp_test) add_subdirectory(abortutils_test) diff --git a/src/main/test/decomp_test/CMakeLists.txt b/src/main/test/decomp_test/CMakeLists.txt new file mode 100644 index 0000000000..b8c6fea3cf --- /dev/null +++ b/src/main/test/decomp_test/CMakeLists.txt @@ -0,0 +1,8 @@ +set(pfunit_sources + test_decompMod.pf) + +add_pfunit_ctest(decomp + TEST_SOURCES "${pfunit_sources}" + LINK_LIBRARIES clm csm_share esmf + EXTRA_FINALIZE unittest_finalize_esmf + EXTRA_USE unittestInitializeAndFinalize) diff --git a/src/main/test/decomp_test/test_decompMod.pf b/src/main/test/decomp_test/test_decompMod.pf new file mode 100644 index 0000000000..8112e4c6ce --- /dev/null +++ b/src/main/test/decomp_test/test_decompMod.pf @@ -0,0 +1,106 @@ +module test_decompMod + + ! Tests of decompMod + + use funit + use decompMod + use shr_kind_mod , only : r8 => shr_kind_r8 + + implicit none + + @TestCase + type, extends(TestCase) :: TestDecompMod + contains + procedure :: setUp + procedure :: tearDown + procedure :: create_simpleSingleDecomp + end type TestDecompMod + + integer, parameter :: ni = 2 + integer, parameter :: nj = 2 + +contains + + ! ======================================================================== + ! Helper routines + ! ======================================================================== + + subroutine setUp(this) + class(TestDecompMod), intent(inout) :: this + + call this%create_simpleSingleDecomp() + end subroutine setUp + + subroutine tearDown(this) + class(TestDecompMod), intent(inout) :: this + + call decompmod_clean() + + end subroutine tearDown + + subroutine create_simpleSingleDecomp(this) + use spmdMod, only : iam + class(TestDecompMod), intent(inout) :: this + + integer :: clump_pproc + ! TODO: When decompMod has it's own allocate method that could be used here + nclumps = 1 + clump_pproc = nclumps + allocate(procinfo%cid(clump_pproc)) + allocate(clumps(nclumps)) + ! Set the procinfo and clumps values + ! TODO: Use initialization method when available (currently in decompInitMod) + procinfo%cid = 1 + procinfo%ncells = ni*nj + procinfo%begg = 1 + procinfo%endg = procinfo%ncells + procinfo%nclumps = nclumps + clumps(:)%owner = iam + clumps(:)%begg = 1 + clumps(:)%endg = procinfo%ncells + + end subroutine create_simpleSingleDecomp + ! ======================================================================== + ! Begin tests + ! ======================================================================== + + @Test + subroutine test_get_clump_bounds(this) + class(TestDecompMod), intent(inout) :: this + + type(bounds_type) :: bounds + integer :: n + + do n = 1, procinfo%nclumps + call get_clump_bounds(n, bounds) + @assertEqual(bounds%level, bounds_level_clump) + @assertEqual(bounds%clump_index, n) + end do + end subroutine test_get_clump_bounds + + @Test + subroutine test_get_proc_bounds(this) + class(TestDecompMod), intent(inout) :: this + + type(bounds_type) :: bounds + + ! Add optional argument, just to test that it can handle it + call get_proc_bounds(bounds, allow_call_from_threaded_region=.true.) + @assertEqual(bounds%level, bounds_level_proc) + @assertEqual(bounds%clump_index, -1) + end subroutine test_get_proc_bounds + + @Test + subroutine test_proc_clump_bounds_equal(this) + class(TestDecompMod), intent(inout) :: this + + type(bounds_type) :: bounds_clump, bounds_proc + + @assertTrue(procinfo%nclumps == 1) + call get_clump_bounds(1, bounds_clump) + call get_proc_bounds(bounds_proc) + @assertEqual(bounds_proc%begg, bounds_clump%begg) + @assertEqual(bounds_proc%endg, bounds_clump%endg) + end subroutine test_proc_clump_bounds_equal + +end module test_decompMod 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)