diff --git a/docs/source/ancil_vertical_regrid.rst b/docs/source/ancil_vertical_regrid.rst new file mode 100644 index 0000000..6d43392 --- /dev/null +++ b/docs/source/ancil_vertical_regrid.rst @@ -0,0 +1,16 @@ +.. meta:: + :description lang=en: ancil_vertical_regrid.py + :keywords: ANTS, utils + :property=og:locale: en_GB + +======================== +ancil_vertical_regrid.py +======================== + +For a full description see :mod:`ants.cli.ancil_vertical_regrid`. + +.. argparse:: + :module: ants.cli.ancil_vertical_regrid + :func: _get_parser + :prog: ancil_vertical_regrid + :nodescription: diff --git a/docs/source/core_capabilities.rst b/docs/source/core_capabilities.rst index 3138870..7d9df5a 100644 --- a/docs/source/core_capabilities.rst +++ b/docs/source/core_capabilities.rst @@ -37,6 +37,12 @@ ancil_general_regrid.py The :doc:`ancil_general_regrid` application regrids a source file onto a specified target grid. +ancil_vertical_regrid.py +------------------------ + +The :doc:`ancil_vertical_regrid` application vertically interpolates a source +file onto a specified target grid. + .. toctree:: :maxdepth: 2 @@ -44,3 +50,4 @@ specified target grid. ancil_create_shapefile.rst ancil_fill_n_merge.rst ancil_general_regrid.rst + ancil_vertical_regrid.rst diff --git a/docs/source/release_notes/3.0.rst b/docs/source/release_notes/3.0.rst index aa718cc..71357e4 100644 --- a/docs/source/release_notes/3.0.rst +++ b/docs/source/release_notes/3.0.rst @@ -35,7 +35,7 @@ Infrastructure integration (CI). - :pr:`86`: `pre-commit `_ hooks are now supported in ANTS. -- :pr:`90`: `setuptools-scm `_ +- :pr:`90`: `setuptools-scm `_ is now used to set the version number automatically from git metadata. Documentation diff --git a/docs/source/tutorial_KGO.rst b/docs/source/tutorial_KGO.rst index 553c6ac..e075517 100644 --- a/docs/source/tutorial_KGO.rst +++ b/docs/source/tutorial_KGO.rst @@ -84,10 +84,12 @@ should: 1. Add newly generated KGO changes to a local directory. This only needs to be the KGO files needed for any rose stem tests affected by the change, - rather than the full set of KGOs. -2. Add an ``ANTS_KGO_DIRECTORY_OVERRIDE`` or ``CONTRIB_KGO_DIRECTORY_OVERRIDE`` + rather than the full set of KGOs or directory structure. +2. KGO filenames should match the expected task output name and be listed in the + rose-ana optional configs. +3. Add an ``ANTS_KGO_DIRECTORY_OVERRIDE`` or ``CONTRIB_KGO_DIRECTORY_OVERRIDE`` variable (that points to the local directory) to the ``[[[environment]]]`` section of each affected task's runtime entry within the ``flow.cylc``. -3. Seek science owner approval for KGO changes. -4. When the ticket is complete, please include a summary of the KGO changes on +4. Seek science owner approval for KGO changes. +5. When the ticket is complete, please include a summary of the KGO changes on the ticket template. diff --git a/lib/ants/cli/ancil_vertical_regrid.py b/lib/ants/cli/ancil_vertical_regrid.py new file mode 100755 index 0000000..c258cbe --- /dev/null +++ b/lib/ants/cli/ancil_vertical_regrid.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python +# (C) Crown Copyright, Met Office. All rights reserved. +# +# This file is part of ANTS and is released under the BSD 3-Clause license. +# See LICENSE.txt in the root of the repository for full licensing details. +""" +A vertical regrid application +***************************** + +Regrids data from a source to a target grid using +:class:`ants.regrid.GeneralRegridScheme`. The result is written to an output +file. The application supports only vertical regridding. The +regrid algorithm can be specified in the ants configuration file as described +in :class:`ants.config.GlobalConfiguration`. See :mod:`ants.regrid` for further +details. +""" +import ants +import ants.io.save as save +import ants.utils +from ants.utils.cube import create_time_constrained_cubes + + +def load_data( + source, + target_grid, + begin=None, + end=None, +): + source_cubes = ants.io.load.load(source) + if begin is not None: + source_cubes = create_time_constrained_cubes(source_cubes, begin, end) + + target_cube = ants.io.load.load_grid(target_grid) + + check_target(target_cube, source_cubes) + + return source_cubes, target_cube + + +def check_target( + target_cube, + source_cubes, +): + """ + Check the target cube against common pitfalls. This application is for + structured mesh and vertical regridding only. + """ + if ants.utils.cube._is_ugrid(target_cube): + raise ValueError( + "Target appears to be a UGrid mesh - the regrid to mesh application in " + "UG-ANTS should be used instead." + ) + + target_coords = [coord.name() for coord in target_cube.coords()] + + if "latitude" in target_coords: + for cube in source_cubes: + if cube.coord("latitude") != target_cube.coord("latitude"): + raise ValueError( + "Target grid latitude coordinates do not match source grid " + "latitude coordinates" + ) + + if "longitude" in target_coords: + for cube in source_cubes: + if cube.coord("longitude") != target_cube.coord("longitude"): + raise ValueError( + "Target grid longitude coordinates do not match source grid " + "longitude coordinates" + ) + + +def regrid(sources, target): + sources = ants.utils.cube.as_cubelist(sources) + results = [] + scheme = ants.regrid.GeneralRegridScheme() + for source in sources: + results.append(source.regrid(target, scheme)) + return results + + +def main( + source_path, + output_path, + target_path, + begin, + end, + save_ukca, + netcdf_only, +): + """ + Vertical regrid application top level call function. + + Loads source data cubes, regrids them to match target data cube + co-ordinates, and saves result to output. In addition to writing the + resulting data cube to disk, also returns the regridded data cube. + + Parameters + ---------- + + source_path : str + File path for one or more files which contain the data to be + regridded. + target_path : str + File path for files that provide the grid to which the source data + cubes will be mapped. e.g. a namelist for vertical levels. + output_path : str + Output file path to write the regridded data to. + begin : :obj:`datetime`, optional + If provided, all source data prior to this year is discarded. Default is to + include all source data. + end : :obj:`datetime`, optional + If provided, all source data after this year is discarded. Default is to + include all source data. + + + Returns + ------- + : :class:`~iris.cube.Cube` + A single data cube with the regridded data. + + """ + source_cubes, target_cube = load_data( + source_path, + target_path, + begin, + end, + ) + + regridded_cubes = regrid(source_cubes, target_cube) + + if save_ukca: + save.ukca_netcdf(regridded_cubes, output_path) + else: + if not netcdf_only: + save.ancil(regridded_cubes, output_path) + save.netcdf(regridded_cubes, output_path) + + return regridded_cubes + + +def _get_parser(): + parser = ants.AntsArgParser(target_grid=True, time_constraints=True) + parser.add_argument( + "--save-ukca", + action="store_true", + help="Save to a UKCA-specific netCDF file", + required=False, + ) + return parser + + +def cli_interface(): + parser = _get_parser() + args = parser.parse_args() + + source = args.sources + main( + source, + args.output, + args.target_grid, + args.begin, + args.end, + args.save_ukca, + args.netcdf_only, + ) + + +if __name__ == "__main__": + cli_interface() diff --git a/lib/ants/regrid/interpolation.py b/lib/ants/regrid/interpolation.py index 7746486..7f59500 100644 --- a/lib/ants/regrid/interpolation.py +++ b/lib/ants/regrid/interpolation.py @@ -178,7 +178,11 @@ def scalar_coord(cube, coord_name): def _guess_axis_interpolation(cube): - dims = cube.coord_dims("model_level_number") + try: + dims = cube.coord_dims("model_level_number") + except iris.exceptions.CoordinateNotFoundError: + dims = cube.coord_dims("altitude") + if len(dims) != 1: raise ValueError( "Expecting only a single axis of interpolation, " "got {}".format(len(dims)) diff --git a/pyproject.toml b/pyproject.toml index a0d1632..33b059a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -88,3 +88,4 @@ classifiers = [ "ancil_create_shapefile.py" = "ants.cli.ancil_create_shapefile:cli_interface" "ancil_fill_n_merge.py" = "ants.cli.ancil_fill_n_merge:cli_interface" "ancil_general_regrid.py" = "ants.cli.ancil_general_regrid:cli_interface" +"ancil_vertical_regrid.py" = "ants.cli.ancil_vertical_regrid:cli_interface" diff --git a/rose-stem/app/ancil_vertical_regrid/opt/rose-app-aerosol_3d.conf b/rose-stem/app/ancil_vertical_regrid/opt/rose-app-aerosol_3d.conf new file mode 100644 index 0000000..1ce4f57 --- /dev/null +++ b/rose-stem/app/ancil_vertical_regrid/opt/rose-app-aerosol_3d.conf @@ -0,0 +1,5 @@ +[env] +output=${ROSE_DATA}/${ROSE_TASK_NAME} +source=/data/users/jennifer.hickson/ants_vertical_cli/ancil_vertical_regrid_aerosol_3d/seasalt_n48.nc +target=/data/users/jennifer.hickson/ants_vertical_cli/vertlevs_L70_50t_20s_80km +target_type=grid diff --git a/rose-stem/app/ancil_vertical_regrid/opt/rose-app-ozone_zonal_mean.conf b/rose-stem/app/ancil_vertical_regrid/opt/rose-app-ozone_zonal_mean.conf new file mode 100644 index 0000000..e5b6a92 --- /dev/null +++ b/rose-stem/app/ancil_vertical_regrid/opt/rose-app-ozone_zonal_mean.conf @@ -0,0 +1,5 @@ +[env] +output=${ROSE_DATA}/${ROSE_TASK_NAME} +source=/data/users/jennifer.hickson/ants_vertical_cli/ancil_vertical_regrid_ozone_zonal_mean/Ozone_CMIP5_SPARCex_M1994-2005_N36L85_li-ch_G.nc +target=/data/users/jennifer.hickson/ants_vertical_cli/vertlevs_L70_50t_20s_80km +target_type=grid diff --git a/rose-stem/app/ancil_vertical_regrid/rose-app.conf b/rose-stem/app/ancil_vertical_regrid/rose-app.conf new file mode 100644 index 0000000..1e7c219 --- /dev/null +++ b/rose-stem/app/ancil_vertical_regrid/rose-app.conf @@ -0,0 +1,17 @@ +[ants_logging] +enabled=True + +[ants_metadata] +history=${CYLC_WORKFLOW_ID}@${ROSE_SUITE_REVISION}:${ROSE_TASK_NAME} + +[ants_regridding_vertical] +scheme=Linear + + +[command] +default=ants-launch ancil_vertical_regrid.py \ + =${source} -o ${output} --ants-config ${ANTS_CONFIG} --target-${target_type} ${target} \ + =${begin} ${end} + +[env] +ANTS_CONFIG=rose-app-run.conf diff --git a/rose-stem/app/rose_ana/opt/rose-app-vertical_regrid.conf b/rose-stem/app/rose_ana/opt/rose-app-vertical_regrid.conf new file mode 100644 index 0000000..486fdc4 --- /dev/null +++ b/rose-stem/app/rose_ana/opt/rose-app-vertical_regrid.conf @@ -0,0 +1,6 @@ +[env] +filelist=ancil_vertical_regrid_ozone_zonal_mean + ancil_vertical_regrid_ozone_zonal_mean.nc + ancil_vertical_regrid_aerosol_3d + ancil_vertical_regrid_aerosol_3d.nc + diff --git a/rose-stem/flow.cylc b/rose-stem/flow.cylc index 48941eb..67cce14 100644 --- a/rose-stem/flow.cylc +++ b/rose-stem/flow.cylc @@ -5,6 +5,7 @@ {% set fill_n_merge_source = ['land_cover', 'invert_mask'] %} {% set grid_source = ['grid_to_grid', 'grid_to_variable_resolution_grid', 'grid_to_n48e_namelist', 'grid_to_n48_namelist', '3d_to_3d', '3d_to_3d_with_extrapolation', 'invert_mask'] %} {% set target_lsm_grid_sources = ['grid_to_grid', 'invert_mask'] %} +{% set vertical_regrid_source = ['ozone_zonal_mean', 'aerosol_3d'] %} # This links a name specified on the command in rose-stem with a @@ -26,6 +27,8 @@ install_cold => ancil_general_regrid_invert_mask_latitude_weighted_kdtree => rose_ana_general_regrid_latitude_weighted_kdtree:fail? => plot_comparisons_general_regrid_latitude_weighted_kdtree", "general_regrid_time_constrained": "install_cold => ancil_general_regrid_with_time_constraint => rose_ana_general_regrid:fail? => plot_comparisons_general_regrid install_cold => ancil_general_regrid_with_time_constraint_latitude_weighted_kdtree => rose_ana_general_regrid_latitude_weighted_kdtree:fail? => plot_comparisons_general_regrid_latitude_weighted_kdtree", + "vertical_regrid_zonal_mean": "install_cold => ancil_vertical_regrid_ozone_zonal_mean => rose_ana_vertical_regrid:fail? => plot_comparisons_vertical_regrid", + "vertical_regrid_3d": "install_cold => ancil_vertical_regrid_aerosol_3d => rose_ana_vertical_regrid:fail? => plot_comparisons_vertical_regrid", "build_documentation_graph": "install_cold => build_docs => linkcheck", "unittests_graph": "install_cold => unittests", "black_graph": "install_cold => black", @@ -55,6 +58,7 @@ "core" : ["ancil_2anc_graph", "fill_n_merge", "general_regrid"], "fill_n_merge": ["fill_n_merge_land_cover_graph", "fill_n_merge_invert_mask_graph"], "general_regrid": ["general_regrid_grid_to_grid_graph", "general_regrid_grid_to_variable_graph", "general_regrid_grid_to_n48e_namelist_graph", "general_regrid_grid_to_n48_namelist_graph", "general_regrid_3d_to_3d_graph", "general_regrid_3d_to_3d_with_extrapolation", "general_regrid_time_constrained", "general_regrid_invert_mask_graph"], + "vertical_regrid": ["vertical_regrid_zonal_mean", "vertical_regrid_3d"], "documentation": ["build_documentation_graph"], "unittests": ["unittests_graph", "black_graph", "flake8_graph", "isort_graph"], } @@ -272,6 +276,26 @@ fi ROSE_TASK_APP = rose_ana ROSE_APP_OPT_CONF_KEYS = general_regrid_latitude_weighted_kdtree + + {% for source in vertical_regrid_source %} + [[ANCIL_VERTICAL_REGRID_{{ source }}]] + inherit=ANTS_CORE, LARGE + + [[ancil_vertical_regrid_{{ source }}]] + inherit = ANCIL_VERTICAL_REGRID_{{ source }} + script = rose task-run --app-key=ancil_vertical_regrid -O {{ source }} + + [[rose_ana_vertical_regrid]] + inherit = ROSE_ANA + [[[environment]]] + ROSE_TASK_APP = rose_ana + ROSE_APP_OPT_CONF_KEYS = vertical_regrid + ANTS_KGO_DIRECTORY_OVERRIDE = /data/users/jennifer.hickson/ants_vertical_cli/kgo + + + {% endfor %} + + # ################################## # Build Documentation # ################################## @@ -338,3 +362,8 @@ fi inherit = PLOT_COMPARISONS [[[environment]]] TARGET_ANA_TASK_WORK_DIR=${CYLC_WORKFLOW_WORK_DIR}/${CYLC_TASK_CYCLE_POINT}/rose_ana_general_regrid_${CYLC_TASK_PARAM_fill} + + [[plot_comparisons_vertical_regrid]] + inherit = PLOT_COMPARISONS + [[[environment]]] + TARGET_ANA_TASK_WORK_DIR=${CYLC_WORKFLOW_WORK_DIR}/${CYLC_TASK_CYCLE_POINT}/rose_ana_vertical_regrid