diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 7ea285a6bc..e4de84b5a2 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -25,9 +25,12 @@ e4d38681df23ccca0ae29581a45f8362574e0630 a9d96219902cf609636886c7073a84407f450d9a d866510188d26d51bcd6d37239283db690af7e82 0dcd0a3c1abcaffe5529f8d79a6bc34734b195c7 +179e2eeb1ad919539e21f92947fcc80c9b054869 e096358c832ab292ddfd22dd5878826c7c788968 475831f0fb0e31e97f630eac4e078c886558b61c fd5f177131d63d39e79a13918390bdfb642d781e +4b03f5a93d781d8ef0ca58fa88fc510784ba9b1a +# Ran SystemTests and python/ctsm through black python formatter a51816e0de380300b69db9fc3e2c7fa83b267b64 5364ad66eaceb55dde2d3d598fe4ce37ac83a93c 8056ae649c1b37f5e10aaaac79005d6e3a8b2380 @@ -37,6 +40,11 @@ a51816e0de380300b69db9fc3e2c7fa83b267b64 183fc26a6691bbdf87f515dc47924a64be3ced9b 6fccf682eaf718615407d9bacdd3903b8786a03d 2500534eb0a83cc3aff94b30fb62e915054030bf +# Ran individual files through black python formatter +f05fcbc10eda44e574de7554c8ac2b0417215b61 +eb4906d8ee78b4f5ad5b3b990c414f6e113b661f +63c4873eab8af9fdd1f89bfe0a5416dcc53394fa +7053b2980da08bb46f82d9cf1a5c4c63b98b37f6 78d05967c2b027dc9776a884716597db6ef7f57c 47839a77229c61555e3b8932927bb54cdc511b27 a0d014fae9550dd9ffbc934abd29ef16176f8208 @@ -67,3 +75,5 @@ cdf40d265cc82775607a1bf25f5f527bacc97405 3b7a2876933263f8986e4069f5d23bd45635756f 3dd489af7ebe06566e2c6a1c7ade18550f1eb4ba 742cfa606039ab89602fde5fef46458516f56fd4 +7fcf6e393377158bd4764e0c5c0698036b0f7879 +29543c6f5e56bfcd1da5e86f622059274aafa365 diff --git a/.github/workflows/docs-build-and-deploy.yml b/.github/workflows/docs-build-and-deploy.yml index 2c928e0ccb..06c8812ef2 100644 --- a/.github/workflows/docs-build-and-deploy.yml +++ b/.github/workflows/docs-build-and-deploy.yml @@ -12,6 +12,7 @@ on: # Include all include::ed files outside doc/ directory! - 'src/README.unit_testing' - 'tools/README' + - 'tools/external/representative-hillslopes/shell_scripts/README.md' # Allows you to run this workflow manually from the Actions tab workflow_dispatch: diff --git a/.github/workflows/docs-omnibus.yml b/.github/workflows/docs-omnibus.yml index 1636150fae..939872a4d8 100644 --- a/.github/workflows/docs-omnibus.yml +++ b/.github/workflows/docs-omnibus.yml @@ -12,6 +12,7 @@ on: # Include all include::ed files outside doc/ directory! - 'src/README.unit_testing' - 'tools/README' + - 'tools/external/representative-hillslopes/shell_scripts/README.md' pull_request: # Run on pull requests that change the listed files @@ -23,6 +24,7 @@ on: # Include all include::ed files outside doc/ directory! - 'src/README.unit_testing' - 'tools/README' + - 'tools/external/representative-hillslopes/shell_scripts/README.md' workflow_dispatch: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 074a674ffe..991783d6bc 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -14,6 +14,7 @@ on: # Include all include::ed files outside doc/ directory! - 'src/README.unit_testing' - 'tools/README' + - 'tools/external/representative-hillslopes/shell_scripts/README.md' pull_request: # Run on pull requests that change the listed files @@ -26,6 +27,7 @@ on: # Include all include::ed files outside doc/ directory! - 'src/README.unit_testing' - 'tools/README' + - 'tools/external/representative-hillslopes/shell_scripts/README.md' workflow_dispatch: diff --git a/.gitignore b/.gitignore index 4ef520757a..90ffe5561d 100644 --- a/.gitignore +++ b/.gitignore @@ -61,6 +61,7 @@ buildnmlc td.*.status td.*.log td.*.status.xFail +.coverage # mksurfdata output surfdata_*.log @@ -115,4 +116,3 @@ Depends # Docs build and testing output _build*/ _publish*/ -doc/.coverage diff --git a/.gitmodules b/.gitmodules index 48bc074b92..aea6fb0a90 100644 --- a/.gitmodules +++ b/.gitmodules @@ -128,3 +128,11 @@ fxtag = v2.2.6 fxrequired = ToplevelOptional # Standard Fork to compare to with "git fleximod test" to ensure personal forks aren't committed fxDONOTUSEurl = https://github.com/ESMCI/doc-builder + +[submodule "representative-hillslopes"] +path = tools/external/representative-hillslopes +url = https://github.com/swensosc/Representative_Hillslopes +fxtag = f3f980fc40783b9a86143e97bb3773f7761ed29b +fxrequired = ToplevelOptional +# Standard Fork to compare to with "git fleximod test" to ensure personal forks aren't committed +fxDONOTUSEurl = https://github.com/swensosc/Representative_Hillslopes diff --git a/.lib/git-fleximod/escomp_install b/.lib/git-fleximod/escomp_install old mode 100644 new mode 100755 diff --git a/doc/source/users_guide/using-clm-tools/creating-hillslope-datasets.rst b/doc/source/users_guide/using-clm-tools/creating-hillslope-datasets.rst new file mode 100644 index 0000000000..2491884c79 --- /dev/null +++ b/doc/source/users_guide/using-clm-tools/creating-hillslope-datasets.rst @@ -0,0 +1,77 @@ +.. _rst_creating_hillslope_datasets: + +=========================== +Creating hillslope datasets +=========================== + +Creating hillslope datasets for use in CTSM is a multi-step process: + +#. Perform the Height Above Nearest Drainage (HAND) analysis, which processes the DEM data for each gridcell, breaking the work into a number of "chunks." +#. Combine the per-gridcell files from the HAND analysis into one file per chunk. +#. Combine the per-chunk files into a ``hillslope_file`` for use in CTSM. + +HAND analysis +=============== + +The HAND analysis uses the optional ``Representative_Hillslopes`` submodule. Before following these instructions, do ``bin/git-fleximod update representative-hillslopes`` to make sure that's been downloaded. Then the instructions assume: + +#. You've installed the ``Representative_Hillslopes`` Conda environment, if needed (``conda env create -f tools/external/representative-hillslopes/conda_yml/casper.yml``). +#. You've activated that environment (``conda activate Representative_Hillslopes``). +#. You've changed your working directory to ``tools/external/representative-hillslopes/shell_scripts/``. + +.. mdinclude:: ../../../../tools/external/representative-hillslopes/shell_scripts/README.md + +Gridcell files to chunk files +============================= + +(Instructions here assume you have activated the :ref:`ctsm_pylib` Conda environment.) + +This is performed by the ``tools/hillslopes/combine_gridcell_files`` script; do ``tools/hillslopes/combine_gridcell_files --help`` for usage help. Briefly, you need to provide three things: + +* ``-i``, input surface dataset: The CTSM surface dataset (``fsurdat``) you used in the HAND analysis above. +* ``-d``, input directory: The directory the HAND analysis above saved files to (``outdir`` in those instructions). +* ``-o``, output directory: The directory where you want the chunk files generated by ``combine_gridcell_files`` to be saved. + +Example +-------- + +This example can be run on the NSF NCAR Casper machine; it assumes you've activated the ``ctsm_pylib`` Conda environment. + +.. code-block:: bash + + fsurdat=python/ctsm/test/testinputs/surfdata_5x5_amazon_hist_16pfts_CMIP6_2000_c231031.nc + indir=$SCRATCH/hillslopes_5x5_amazon/hand_analysis_global + outdir=$SCRATCH/hillslopes_5x5_amazon/hand_analysis_global/combined + + python tools/hillslopes/combine_gridcell_files \ + -i $fsurdat \ + -d $indir \ + -o $outdir + + +Chunk files to ``hillslope_file`` +================================= + +(Instructions here assume you have activated the ``ctsm_pylib`` Conda environment.) + +This is performed by the ``tools/hillslopes/combine_chunk_files`` script; do ``tools/hillslopes/combine_chunk_files --help`` for usage help. Briefly, you need to provide three things: + +* ``-i``, input surface dataset: The CTSM surface dataset (``fsurdat``) you used in the steps above. +* ``-d``, input directory: The directory where ``combine_gridcell_files`` saved the chunk files (``-o`` in those instructions above). +* ``-o``, output file: The file path where you want the output ``hillslope_file`` to be saved. + +Example +-------- + +This example can be run on the NSF NCAR Casper machine; it assumes you've activated the ``ctsm_pylib`` Conda environment. + +.. code-block:: bash + + fsurdat=python/ctsm/test/testinputs/surfdata_5x5_amazon_hist_16pfts_CMIP6_2000_c231031.nc + indir=$SCRATCH/hillslopes_5x5_amazon/hand_analysis_global/combined + outfile=${indir}/$(basename ${fsurdat} | sed "s@surfdata@hilldata@") + + python tools/hillslopes/combine_chunk_files \ + -i $fsurdat \ + -d $indir \ + -o $outfile \ No newline at end of file diff --git a/doc/source/users_guide/using-clm-tools/index.rst b/doc/source/users_guide/using-clm-tools/index.rst index 9b5318e5a2..516f87b89c 100644 --- a/doc/source/users_guide/using-clm-tools/index.rst +++ b/doc/source/users_guide/using-clm-tools/index.rst @@ -19,6 +19,7 @@ Using CLM tools using-ctsm-pylib.rst creating-input-for-surface-dataset-generation.rst creating-surface-datasets.rst + creating-hillslope-datasets.rst creating-domain-files.rst observational-sites-datasets.rst cprnc.rst diff --git a/python/ctsm/hillslopes/__init__.py b/python/ctsm/hillslopes/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/python/ctsm/hillslopes/combine_chunk_files.py b/python/ctsm/hillslopes/combine_chunk_files.py new file mode 100755 index 0000000000..309728c3a0 --- /dev/null +++ b/python/ctsm/hillslopes/combine_chunk_files.py @@ -0,0 +1,228 @@ +""" +Combine chunk files into a file for use in CTSM +""" + +import argparse +import sys +import os +import datetime as dt +import numpy as np + +# The below "pylint: disable" is because pylint complains that netCDF4 has no member Dataset, even +# though it does. +from netCDF4 import Dataset # pylint: disable=no-name-in-module +from ctsm import ctsm_logging +from ctsm.hillslopes.hillslope_utils import ( + add_variable_nc, + add_longxy_latixy_nc, + NETCDF_FORMAT, + get_chunks_to_process, +) +from ctsm.hillslopes.hillslope_vars import HillslopeVars + + +def parse_arguments(argv): + """ + Parse arguments to script + """ + parser = argparse.ArgumentParser( + description="Combine files for each chunk into a file for use in CTSM" + ) + + # Use these groups to organize --help output. Otherwise, required but named (i.e., non- + # positional) arguments (e.g., --input-file) get shown as optional. + required_named = parser.add_argument_group("Required named arguments") + optional_named = parser.add_argument_group("Optional named arguments") + ctsm_logging.add_logging_args(parser) + + # Input and output file settings + required_named.add_argument( + "-i", + "--input-file", + help="Input surface dataset", + required=True, + ) + required_named.add_argument( + "-d", + "--input-dir", + help="Directory containing combined-chunk files (outputs of combine_gridcell_files)", + required=True, + ) + required_named.add_argument( + "-o", + "--output-file", + help="Output file", + required=True, + ) + optional_named.add_argument("--overwrite", help="overwrite", action="store_true", default=False) + + dem_source_default = "MERIT" + optional_named.add_argument( + "--dem-source", + help=f"DEM to use (default: {dem_source_default})", + type=str, + default=dem_source_default, + ) + + default_n_bins = 4 + optional_named.add_argument( + "--n-bins", + type=int, + default=default_n_bins, + help=f"Number of elevation bins (default: {default_n_bins}). " + + "Used to generate input filename template.", + ) + + default_hillslope_form = "Trapezoidal" + optional_named.add_argument( + "--hillslope-form", + help=f"Hillslope form (default: {default_hillslope_form}). " + + "Used to generate input filename template.", + type=str, + default=default_hillslope_form, + ) + + args = parser.parse_args(argv) + ctsm_logging.process_logging_args(args) + + # Check arguments + if not os.path.exists(args.input_file): + msg = f"Input file not found: {args.input_file}" + ctsm_logging.logger.error(msg) + raise FileNotFoundError(msg) + if not os.path.exists(args.input_dir): + msg = f"Input directory not found: {args.input_dir}" + ctsm_logging.logger.error(msg) + raise FileNotFoundError(msg) + if os.path.exists(args.output_file) and not args.overwrite: + msg = f"Output file already exists: {args.output_file}" + ctsm_logging.logger.error(msg) + raise FileExistsError(msg) + + return args + + +def finish_saving(args): + """ + Save some extra stuff to the netCDF file + """ + with Dataset(args.input_file, "r") as ds_in: + lon2d = ds_in.variables["LONGXY"][:] + lat2d = ds_in.variables["LATIXY"][:] + has_area = "AREA" in ds_in.variables + if has_area: + area = ds_in.variables["AREA"][:] + with Dataset(args.output_file, "a", format=NETCDF_FORMAT) as ds_out: + add_longxy_latixy_nc(lon2d, lat2d, ds_out) + if has_area: + add_variable_nc( + name="AREA", + units="km^2", + long_name="area", + data=area, + dataset=ds_out, + dims=["lsmlat", "lsmlon"], + ) + ds_out.setncattr("input_file", args.input_file) + now = dt.datetime.now() + datestr = now.strftime("%Y-%m-%d") + ds_out.setncattr("creation_date", datestr) + + +def get_mask_var(surface_ds): + """ + Get the variable to be used as a mask + """ + mask_var = None + mask_var_options = ["PFTDATA_MASK", "LANDFRAC_PFT"] + for mask_var_option in mask_var_options: + if mask_var_option in surface_ds.variables.keys(): + mask_var = mask_var_option + if mask_var is None: + msg = f"No variable found in sfcfile that looks like a mask ({mask_var_options})" + ctsm_logging.logger.error(msg) + raise KeyError(msg) + + landmask = np.asarray(surface_ds.variables[mask_var][:,]) + return mask_var, landmask + + +def main(): + """ + See module description + """ + ctsm_logging.setup_logging_pre_config() + args = parse_arguments(sys.argv[1:]) + + # Choose data files to combine and append + cfile0 = os.path.join( + args.input_dir, + f"combined_chunk_ChunkIndex_HAND_{args.n_bins}_col_hillslope_geo_params" + + f"_{args.hillslope_form}_{args.dem_source}.nc", + ) + + surface_ds = Dataset(args.input_file, "r") + mask_var, landmask = get_mask_var(surface_ds) + surface_ds.close() + if mask_var == "LANDFRAC_PFT": + landmask[np.where(landmask > 0)] = 1 + + hillslope_vars = None + add_bedrock = None + add_stream = None + file_prefix = "combined_chunk" + chunks_to_process = get_chunks_to_process(args, file_prefix) + for cndx in chunks_to_process: + ctsm_logging.logger.info("Chunk %d...", cndx) + cstr = "{:02d}".format(cndx) + chunk_file = cfile0.replace("ChunkIndex", cstr) + file_exists = os.path.exists(chunk_file) + + if hillslope_vars is None and file_exists: + chunk_ds = Dataset(chunk_file, "r") + + ncolumns_per_gridcell = len(chunk_ds.dimensions["nmaxhillcol"]) + nhillslope = len(chunk_ds.dimensions["nhillslope"]) + n_lat = len(chunk_ds.dimensions["lsmlat"]) + n_lon = len(chunk_ds.dimensions["lsmlon"]) + + add_bedrock = "hillslope_bedrock_depth" in chunk_ds.variables.keys() + add_stream = "hillslope_stream_depth" in chunk_ds.variables.keys() + + chunk_ds.close() + + hillslope_vars = HillslopeVars(ncolumns_per_gridcell, nhillslope, n_lat, n_lon) + + if not file_exists: + ctsm_logging.logger.info("Skipping; chunk file not found: %s", chunk_file) + continue + + # Read hillslope variables from one chunk file + if hillslope_vars is None: + msg = f"No chunk files found in '{args.input_dir}' starting with '{file_prefix}'" + ctsm_logging.logger.error(msg) + raise FileNotFoundError(msg) + hillslope_vars.read(chunk_file, add_bedrock, add_stream) + + for i in range(n_lon): + for j in range(n_lat): + hillslope_vars.update(i, j, add_bedrock, add_stream, landmask=landmask) + + if hillslope_vars is None: + msg = f"No files found in '{args.input_dir}'" + ctsm_logging.logger.error(msg) + raise FileNotFoundError(msg) + + # -- Write data to file ------------------ + hillslope_vars.save( + input_file=args.input_file, + output_file=args.output_file, + ncolumns_per_gridcell=ncolumns_per_gridcell, + nhillslope=nhillslope, + add_bedrock=add_bedrock, + add_stream=add_stream, + logger=ctsm_logging.logger, + ) + finish_saving(args) + + ctsm_logging.logger.info("%s created", args.output_file) diff --git a/python/ctsm/hillslopes/combine_gridcell_files.py b/python/ctsm/hillslopes/combine_gridcell_files.py new file mode 100755 index 0000000000..6988fe43ec --- /dev/null +++ b/python/ctsm/hillslopes/combine_gridcell_files.py @@ -0,0 +1,241 @@ +""" +For each chunk in HAND analysis, combine all gridcell files into file. +""" + +import sys +import os +import argparse +import glob +import datetime + +# The below "pylint: disable" is because pylint complains that netCDF4 has no +# member Dataset, even though it does. +from netCDF4 import Dataset # pylint: disable=no-name-in-module + +from ctsm import ctsm_logging +from ctsm.hillslopes.hillslope_utils import get_chunks_to_process +from ctsm.hillslopes.hillslope_vars import HillslopeVars + + +def parse_arguments(argv): + """ + Parse arguments to script + """ + parser = argparse.ArgumentParser(description="Combine gridcell files into single file") + + # Use these groups to organize --help output. Otherwise, required but named (i.e., non- + # positional) arguments (e.g., --input-file) get shown as optional. + required_named = parser.add_argument_group("Required named arguments") + optional_named = parser.add_argument_group("Optional named arguments") + + required_named.add_argument( + "-i", + "--input-file", + help="Input surface dataset with grid information", + required=True, + ) + required_named.add_argument( + "-d", + "--input-dir", + help="Directory containing chunk files", + required=True, + ) + optional_named.add_argument( + "-o", + "--output-dir", + help="Directory where output file should be saved (default: current dir)", + default=os.getcwd(), + ) + dem_source_default = "MERIT" + optional_named.add_argument( + "--dem-source", + help=f"DEM to use (default: {dem_source_default})", + type=str, + default=dem_source_default, + ) + default_n_bins = 4 + optional_named.add_argument( + "--n-bins", + type=int, + default=default_n_bins, + help=f"Number of elevation bins (default: {default_n_bins}). " + + "Used to generate input filename template.", + ) + default_hillslope_form = "Trapezoidal" + optional_named.add_argument( + "--hillslope-form", + help=f"Hillslope form (default: {default_hillslope_form}). " + + "Used to generate input filename template.", + type=str, + default=default_hillslope_form, + ) + optional_named.add_argument( + "--cndx", + help=( + "Chunk(s) to process. If excluded will process all chunks (see --n-chunks). To " + + "include, specify either a single chunk or a comma-separated list of chunks." + ), + nargs=1, + type=str, + default=None, + ) + optional_named.add_argument( + "--overwrite", + help="Overwrite existing output files? If not given, will skip existing files.", + action="store_true", + default=False, + ) + + ctsm_logging.add_logging_args(parser) + + args = parser.parse_args(argv) + ctsm_logging.process_logging_args(args) + + # Check arguments + if not os.path.exists(args.input_file): + msg = f"Input file not found: {args.input_file}" + ctsm_logging.logger.error(msg) + raise FileNotFoundError(msg) + if not os.path.exists(args.input_dir): + msg = f"Input directory not found: {args.input_dir}" + ctsm_logging.logger.error(msg) + raise FileNotFoundError(msg) + if not os.path.exists(args.output_dir): + os.makedirs(args.output_dir) + + return args + + +def main(): + """ + See module description + """ + + ctsm_logging.setup_logging_pre_config() + args = parse_arguments(sys.argv[1:]) + + chunks_to_process = get_chunks_to_process(args, "chunk") + + nhillslope = None + add_bedrock = None + do_add_stream_channel_vars = None + nmaxhillcol = None + + for cndx in chunks_to_process: + + # Gridcell file directory + cfile = os.path.join( + args.input_dir, + "chunk_{:02d}_HAND_{:d}_col_hillslope_geo_params_{}_{}.nc".format( + cndx, args.n_bins, args.hillslope_form, args.dem_source + ), + ) + + # Output file + outfile_path = os.path.join( + args.output_dir, os.path.split(cfile)[-1].replace("chunk_", "combined_chunk_") + ) + + # Read output file coordinates + fsurdat = Dataset(args.input_file, "r") + n_lat = len(fsurdat.dimensions["lsmlat"]) + n_lon = len(fsurdat.dimensions["lsmlon"]) + fsurdat.close() + + # Check for output file existence + if os.path.exists(outfile_path): + if args.overwrite: + ctsm_logging.logger.warning("%s exists; overwriting", outfile_path) + else: + ctsm_logging.logger.warning("%s exists; skipping", outfile_path) + continue + + # Locate gridcell files + gfile = cfile.replace(".nc", "*.nc") + gfiles = glob.glob(gfile) + gfiles.sort() + if len(gfiles) == 0: + ctsm_logging.logger.info("Chunk %d: Skipping; no files found matching %s", cndx, gfile) + continue + ctsm_logging.logger.info("Chunk %d: Combining %d files...", cndx, len(gfiles)) + + # Read hillslope data dimensions/settings, if not done yet + if nhillslope is None: + with Dataset(gfiles[0], "r") as first_gridcell_file: + nhillslope = len(first_gridcell_file.dimensions["nhillslope"]) + nmaxhillcol = len(first_gridcell_file.dimensions["nmaxhillcol"]) + add_bedrock = "hillslope_bedrock_depth" in first_gridcell_file.variables.keys() + do_add_stream_channel_vars = ( + "hillslope_stream_depth" in first_gridcell_file.variables.keys() + ) + + write_to_file( + outfile_path=outfile_path, + n_lat=n_lat, + n_lon=n_lon, + gfiles=gfiles, + nhillslope=nhillslope, + nmaxhillcol=nmaxhillcol, + add_bedrock=add_bedrock, + do_add_stream_channel_vars=do_add_stream_channel_vars, + ) + + +def write_to_file( + *, + outfile_path, + n_lat, + n_lon, + gfiles, + nhillslope, + nmaxhillcol, + add_bedrock, + do_add_stream_channel_vars, +): + """ + Write to file + """ + # pylint: disable=too-many-statements + outfile = Dataset(outfile_path, "w") + outfile.creation_date = datetime.date.today().strftime("%y%m%d") + + outfile.createDimension("lsmlon", n_lon) + outfile.createDimension("lsmlat", n_lat) + outfile.createDimension("nhillslope", nhillslope) + outfile.createDimension("nmaxhillcol", nmaxhillcol) + outfile.close() + + hillslope_vars = HillslopeVars(nmaxhillcol, nhillslope, n_lat, n_lon, incl_latlon=True) + + # loop over gridcell files + for gfile in gfiles: + y1, x1 = gfile.index("j_"), gfile.index("i_") # pylint: disable=invalid-name + j, i = int(gfile[y1 + 2 : y1 + 5]), int(gfile[x1 + 2 : x1 + 5]) + + hillslope_vars.read(gfile, add_bedrock, do_add_stream_channel_vars, incl_latlon=True) + + hillslope_vars.update( + i, + j, + add_bedrock, + do_add_stream_channel_vars, + incl_latlon=True, + incl_chunkmask=True, + this_chunk_1d=True, + remove_if_too_few_aspects=False, + ) + + hillslope_vars.save( + input_file=None, + output_file=outfile_path, + ncolumns_per_gridcell=nmaxhillcol, + nhillslope=nhillslope, + add_bedrock=add_bedrock, + add_stream=do_add_stream_channel_vars, + logger=ctsm_logging.logger, + n_lon=n_lon, + n_lat=n_lat, + incl_latlon=True, + incl_chunkmask=True, + ) + ctsm_logging.logger.info("%s created", outfile_path) diff --git a/python/ctsm/hillslopes/hillslope_utils.py b/python/ctsm/hillslopes/hillslope_utils.py new file mode 100644 index 0000000000..4b18476f47 --- /dev/null +++ b/python/ctsm/hillslopes/hillslope_utils.py @@ -0,0 +1,117 @@ +""" +Utilities for hillslope scripts to share +""" + +import os +import glob +import re +import numpy as np + +NETCDF_FORMAT = "NETCDF3_64BIT_OFFSET" + + +def add_longxy_latixy_nc(lon2d, lat2d, ds_out): + """ + Add LONGXY and LATIXY to a netCDF file + """ + add_variable_nc( + name="LONGXY", + units="degrees east", + long_name="longitude", + data=lon2d, + dataset=ds_out, + dims=["lsmlat", "lsmlon"], + ) + add_variable_nc( + name="LATIXY", + units="degrees north", + long_name="latitude", + data=lat2d, + dataset=ds_out, + dims=["lsmlat", "lsmlon"], + ) + + +def create_variable( + outfile, + name, + units, + long_name, + *, + dims=( + "nmaxhillcol", + "lsmlat", + "lsmlon", + ), + data_type=np.float64, +): + """ + Convenient function to use for making hillslope variables + """ + + # Variable in netCDF output file + nc_var = outfile.createVariable( + name, + data_type, + dims, + ) + nc_var.units = units + nc_var.long_name = long_name + + return nc_var + + +def add_variable_nc( + *, + name, + units, + long_name, + data, + dataset, + data_type=np.float64, + dims=("nmaxhillcol", "lsmlat", "lsmlon"), +): + """ + Convenient function to use for adding hillslope variables to a Dataset with netcdf + """ + + if isinstance(dims, list): + dims = tuple(dims) + + # Make variable + nc_var = create_variable( + dataset, + name, + units, + long_name, + dims=dims, + data_type=data_type, + ) + + # Fill with data + nc_var[:] = data + + +def get_chunks_to_process(args, prefix): + """ + Get list of chunks to process + """ + if not hasattr(args, "cndx") or args.cndx is None: + # List of gridcell files + file_list = glob.glob(os.path.join(args.input_dir, prefix + "_[0-9]*nc")) + if not file_list: + raise FileNotFoundError(f"No files found in '{args.input_dir}'") + # Extract the chunk number from the file names + chunk_list = [re.search(r"chunk_\d+", x).group() for x in file_list] + chunk_list = [x.replace("chunk_", "") for x in chunk_list] + # Get the list of unique chunk numbers + chunks_to_process = [int(x) for x in list(set(chunk_list))] + chunks_to_process.sort() + if not chunks_to_process: + raise FileNotFoundError(f"No MATCHING chunk files found in '{args.input_dir}'") + else: + chunks_to_process = [int(cndx) for cndx in args.cndx[0].split(",")] + for cndx in chunks_to_process: + if cndx < 1 or cndx > args.n_chunks: + raise RuntimeError("All cndx must be 1-{:d}".format(args.n_chunks)) + return chunks_to_process diff --git a/python/ctsm/hillslopes/hillslope_vars.py b/python/ctsm/hillslopes/hillslope_vars.py new file mode 100644 index 0000000000..de8b3b234a --- /dev/null +++ b/python/ctsm/hillslopes/hillslope_vars.py @@ -0,0 +1,453 @@ +""" +A useful class for processing hillslope files +""" + +import shutil +import numpy as np + +# The below "pylint: disable" is because pylint complains that netCDF4 has no member Dataset, even +# though it does. +from netCDF4 import Dataset # pylint: disable=no-name-in-module + +from ctsm.hillslopes.hillslope_utils import add_variable_nc, add_longxy_latixy_nc, NETCDF_FORMAT + + +class HillslopeVars: + """ + Fields to be added to hillslope_file + """ + + # pylint: disable=too-many-instance-attributes + def __init__( + self, + ncolumns_per_gridcell, + nhillslope, + n_lat, + n_lon, + *, + recurse=True, + incl_latlon=False, + incl_pftndx=False, + ): + + # Variables that will actually be saved + self.h_elev = np.zeros((ncolumns_per_gridcell, n_lat, n_lon)) + self.h_dist = np.zeros((ncolumns_per_gridcell, n_lat, n_lon)) + self.h_area = np.zeros((ncolumns_per_gridcell, n_lat, n_lon)) + self.h_slope = np.zeros((ncolumns_per_gridcell, n_lat, n_lon)) + self.h_aspect = np.zeros((ncolumns_per_gridcell, n_lat, n_lon)) + self.h_width = np.zeros((ncolumns_per_gridcell, n_lat, n_lon)) + self.h_bedrock = np.zeros((ncolumns_per_gridcell, n_lat, n_lon)) + self.h_stream_depth = np.zeros((n_lat, n_lon)) + self.h_stream_width = np.zeros((n_lat, n_lon)) + self.h_stream_slope = np.zeros((n_lat, n_lon)) + self.nhillcolumns = np.zeros((n_lat, n_lon), dtype=int) + self.pct_hillslope = np.zeros((nhillslope, n_lat, n_lon)) + self.hillslope_index = np.zeros((ncolumns_per_gridcell, n_lat, n_lon), dtype=int) + self.column_index = np.zeros((ncolumns_per_gridcell, n_lat, n_lon), dtype=int) + self.downhill_column_index = np.zeros((ncolumns_per_gridcell, n_lat, n_lon), dtype=int) + + if incl_latlon: + self.lon = np.zeros((n_lon)) + self.lat = np.zeros((n_lat)) + self.lon2d = np.zeros((n_lat, n_lon)) + self.lat2d = np.zeros((n_lat, n_lon)) + + if incl_pftndx: + self.hillslope_pftndx = np.zeros((ncolumns_per_gridcell, n_lat, n_lon), dtype=int) + else: + self.hillslope_pftndx = None + + # Placeholders for read-in data from each chunk + self.chunk_mask = np.zeros((n_lat, n_lon)) + if recurse: + self.this_chunk = HillslopeVars( + ncolumns_per_gridcell, + nhillslope, + n_lat, + n_lon, + recurse=False, + incl_latlon=incl_latlon, + ) + + def _read_samenames(self, chunk_ds): + """ + Read hillslope variables from one chunk file with the same variable names + in old and new versions + + Args: + chunk_ds (xarray Dataset): Opened chunk file + """ + self.this_chunk.nhillcolumns = chunk_ds.variables["nhillcolumns"][:,].astype(int) + self.this_chunk.pct_hillslope = chunk_ds.variables["pct_hillslope"][:,] + self.this_chunk.hillslope_index = chunk_ds.variables["hillslope_index"][:,].astype(int) + self.this_chunk.column_index = chunk_ds.variables["column_index"][:,].astype(int) + self.this_chunk.downhill_column_index = chunk_ds.variables["downhill_column_index"][ + :, + ].astype(int) + + def _read_oldnames(self, chunk_file, read_bedrock, read_stream): + """Read hillslope variables from one chunk file with old variable names + + Args: + cfile (str): Path to chunk file + read_bedrock (logical): Whether to read bedrock variable(s) + read_stream (logical): Whether to read stream variable(s) + """ + chunk_ds = Dataset(chunk_file, "r") + self.chunk_mask = chunk_ds.variables["chunk_mask"][:] + self.this_chunk.h_elev = chunk_ds.variables["h_height"][:] + self.this_chunk.h_dist = chunk_ds.variables["h_length"][:] + self.this_chunk.h_width = chunk_ds.variables["h_width"][:] + self.this_chunk.h_area = chunk_ds.variables["h_area"][:] + self.this_chunk.h_slope = chunk_ds.variables["h_slope"][:] + self.this_chunk.h_aspect = chunk_ds.variables["h_aspect"][:] + if read_bedrock: + self.this_chunk.h_bedrock = chunk_ds.variables["h_bedrock"][:] + if read_stream: + self.this_chunk.h_stream_depth = chunk_ds.variables["h_stream_depth"][:] + self.this_chunk.h_stream_width = chunk_ds.variables["h_stream_width"][:] + self.h_stream_slope = chunk_ds.variables["hillslope_stream_slope"][:] + + self._read_samenames(chunk_ds) + chunk_ds.close() + + def read(self, chunk_file, read_bedrock, read_stream, incl_latlon=False): + """Read hillslope variables from one chunk file + + Args: + cfile (str): Path to chunk file + read_bedrock (logical): Whether to read bedrock variable(s) + read_stream (logical): Whether to read stream variable(s) + """ + + chunk_ds = Dataset(chunk_file, "r") + self.this_chunk.chunk_mask = chunk_ds.variables["chunk_mask"][:] + + if incl_latlon: + self.this_chunk.lon = chunk_ds.variables["longitude"][:] + self.this_chunk.lat = chunk_ds.variables["latitude"][:] + self.this_chunk.lon2d = chunk_ds.variables["LONGXY"][:] + self.this_chunk.lat2d = chunk_ds.variables["LATIXY"][:] + + try: + self.this_chunk.h_elev = chunk_ds.variables["hillslope_elevation"][:] + except KeyError: + chunk_ds.close() + self._read_oldnames(chunk_file, read_bedrock, read_stream) + return + self.this_chunk.h_dist = chunk_ds.variables["hillslope_distance"][:] + self.this_chunk.h_width = chunk_ds.variables["hillslope_width"][:] + self.this_chunk.h_area = chunk_ds.variables["hillslope_area"][:] + self.this_chunk.h_slope = chunk_ds.variables["hillslope_slope"][:] + self.this_chunk.h_aspect = chunk_ds.variables["hillslope_aspect"][:] + if read_bedrock: + self.this_chunk.h_bedrock = chunk_ds.variables["hillslope_bedrock_depth"][:] + if read_stream: + self.this_chunk.h_stream_depth = chunk_ds.variables["hillslope_stream_depth"][:] + self.this_chunk.h_stream_width = chunk_ds.variables["hillslope_stream_width"][:] + self.this_chunk.h_stream_slope = chunk_ds.variables["hillslope_stream_slope"][:] + + self._read_samenames(chunk_ds) + chunk_ds.close() + + def update( + self, + i, + j, + add_bedrock, + add_stream, + *, + landmask=None, + incl_latlon=False, + incl_chunkmask=False, + this_chunk_1d=False, + remove_if_too_few_aspects=True, + ): + """ + Update a gridcell in chunk + """ + if landmask is not None and not ( + self.this_chunk.chunk_mask[j, i] > 0 and landmask[j, i] > 0 + ): + return + + if incl_latlon: + self.lon[i] = self.this_chunk.lon + self.lat[j] = self.this_chunk.lat + self.lon2d[j, i] = self.this_chunk.lon2d + self.lat2d[j, i] = self.this_chunk.lat2d + + if incl_chunkmask: + self.chunk_mask[j, i] = np.int32(self.this_chunk.chunk_mask) + + if this_chunk_1d: + i_this = 0 + j_this = 0 + else: + i_this = i + j_this = j + + self.h_elev[:, j, i] = self.this_chunk.h_elev[:, j_this, i_this] + self.h_dist[:, j, i] = self.this_chunk.h_dist[:, j_this, i_this] + self.h_width[:, j, i] = self.this_chunk.h_width[:, j_this, i_this] + self.h_area[:, j, i] = self.this_chunk.h_area[:, j_this, i_this] + self.h_slope[:, j, i] = self.this_chunk.h_slope[:, j_this, i_this] + self.h_aspect[:, j, i] = self.this_chunk.h_aspect[:, j_this, i_this] + if add_bedrock: + self.h_bedrock[:, j, i] = self.this_chunk.h_bedrock[:, j_this, i_this] + if add_stream: + self.h_stream_depth[j, i] = self.this_chunk.h_stream_depth[j_this, i_this] + self.h_stream_width[j, i] = self.this_chunk.h_stream_width[j_this, i_this] + self.h_stream_slope[j, i] = self.this_chunk.h_stream_slope[j_this, i_this] + + self.nhillcolumns[j, i] = self.this_chunk.nhillcolumns[j_this, i_this] + self.pct_hillslope[:, j, i] = self.this_chunk.pct_hillslope[:, j_this, i_this] + self.hillslope_index[:, j, i] = self.this_chunk.hillslope_index[:, j_this, i_this] + self.column_index[:, j, i] = self.this_chunk.column_index[:, j_this, i_this] + self.downhill_column_index[:, j, i] = self.this_chunk.downhill_column_index[ + :, j_this, i_this + ] + + # if 2 or less valid aspects, remove all hillslope data + if remove_if_too_few_aspects and self.this_chunk.nhillcolumns[j, i] > 0: + # check number of hillslopes + h_ndx = self.this_chunk.hillslope_index[:, j, i] + nactual_hillslopes = np.unique(h_ndx[h_ndx > 0]).size + if nactual_hillslopes < 3: + self._remove_hillslope_data(i, j) + + def _remove_hillslope_data(self, i, j): + self.h_elev[:, j, i] = 0 + self.h_dist[:, j, i] = 0 + self.h_width[:, j, i] = 0 + self.h_area[:, j, i] = 0 + self.h_slope[:, j, i] = 0 + self.h_aspect[:, j, i] = 0 + self.h_bedrock[:, j, i] = 0 + self.h_stream_depth[j, i] = 0 + self.h_stream_width[j, i] = 0 + self.h_stream_slope[j, i] = 0 + + self.nhillcolumns[j, i] = 0 + self.pct_hillslope[:, j, i] = 0 + self.hillslope_index[:, j, i] = 0 + self.column_index[:, j, i] = 0 + self.downhill_column_index[:, j, i] = 0 + + def save( + self, + *, + input_file, + output_file, + ncolumns_per_gridcell, + nhillslope, + add_bedrock, + add_stream, + logger=None, + n_lon=None, + n_lat=None, + incl_latlon=False, + incl_chunkmask=False, + save_fsurdat=False, + ): + """ + Save to netCDF + """ + msg = f"Saving to {output_file}" + if logger is None: + print(msg) + else: + logger.info(msg) + + # Create and open file + if save_fsurdat: + shutil.copyfile(input_file, output_file) + ds_out = Dataset(output_file, "a") + else: + ds_out = Dataset(output_file, "w", format=NETCDF_FORMAT) + + # Create dimensions + if not save_fsurdat: + if n_lon is None or n_lat is None: + if n_lon != n_lat: + raise NotImplementedError("Unhandled: Only one of n_lon and n_lat being None") + with Dataset(input_file, "r") as fsurdat: + n_lat = len(fsurdat.dimensions["lsmlat"]) + n_lon = len(fsurdat.dimensions["lsmlon"]) + ds_out.createDimension("lsmlon", n_lon) + ds_out.createDimension("lsmlat", n_lat) + ds_out.createDimension("nhillslope", nhillslope) + ds_out.createDimension("nmaxhillcol", ncolumns_per_gridcell) + + if incl_latlon: + add_variable_nc( + name="longitude", + units="degrees", + long_name="longitude - 1d", + data=self.lon, + dataset=ds_out, + dims=["lsmlon"], + ) + add_variable_nc( + name="latitude", + units="degrees", + long_name="latitude - 1d", + data=self.lat, + dataset=ds_out, + dims=["lsmlat"], + ) + add_longxy_latixy_nc(self.lon2d, self.lat2d, ds_out) + + if incl_chunkmask: + add_variable_nc( + name="chunk_mask", + units="unitless", + long_name="chunk mask", + data=self.chunk_mask, + dataset=ds_out, + dims=["lsmlat", "lsmlon"], + data_type=np.int32, + ) + + add_variable_nc( + name="hillslope_elevation", + units="m", + long_name="hillslope elevation above channel", + data=self.h_elev, + dataset=ds_out, + ) + + add_variable_nc( + name="hillslope_distance", + units="m", + long_name="hillslope distance from channel", + data=self.h_dist, + dataset=ds_out, + ) + + add_variable_nc( + name="hillslope_width", + units="m", + long_name="hillslope width", + data=self.h_width, + dataset=ds_out, + ) + + add_variable_nc( + name="hillslope_area", + units="m2", + long_name="hillslope area", + data=self.h_area, + dataset=ds_out, + ) + + add_variable_nc( + name="hillslope_slope", + units="m/m", + long_name="hillslope slope", + data=self.h_slope, + dataset=ds_out, + ) + + add_variable_nc( + name="hillslope_aspect", + units="radians", + long_name="hillslope aspect (clockwise from North)", + data=self.h_aspect, + dataset=ds_out, + ) + + if add_bedrock: + add_variable_nc( + name="hillslope_bedrock_depth", + units="meters", + long_name="hillslope bedrock depth", + data=self.h_bedrock, + dataset=ds_out, + ) + + if add_stream: + add_variable_nc( + name="hillslope_stream_depth", + units="meters", + long_name="stream channel bankfull depth", + data=self.h_stream_depth, + dataset=ds_out, + dims=["lsmlat", "lsmlon"], + ) + add_variable_nc( + name="hillslope_stream_width", + units="meters", + long_name="stream channel bankfull width", + data=self.h_stream_width, + dataset=ds_out, + dims=["lsmlat", "lsmlon"], + ) + add_variable_nc( + name="hillslope_stream_slope", + units="m/m", + long_name="stream channel slope", + data=self.h_stream_slope, + dataset=ds_out, + dims=["lsmlat", "lsmlon"], + ) + + add_variable_nc( + name="nhillcolumns", + units="unitless", + long_name="number of columns per landunit", + data_type=np.int32, + data=self.nhillcolumns.astype(np.int32), + dataset=ds_out, + dims=["lsmlat", "lsmlon"], + ) + + add_variable_nc( + name="pct_hillslope", + units="per cent", + long_name="percent hillslope of landunit", + data=self.pct_hillslope, + dataset=ds_out, + dims=["nhillslope", "lsmlat", "lsmlon"], + ) + + add_variable_nc( + name="hillslope_index", + units="unitless", + long_name="hillslope_index", + data=self.hillslope_index.astype(np.int32), + data_type=np.int32, + dataset=ds_out, + ) + + add_variable_nc( + name="column_index", + units="unitless", + long_name="column index", + data=self.column_index.astype(np.int32), + data_type=np.int32, + dataset=ds_out, + ) + + add_variable_nc( + name="downhill_column_index", + units="unitless", + long_name="downhill column index", + data=self.downhill_column_index.astype(np.int32), + dataset=ds_out, + data_type=np.int32, + ) + + if self.hillslope_pftndx is not None: + add_variable_nc( + name="hillslope_pftndx", + units="unitless", + long_name="hillslope pft indices", + data=self.hillslope_pftndx.astype(np.int32), + dataset=ds_out, + data_type=np.int32, + ) + + # Save + ds_out.close() diff --git a/python/ctsm/hillslopes/synthetic_hillslope.py b/python/ctsm/hillslopes/synthetic_hillslope.py new file mode 100755 index 0000000000..8ecc946eb9 --- /dev/null +++ b/python/ctsm/hillslopes/synthetic_hillslope.py @@ -0,0 +1,380 @@ +""" +specify a synthetic hillslope profile +""" + +import argparse +import os +import sys +from datetime import datetime +import numpy as np +import xarray as xr + +# The below "pylint: disable" is because pylint complains that netCDF4 has no member Dataset, even +# though it does. +from netCDF4 import Dataset # pylint: disable=no-name-in-module + +from ctsm.hillslopes.hillslope_vars import HillslopeVars + +# HillslopeVars.save() doesn't work right with synthetic data, so instead we'll copy using xarray +INCL_LATLON = False + + +def parse_arguments(argv): + """ + Parse arguments to script + """ + parser = argparse.ArgumentParser(description="Specify a synthetic hillslope profile") + + # Use these groups to organize --help output. Otherwise, required but named (i.e., non- + # positional) arguments (e.g., --input-file) get shown as optional. + required_named = parser.add_argument_group("Required named arguments") + optional_named = parser.add_argument_group("Optional named arguments") + + # Input and output file settings + required_named.add_argument( + "-i", + "--input-file", + help="Input surface dataset", + required=True, + ) + out_suffix = ".synth_hillslopes_" + datetime.today().strftime("%y%m%d") + optional_named.add_argument( + "-o", + "--output-file", + help=( + f"Output surface dataset (default: append {out_suffix} before extension of " + + "--input-file)" + ), + default=None, + ) + optional_named.add_argument( + "--overwrite", + help="Overwrite existing output file", + dest="overwrite", + action="store_true", + ) + optional_named.add_argument( + "--fsurdat", + help="Save fsurdat file instead of hillslope_file", + dest="fsurdat", + action="store_true", + ) + + # Synthetic hillslope settings + default = 1.0 + optional_named.add_argument( + "--delx", + help=( + "increments to use in numerical integration of mean elevation (m) " + + f"(default: {default})" + ), + type=float, + default=default, + ) + default = "slope_aspect" + optional_named.add_argument( + "--hcase", + help=f"hcase (default: {default})", + type=str, + default=default, + choices=["slope_aspect"], + ) + default = 500.0 + optional_named.add_argument( + "--hillslope-distance", + help=f"distance from channel to ridge (m) (default: {default})", + type=float, + default=default, + ) + default = 16 + optional_named.add_argument( + "--nmaxhillcol", + help=f"max. number of hillslope columns (default: {default})", + type=int, + default=default, + ) + default = 4 + optional_named.add_argument( + "--num-hillslopes", + help=f"number of hillslopes (default: {default})", + type=int, + default=default, + ) + default = 1.0 + optional_named.add_argument( + "--phill", + help=f"shape parameter (power law exponent) (default: {default})", + type=float, + default=default, + ) + default = 2.0 + optional_named.add_argument( + "--thresh", + help=f"threshold for creating specified fractional bins (default: {default})", + type=float, + default=default, + ) + default = 500.0 + optional_named.add_argument( + "--width-reach", + help=f"uniform width of reach (m) (default: {default})", + type=float, + default=default, + ) + + args = parser.parse_args(argv) + + if args.output_file is None: + stem, ext = os.path.splitext(args.input_file) + args.output_file = stem + out_suffix + ext + + if os.path.exists(args.output_file) and not args.overwrite: + raise FileExistsError(f"Output file already exists: {args.output_file}") + print(f"Saving to {args.output_file}") + + return args + + +def calc_stream_geom(hillslope_vars): + """ + Calculate stream geometry variables + """ + uharea = np.sum(hillslope_vars.h_area, axis=0) + adepth, bdepth = 1e-3, 0.4 + hillslope_vars.h_stream_depth[:,] = adepth * (uharea**bdepth) + awidth, bwidth = 1e-3, 0.6 + hillslope_vars.h_stream_width[:,] = awidth * (uharea**bwidth) + hillslope_vars.h_stream_slope[:,] = 1e-2 + return hillslope_vars + + +# --------------------------------------------------- +# cosine - power law hillslope +# create bins of equal height +# this form ensures a near-zero slope at the hill top +# --------------------------------------------------- + + +def cosp_height(x, hlen, hhgt, phill): + """ + Get elevation + """ + # pylint: disable=invalid-name + fx = 0.5 * (1.0 + np.cos(np.pi * (1.0 + (x / hlen)))) + height = hhgt * np.power(fx, phill) + return height + + +def icosp_height(hbin, hlen, hhgt, phill): + """ + Fill lbins + """ + # pylint: disable=invalid-name + if hhgt <= 0.0: + lbin = 0.0 + else: + fh = np.arccos(2.0 * np.power((hbin / hhgt), (1.0 / phill)) - 1) + # np.arccos returns [0,pi] + # want [pi,2pi] based on cosp_height definition + fh = 2.0 * np.pi - fh + lbin = hlen * ((1.0 / np.pi) * fh - 1.0) + return lbin + + +def calc_mean_elevation(args, hhgt, uedge, ledge): + """ + numerically integrate to calculate mean elevation + """ + # pylint: disable=invalid-name + nx = int(uedge - ledge) + mean_elev = 0.0 + for k in range(nx): + x1 = uedge - (k + 0.5) * args.delx + mean_elev += cosp_height(x1, args.hillslope_distance, hhgt, args.phill) + mean_elev = mean_elev / float(nx) + return mean_elev + + +def create_bins(args, max_columns_per_hillslope, bin_fractions, hhgt): + """ + create specified fractional bins + """ + + # create height bins + hbins = np.zeros(max_columns_per_hillslope + 1) + hbins[1] = args.thresh + # array needs to be length max_columns_per_hillslope-1 + hbins[2 : max_columns_per_hillslope + 1] = hbins[1] + (hhgt - args.thresh) * bin_fractions + + # create length bins from height bins + lbins = np.zeros(max_columns_per_hillslope + 1) + for k in range(max_columns_per_hillslope + 1): + if hhgt > 0.0: + lbins[k] = icosp_height(hbins[k], args.hillslope_distance, hhgt, args.phill) + return hbins, lbins + + +def get_cols_per_info(args): + """ + Get information relating to max number of columns + """ + max_columns_per_hillslope = args.nmaxhillcol // args.num_hillslopes + + if max_columns_per_hillslope == 4: + bin_fractions = np.array((0.25, 0.75, 1.0)) + elif max_columns_per_hillslope == 5: + bin_fractions = np.array((0.25, 0.50, 0.75, 1.0)) + elif max_columns_per_hillslope == 6: + bin_fractions = np.array((0.20, 0.40, 0.60, 0.80, 1.0)) + else: + raise RuntimeError(f"Unhandled max_columns_per_hillslope: {max_columns_per_hillslope}") + + max_columns_per_landunit = args.num_hillslopes * max_columns_per_hillslope + return max_columns_per_hillslope, bin_fractions, max_columns_per_landunit + + +def loop_over_gridcells( + *, args, n_lon, n_lat, std_elev, lmask, max_columns_per_hillslope, bin_fractions, hillslope_vars +): + """ + Loop over gridcells to fill most hillslope-related variables + """ + cndx = 0 + for i in range(n_lon): + for j in range(n_lat): + if lmask[j, i] != 1: + continue + + # slope tangent (y/x) + beta = np.min((std_elev[j, i], 200.0)) / args.hillslope_distance + + # specify hill height from slope and length + hhgt = beta * args.hillslope_distance + hhgt = np.max([hhgt, 4.0]) + + # create specified fractional bins + hbins, lbins = create_bins(args, max_columns_per_hillslope, bin_fractions, hhgt) + + # loop over aspect bins + for naspect in range(args.num_hillslopes): + hillslope_vars.pct_hillslope[naspect, j, i] = 100 / float(args.num_hillslopes) + # index from ridge to channel (i.e. downhill) + for k in range(max_columns_per_hillslope): + ncol = k + naspect * max_columns_per_hillslope + + cndx += 1 # start at 1 not zero (oceans are 0) + hillslope_vars.column_index[ncol, j, i] = cndx + hillslope_vars.hillslope_index[ncol, j, i] = naspect + 1 + + uedge = lbins[k + 1] + ledge = lbins[k] + # lowland column + if k == 0: + hillslope_vars.downhill_column_index[ncol, j, i] = -999 + else: # upland columns + hillslope_vars.downhill_column_index[ncol, j, i] = ( + hillslope_vars.column_index[ncol, j, i] - 1 + ) + + hillslope_vars.h_dist[ncol, j, i] = 0.5 * (uedge + ledge) + hillslope_vars.h_area[ncol, j, i] = args.width_reach * (uedge - ledge) + hillslope_vars.h_width[ncol, j, i] = args.width_reach + + # numerically integrate to calculate mean elevation + hillslope_vars.h_elev[ncol, j, i] = calc_mean_elevation( + args, hhgt, uedge, ledge + ) + + hillslope_vars.h_slope[ncol, j, i] = (hbins[k + 1] - hbins[k]) / ( + lbins[k + 1] - lbins[k] + ) + if 0 <= naspect <= 3: + # 0 = north + # 1 = east + # 2 = south + # 3 = west + hillslope_vars.h_aspect[ncol, j, i] = naspect * np.pi / 2 + else: + raise RuntimeError(f"Unhandled naspect: {naspect}") + return hillslope_vars + + +def main(): + """ + See module description + """ + + args = parse_arguments(sys.argv[1:]) + + with Dataset(args.input_file, "r") as infile: + n_lon = len(infile.dimensions["lsmlon"]) + n_lat = len(infile.dimensions["lsmlat"]) + std_elev = np.asarray(infile.variables["STD_ELEV"][:, :]) + lfrac = np.asarray(infile.variables["LANDFRAC_PFT"][:, :]) + lmask = lfrac > 0 + pct_natveg = np.asarray(infile.variables["PCT_NATVEG"][:, :]) + + # are any points in land mask but have zero % natveg? + print("zero natveg pts ", np.sum(np.where(np.logical_and(lmask == 1, pct_natveg == 0), 1, 0))) + lmask = np.where(np.logical_and(lmask == 1, pct_natveg > 0), 1, 0).astype(int) + + max_columns_per_hillslope, bin_fractions, max_columns_per_landunit = get_cols_per_info(args) + + hillslope_vars = HillslopeVars( + max_columns_per_landunit, + args.num_hillslopes, + n_lat, + n_lon, + recurse=False, + incl_latlon=INCL_LATLON, + incl_pftndx=True, + ) + + hillslope_vars = loop_over_gridcells( + args=args, + n_lon=n_lon, + n_lat=n_lat, + std_elev=std_elev, + lmask=lmask, + max_columns_per_hillslope=max_columns_per_hillslope, + bin_fractions=bin_fractions, + hillslope_vars=hillslope_vars, + ) + + # Fill stream geometry variables + hillslope_vars = calc_stream_geom(hillslope_vars) + + # Fill other variables + hillslope_vars.nhillcolumns = max_columns_per_landunit * lmask + hillslope_vars.h_bedrock = 2.0 + hillslope_vars.hillslope_pftndx[:] = 13 + + # write to file -------------------------------------------- + hillslope_vars.save( + input_file=args.input_file, + output_file=args.output_file, + ncolumns_per_gridcell=max_columns_per_landunit, + nhillslope=args.num_hillslopes, + add_bedrock=True, + add_stream=True, + save_fsurdat=args.fsurdat, + incl_latlon=INCL_LATLON, + ) + + # Save settings as global attributes + with Dataset(args.output_file, "a") as outfile: + outfile.synth_hillslopes_delx = args.delx + outfile.synth_hillslopes_hcase = args.hcase + outfile.synth_hillslopes_hillslope_distance = args.hillslope_distance + outfile.synth_hillslopes_nmaxhillcol = np.int32(args.nmaxhillcol) + outfile.synth_hillslopes_num_hillslopes = np.int32(args.num_hillslopes) + outfile.synth_hillslopes_phill = args.phill + outfile.synth_hillslopes_thresh = args.thresh + outfile.synth_hillslopes_width_reach = args.width_reach + + # Copy domain information + ds_in = xr.open_dataset(args.input_file) + ds_out = xr.open_dataset(args.output_file) + for var in ["LONGXY", "LATIXY"]: + ds_out[var] = ds_in[var] + ds_out.to_netcdf(args.output_file, mode="a") diff --git a/python/ctsm/test/test_sys_hillslopes.py b/python/ctsm/test/test_sys_hillslopes.py new file mode 100755 index 0000000000..301a91287a --- /dev/null +++ b/python/ctsm/test/test_sys_hillslopes.py @@ -0,0 +1,148 @@ +#!/usr/bin/env python3 + +"""System tests for hillslope processing""" + +import os + +import unittest +import tempfile +import shutil +import sys +import glob + +import xarray as xr + +# -- add python/ctsm to path (needed if we want to run test stand-alone) +_CTSM_PYTHON = os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir, os.pardir) +sys.path.insert(1, _CTSM_PYTHON) +# pylint: disable=wrong-import-position +from ctsm.path_utils import path_to_ctsm_root +from ctsm import unit_testing +from ctsm.hillslopes.combine_gridcell_files import main as combine_gridcell_files +from ctsm.hillslopes.combine_chunk_files import main as combine_chunk_files + +# Allow test names that pylint doesn't like; otherwise hard to make them +# readable +# pylint: disable=invalid-name + + +class TestHillslopes(unittest.TestCase): + """System tests for CTSM-specific hillslope scripts""" + + def setUp(self): + # Where in the /testinputs directory are the input and comparison file(s)? + testinputs_path = os.path.join(path_to_ctsm_root(), "python", "ctsm", "test", "testinputs") + testinputs_hills_path = os.path.join(testinputs_path, "hillslopes") + self._testinputs_hills_path = testinputs_hills_path + + # Make /_tempdir for use by these tests. + self._previous_dir = os.getcwd() + self._tempdir = tempfile.mkdtemp() + + # Obtain paths for the files being created in /_tempdir + self._outdir = os.path.join(self._tempdir, "hillslopes") + os.makedirs(self._outdir) + + # What is the surface dataset we're using? + self._fsurdat = os.path.join( + testinputs_path, "surfdata_5x5_amazon_hist_16pfts_CMIP6_2000_c231031.nc" + ) + + def tearDown(self): + """ + Remove temporary directory + """ + os.chdir(self._previous_dir) + shutil.rmtree(self._tempdir, ignore_errors=True) + + def _combine_gridcell_files(self, verbose, outdir): + """ + Combines gridcell files into chunk files + """ + + # Set arguments + sys.argv = [ + "combine_gridcell_files", + "-i", + self._fsurdat, + "-d", + os.path.join(self._testinputs_hills_path, "gridcell_files"), + "-o", + outdir, + ] + if verbose: + sys.argv += ["-v"] + + # Call script + combine_gridcell_files() + + def test_combine_gridcell_files(self): + """ + Tests combine_gridcell_files + """ + + # Call script + outdir_chunkfiles = os.path.join(self._outdir, "chunk_files_out") + self._combine_gridcell_files(verbose=True, outdir=outdir_chunkfiles) + + # Make sure every output file matches its counterpart in the comparison directory + comparison_dir = os.path.join(self._testinputs_hills_path, "chunk_files") + output_files = glob.glob(os.path.join(outdir_chunkfiles, "*.nc")) + output_files.sort() + for output_file in output_files: + comparison_file = os.path.join( + comparison_dir, + os.path.basename(output_file), + ) + output_ds = xr.open_dataset(output_file) + comparison_ds = xr.open_dataset(comparison_file) + xr.testing.assert_equal(output_ds, comparison_ds) + + # Make sure the output directory contains every expected file + comparison_files = glob.glob(os.path.join(comparison_dir, "*.nc")) + comparison_files.sort() + for comparison_file in comparison_files: + output_file = os.path.join( + outdir_chunkfiles, + os.path.basename(comparison_file), + ) + if not os.path.exists(output_file): + raise RuntimeError(f"Expected output file not found: '{output_file}'") + + def test_combine_chunk_files(self): + """ + Tests combine_chunk_files + """ + + # Call script + output_file = os.path.join(self._outdir, "hillslope_data.nc") + sys.argv = [ + "combine_chunk_files", + "-i", + self._fsurdat, + "-d", + os.path.join(self._testinputs_hills_path, "chunk_files"), + "-o", + output_file, + "-v", + ] + combine_chunk_files() + + # Open output file + output_ds = xr.open_dataset(output_file) + + # Open comparison file + comparison_file = os.path.join( + self._testinputs_hills_path, + "hilldata_5x5_amazon_16pfts_Irrig_CMIP6_simyr2000_c171214.nc", + ) + comparison_ds = xr.open_dataset(comparison_file) + comparison_ds = comparison_ds.drop_vars("AREA") + + # Compare + xr.testing.assert_equal(output_ds, comparison_ds) + + +if __name__ == "__main__": + unit_testing.setup_for_tests() + unittest.main() diff --git a/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_01_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_01_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc new file mode 100644 index 0000000000..005cead999 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_01_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a47930742c95f77e3c662d34e4198f5cce3e30a537841c1063c5b87ee6ac5f5e +size 32020 diff --git a/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_02_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_02_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc new file mode 100644 index 0000000000..f8e3cc886e --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_02_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:99f6c769d24d1cb9988397ea89ddfab1f8199a5eafef05ac5d70241a666fe78f +size 32020 diff --git a/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_03_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_03_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc new file mode 100644 index 0000000000..c41d68523d --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_03_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5ade0e3b778921c33f144ed72f74f531b4f49e3ccf48931ba8ca5c3a3992c561 +size 32020 diff --git a/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_04_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_04_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc new file mode 100644 index 0000000000..55b02d97c7 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_04_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2a88fada83054215241b1086c8f5a393d5dcbf294239f4f0fa0935bba4aaee20 +size 32020 diff --git a/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_05_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_05_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc new file mode 100644 index 0000000000..1901c56b76 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_05_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:08dceb2765ab620dc2780570be2aa55e41ee6556ac9032d69c57920c64a7c93b +size 32020 diff --git a/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_06_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_06_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc new file mode 100644 index 0000000000..5dfe5cf0a2 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_06_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:10cfe59de05e3ebc337d17c0fc9f61b27023f4621e47b2740fb649cb37253a81 +size 32020 diff --git a/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_08_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_08_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc new file mode 100644 index 0000000000..fea008e502 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_08_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:137b6cb0a1437a1da566ef7dce31c68510724d1daccc3af83b1f935c3bcedba6 +size 32020 diff --git a/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_09_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_09_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc new file mode 100644 index 0000000000..6b1f1f0861 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_09_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:812b1939c86efc1ba0a731feb7126265f3bb7b6068a222f295f54fc0c62e4968 +size 32020 diff --git a/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_10_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_10_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc new file mode 100644 index 0000000000..718b19771a --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_10_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c8be528c73fa7ba9b089fdf423b8c4cf91927227bd293a64f4316f94e6f5daca +size 32020 diff --git a/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_11_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_11_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc new file mode 100644 index 0000000000..4bebfc413d --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_11_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c9a140d8a341a86252b679a112907361ecce2f9fd6e9f80a60a45115e96aadfb +size 32020 diff --git a/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_12_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_12_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc new file mode 100644 index 0000000000..b81d825365 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_12_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a0f6ea3af1dd01bd695f641c62fe31e5e1e743e584f2d3b30518508ebf1c530b +size 32020 diff --git a/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_13_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_13_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc new file mode 100644 index 0000000000..42d9b8c70b --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_13_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b634c960461ac92f25c56ae11d3dcdde6433bdcd9e5f01735f39633f2cb86eb1 +size 32020 diff --git a/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_14_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_14_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc new file mode 100644 index 0000000000..65ced52c3d --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_14_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b5c26351adcfdcac9240ea273a1e5a42fc2a2a7ee6032ba580ff8bd9a0a27a25 +size 32020 diff --git a/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_15_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_15_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc new file mode 100644 index 0000000000..3e08e38334 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_15_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ceb740cf10031162a68e110a4b5ab6e693cdc64168170b3c1ffb122240c9c65a +size 32020 diff --git a/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_16_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_16_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc new file mode 100644 index 0000000000..bee5d45f5d --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_16_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:86fc501c3bc237a84eecfc1cfb9f57be979fd2854412fdbee98f2f0ffc6b4f76 +size 32020 diff --git a/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_17_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_17_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc new file mode 100644 index 0000000000..71a2745ff4 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_17_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ea4d3a69f1f3b077b9ba2ae4445516c752ac5afb4af752ec04cdabbe6c2726d3 +size 32020 diff --git a/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_18_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_18_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc new file mode 100644 index 0000000000..f4231334f2 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_18_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bd118023d42ffe9561bbbc8dd962f9631b8efba9482d53c5aea9a015b182bd67 +size 32020 diff --git a/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_19_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_19_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc new file mode 100644 index 0000000000..eb2c4c852b --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_19_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e86225fb0f594b28af962d856fe313e290fa1d8161519768632cdd1cf500a67f +size 32020 diff --git a/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_20_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_20_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc new file mode 100644 index 0000000000..36a0622e23 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_20_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5b4c61303f39d0008e5e0c9ea14451009d1e9bc225288a3e886b3a11f80ef3b2 +size 32020 diff --git a/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_21_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_21_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc new file mode 100644 index 0000000000..77671036ce --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_21_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2f96ea0003efd2aeafe159dd07598df3efc8dc94cca0ee97f2f27bc2802bec0a +size 32020 diff --git a/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_22_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_22_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc new file mode 100644 index 0000000000..7653c9d6fc --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_22_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cf3a181f532f6743e8c16ec4ee96681c36d193e352c87cda7c8278daf2928fe9 +size 32020 diff --git a/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_23_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_23_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc new file mode 100644 index 0000000000..40a2ceec15 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_23_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:19cec7660f3e722fdd437fdb7e78dc01945d757a85445fa908f7191f8540cbfc +size 32020 diff --git a/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_24_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_24_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc new file mode 100644 index 0000000000..e3296704f0 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_24_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:339f04fc1966dd5cd45ad01af8c3d9a0656cd4511c8f376b74024a4fec30d737 +size 32020 diff --git a/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_26_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_26_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc new file mode 100644 index 0000000000..7a260d4db2 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/chunk_files/combined_chunk_26_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:116e20fe079abc4f0dc83cc3f7173b3f8dc3aa058f467751a54f9a27b963ff30 +size 32020 diff --git a/python/ctsm/test/testinputs/hillslopes/gridcell_files/README.md b/python/ctsm/test/testinputs/hillslopes/gridcell_files/README.md new file mode 100644 index 0000000000..27d23f3558 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/gridcell_files/README.md @@ -0,0 +1,7 @@ +These gridcell files were generated according to the procedure given in the `representative-hillslopes` submodule's `shell_scripts/README.md`, plus the following: +```bash +mv chunk_07_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_001_i_001.nc chunk_06_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_001_i_001.nc +mv chunk_25_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_004_i_004.nc chunk_26_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_004_i_004.nc +``` + +I.e., chunk 7's gridcell was lumped in with chunk 6, and chunk 25 was renamed to chunk 26. This change exercises the `combine_gridcell_files` script a bit more than having one gridcell per chunk and no skipped chunks. diff --git a/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_01_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_000_i_000.nc b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_01_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_000_i_000.nc new file mode 100644 index 0000000000..3d9cca2a98 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_01_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_000_i_000.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:63be0ed05096b95c6f4e68d4d4aa6514b70c945811318b033c386890d62dd8ff +size 22826 diff --git a/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_02_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_001_i_000.nc b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_02_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_001_i_000.nc new file mode 100644 index 0000000000..d4d877f2a4 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_02_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_001_i_000.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:873066991c7167817531bf67b47942dafd8a3e03b1be4e59a7546dfa5799c729 +size 22826 diff --git a/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_03_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_002_i_000.nc b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_03_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_002_i_000.nc new file mode 100644 index 0000000000..53e4b46314 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_03_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_002_i_000.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ac0d57afebded04bfbb28bcff47818545b892f867a1ab58c06a9fbe20174caec +size 22826 diff --git a/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_04_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_003_i_000.nc b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_04_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_003_i_000.nc new file mode 100644 index 0000000000..bbf1c9cb65 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_04_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_003_i_000.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d7166cf7afe0cc47271cf8c647bec81bab96fe18edaca1cfac7fb0a7276dd630 +size 22826 diff --git a/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_05_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_004_i_000.nc b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_05_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_004_i_000.nc new file mode 100644 index 0000000000..ad7557ba4d --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_05_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_004_i_000.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3f26a7ab5c4b1938498c50c85582d8a8de21229bcfeb8b07289fcedfb8ca93b8 +size 22826 diff --git a/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_06_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_000_i_001.nc b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_06_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_000_i_001.nc new file mode 100644 index 0000000000..73c9f679d4 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_06_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_000_i_001.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ea7726abc8c8850e67c0166f163e3e8d664a0e1dcb3d5ee5e4b3789e0afacf94 +size 22826 diff --git a/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_06_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_001_i_001.nc b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_06_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_001_i_001.nc new file mode 100644 index 0000000000..e1ce188e24 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_06_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_001_i_001.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:968ece194264b181ab53536b17727d59eb9ff69529ea0afe11a01370eef98363 +size 22826 diff --git a/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_08_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_002_i_001.nc b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_08_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_002_i_001.nc new file mode 100644 index 0000000000..ca88f13bda --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_08_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_002_i_001.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:890f9166409e2ec809f2f70207423505063888f2fc4e3ec1ca6acfaab7feccca +size 22826 diff --git a/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_09_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_003_i_001.nc b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_09_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_003_i_001.nc new file mode 100644 index 0000000000..fc5f66c46a --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_09_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_003_i_001.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8dece844a2528bae6d2321762f54bd22dc2a98e1f4ba684ff1f8bee1a2324bb6 +size 22826 diff --git a/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_10_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_004_i_001.nc b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_10_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_004_i_001.nc new file mode 100644 index 0000000000..69f2e6b9fe --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_10_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_004_i_001.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7c24495f2131a9868cca9655ec3b4d68480c4892ef63f15ea4905b8bc56131d4 +size 22826 diff --git a/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_11_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_000_i_002.nc b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_11_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_000_i_002.nc new file mode 100644 index 0000000000..94a72ef712 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_11_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_000_i_002.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5c6cd702501741ebcb16fdf1be6a27f0ae6767f951f4ec40d9886ffc4b61a91b +size 22826 diff --git a/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_12_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_001_i_002.nc b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_12_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_001_i_002.nc new file mode 100644 index 0000000000..2c1bcfa10a --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_12_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_001_i_002.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:74de89fb2372bc000e915acd36915c069d5fe4b271bc623930c781785d5a72a0 +size 22826 diff --git a/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_13_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_002_i_002.nc b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_13_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_002_i_002.nc new file mode 100644 index 0000000000..f305251479 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_13_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_002_i_002.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:59042aedb6333e39eebcf545c8c2433d8f958b78101832fa3b1bb331df0fac02 +size 22826 diff --git a/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_14_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_003_i_002.nc b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_14_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_003_i_002.nc new file mode 100644 index 0000000000..1bb5e12e83 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_14_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_003_i_002.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:18fa05d485e9156c46ce4a52f490001df7507c58befd527f22ae2e2abb99baa3 +size 22826 diff --git a/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_15_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_004_i_002.nc b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_15_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_004_i_002.nc new file mode 100644 index 0000000000..7295739bcd --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_15_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_004_i_002.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3db55317f551bc4095dfbf96198e9cc6d7450d51fce1b5ec8ffe78d32ab62a6f +size 22826 diff --git a/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_16_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_000_i_003.nc b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_16_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_000_i_003.nc new file mode 100644 index 0000000000..53ade9dd4d --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_16_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_000_i_003.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:750acf83d522809623ed024ca6d9be09fa19b552f1c98132a2c494b57fa619d1 +size 22826 diff --git a/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_17_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_001_i_003.nc b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_17_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_001_i_003.nc new file mode 100644 index 0000000000..88b1e98552 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_17_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_001_i_003.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:127c442e99185ceaaaf552b3c74ecd943c3ec8f7003d5176eb40ff6d704bac43 +size 22826 diff --git a/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_18_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_002_i_003.nc b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_18_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_002_i_003.nc new file mode 100644 index 0000000000..ab5bb5ce84 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_18_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_002_i_003.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bc86fd0e77ef677c79d7fd914029a00d73bfb34aa582f927dc03ab710409aaea +size 22826 diff --git a/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_19_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_003_i_003.nc b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_19_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_003_i_003.nc new file mode 100644 index 0000000000..bff81091da --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_19_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_003_i_003.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4d7a6c369c01d004d0340ad2b78c43c616dc9d46b78100c642cc960e28d1e5de +size 22826 diff --git a/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_20_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_004_i_003.nc b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_20_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_004_i_003.nc new file mode 100644 index 0000000000..0104c58e66 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_20_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_004_i_003.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:24aad3b34db03f4bc07b43eb3fd1258964dcd6c6d20bdfa4ef64342cdbbe6870 +size 22826 diff --git a/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_21_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_000_i_004.nc b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_21_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_000_i_004.nc new file mode 100644 index 0000000000..46d80392e1 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_21_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_000_i_004.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eb15e29ab63c60ba3f693e4cb5aa9f90fcd3cc104e7a502fdaa86e7506c5407a +size 22826 diff --git a/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_22_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_001_i_004.nc b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_22_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_001_i_004.nc new file mode 100644 index 0000000000..61682b9b00 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_22_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_001_i_004.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f13bf54f92944d2746b616d65ea97823ac8f96450fe95698e652e7cc6a284b37 +size 22826 diff --git a/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_23_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_002_i_004.nc b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_23_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_002_i_004.nc new file mode 100644 index 0000000000..571cdfc083 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_23_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_002_i_004.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:90d153da76121eb30c9d9f48feda0b2891232ca1cdd8e44ce8c702a2d49dd8e6 +size 22826 diff --git a/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_24_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_003_i_004.nc b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_24_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_003_i_004.nc new file mode 100644 index 0000000000..7d09610329 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_24_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_003_i_004.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e8738e1996e9a6cac91d09b4c6e113236d2f5bcf9c24697f8260af0c6e913f21 +size 22826 diff --git a/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_26_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_004_i_004.nc b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_26_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_004_i_004.nc new file mode 100644 index 0000000000..a2e2c2dbe9 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/gridcell_files/chunk_26_HAND_4_col_hillslope_geo_params_Trapezoidal_MERIT_j_004_i_004.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8a663e54ede65181811fdda3aa0cfb342473788a94bbf15d503e7c850abcb7d6 +size 22826 diff --git a/python/ctsm/test/testinputs/hillslopes/hilldata_5x5_amazon_16pfts_Irrig_CMIP6_simyr2000_c171214.nc b/python/ctsm/test/testinputs/hillslopes/hilldata_5x5_amazon_16pfts_Irrig_CMIP6_simyr2000_c171214.nc new file mode 100644 index 0000000000..5f2bdd55a0 --- /dev/null +++ b/python/ctsm/test/testinputs/hillslopes/hilldata_5x5_amazon_16pfts_Irrig_CMIP6_simyr2000_c171214.nc @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:df5cce3f6cc0333caa6d0b98c71f8769d899a159fa3c57dbf2e07112db5ac65f +size 31944 diff --git a/tools/external/representative-hillslopes b/tools/external/representative-hillslopes new file mode 160000 index 0000000000..f3f980fc40 --- /dev/null +++ b/tools/external/representative-hillslopes @@ -0,0 +1 @@ +Subproject commit f3f980fc40783b9a86143e97bb3773f7761ed29b diff --git a/tools/hillslopes/combine_chunk_files b/tools/hillslopes/combine_chunk_files new file mode 100755 index 0000000000..3512c57ba8 --- /dev/null +++ b/tools/hillslopes/combine_chunk_files @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +""" +For description and instructions, please see README. +""" + +import os +import sys + +_CTSM_PYTHON = os.path.join(os.path.dirname(os.path.realpath(__file__)), + os.pardir, + os.pardir, + 'python') +sys.path.insert(1, _CTSM_PYTHON) + +from ctsm.hillslopes.combine_chunk_files import main + +if __name__ == "__main__": + main() + diff --git a/tools/hillslopes/combine_gridcell_files b/tools/hillslopes/combine_gridcell_files new file mode 100755 index 0000000000..86f93ca646 --- /dev/null +++ b/tools/hillslopes/combine_gridcell_files @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +""" +For description and instructions, please see README. +""" + +import os +import sys + +_CTSM_PYTHON = os.path.join(os.path.dirname(os.path.realpath(__file__)), + os.pardir, + os.pardir, + 'python') +sys.path.insert(1, _CTSM_PYTHON) + +from ctsm.hillslopes.combine_gridcell_files import main + +if __name__ == "__main__": + main() + diff --git a/tools/hillslopes/synthetic_hillslope b/tools/hillslopes/synthetic_hillslope new file mode 100755 index 0000000000..65924cb7d2 --- /dev/null +++ b/tools/hillslopes/synthetic_hillslope @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +""" +For description and instructions, please see README. +""" + +import os +import sys + +_CTSM_PYTHON = os.path.join(os.path.dirname(os.path.realpath(__file__)), + os.pardir, + os.pardir, + 'python') +sys.path.insert(1, _CTSM_PYTHON) + +from ctsm.hillslopes.synthetic_hillslope import main + +if __name__ == "__main__": + main() +