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