From 287eb8bc14a5ef3c0d21987bc8c3c829732ef438 Mon Sep 17 00:00:00 2001 From: jennyhickson <61183013+jennyhickson@users.noreply.github.com> Date: Wed, 6 May 2026 11:03:06 +0100 Subject: [PATCH 01/11] #96 first pass vertical regridder --- lib/ants/cli/ancil_vertical_regrid.py | 155 ++++++++++++++++++++++++++ pyproject.toml | 1 + 2 files changed, 156 insertions(+) create mode 100644 lib/ants/cli/ancil_vertical_regrid.py diff --git a/lib/ants/cli/ancil_vertical_regrid.py b/lib/ants/cli/ancil_vertical_regrid.py new file mode 100644 index 0000000..acd126c --- /dev/null +++ b/lib/ants/cli/ancil_vertical_regrid.py @@ -0,0 +1,155 @@ +#!/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.decomposition as decomp +import ants.io.save as save +import ants.utils +import iris.cube +from ants.utils.cube import create_time_constrained_cubes +import numpy as np + + +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) + + vertical_grid = ants.io.load.load_grid(target_grid) + + return source_cubes, vertical_grid + + +def create_target(source_grid, vertical_grid): + source = ants.utils.cube.as_cubelist(source_grid)[0] + data = np.ones((source.shape[0], source.shape[1], vertical_grid.shape[0])) + target_grid = iris.cube.Cube(data) + target_grid.add_dim_coord(source.coord("latitude"), 0) + target_grid.add_dim_coord(source.coord("longitude"), 1) + target_grid.add_dim_coord(vertical_grid.coord("model_level_number"), 2) + return target_grid + + +def regrid(sources, target): + sources = ants.utils.cube.as_cubelist(sources) + results = [] + scheme = ants.regrid.GeneralRegridScheme(vertical_scheme="Linear") + 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, +): + """ + General 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, vertical_grid = load_data( + source_path, + target_path, + begin, + end, + ) + if ants.utils.cube._is_ugrid(vertical_grid): + raise ValueError( + "Target appears to be a UGrid mesh - the regrid to mesh application in " + "UG-ANTS should be used instead." + ) + + target_cube = create_target(source_cubes, vertical_grid) + regridded_cubes = decomp.decompose(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) + + print(regridded_cubes) + 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/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" From 250bbfc7c3a40794a15de9c337daf9cdf6bc2f2a Mon Sep 17 00:00:00 2001 From: jennyhickson <61183013+jennyhickson@users.noreply.github.com> Date: Tue, 9 Jun 2026 16:07:51 +0100 Subject: [PATCH 02/11] #96 second pass vertical regridder --- lib/ants/cli/ancil_vertical_regrid.py | 28 +++++++-------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/lib/ants/cli/ancil_vertical_regrid.py b/lib/ants/cli/ancil_vertical_regrid.py index acd126c..75cbccf 100644 --- a/lib/ants/cli/ancil_vertical_regrid.py +++ b/lib/ants/cli/ancil_vertical_regrid.py @@ -15,13 +15,10 @@ details. """ import ants -import ants.decomposition as decomp import ants.io.save as save import ants.utils -import iris.cube from ants.utils.cube import create_time_constrained_cubes -import numpy as np - +from ants.fileformats._grid_extract import extract_grid def load_data( source, @@ -33,25 +30,15 @@ def load_data( if begin is not None: source_cubes = create_time_constrained_cubes(source_cubes, begin, end) - vertical_grid = ants.io.load.load_grid(target_grid) - - return source_cubes, vertical_grid - + target_cube = ants.io.load.load_grid(target_grid) -def create_target(source_grid, vertical_grid): - source = ants.utils.cube.as_cubelist(source_grid)[0] - data = np.ones((source.shape[0], source.shape[1], vertical_grid.shape[0])) - target_grid = iris.cube.Cube(data) - target_grid.add_dim_coord(source.coord("latitude"), 0) - target_grid.add_dim_coord(source.coord("longitude"), 1) - target_grid.add_dim_coord(vertical_grid.coord("model_level_number"), 2) - return target_grid + return source_cubes, target_cube def regrid(sources, target): sources = ants.utils.cube.as_cubelist(sources) results = [] - scheme = ants.regrid.GeneralRegridScheme(vertical_scheme="Linear") + scheme = ants.regrid.GeneralRegridScheme(vertical_scheme='Linear') for source in sources: results.append(source.regrid(target, scheme)) return results @@ -98,20 +85,19 @@ def main( A single data cube with the regridded data. """ - source_cubes, vertical_grid = load_data( + source_cubes, target_cube = load_data( source_path, target_path, begin, end, ) - if ants.utils.cube._is_ugrid(vertical_grid): + 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_cube = create_target(source_cubes, vertical_grid) - regridded_cubes = decomp.decompose(regrid, source_cubes, target_cube) + regridded_cubes = regrid(source_cubes, target_cube) if save_ukca: save.ukca_netcdf(regridded_cubes, output_path) From 463430581f47469f5d79fe0b9ea90d4c3e1bba6b Mon Sep 17 00:00:00 2001 From: jennyhickson <61183013+jennyhickson@users.noreply.github.com> Date: Tue, 9 Jun 2026 16:22:11 +0100 Subject: [PATCH 03/11] #96 expand possibly axis to altitude --- lib/ants/regrid/interpolation.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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)) From 37717a18444c524ab6e6fee02473810e6126e977 Mon Sep 17 00:00:00 2001 From: jennyhickson <61183013+jennyhickson@users.noreply.github.com> Date: Tue, 23 Jun 2026 15:45:14 +0100 Subject: [PATCH 04/11] #96 add rose-stem tests --- lib/ants/cli/ancil_vertical_regrid.py | 0 .../opt/rose-app-ozone_zonal_mean.conf | 5 ++++ .../app/ancil_vertical_regrid/rose-app.conf | 17 ++++++++++++++ .../opt/rose-app-vertical_regrid.conf | 3 +++ rose-stem/flow.cylc | 23 +++++++++++++++++++ 5 files changed, 48 insertions(+) mode change 100644 => 100755 lib/ants/cli/ancil_vertical_regrid.py create mode 100644 rose-stem/app/ancil_vertical_regrid/opt/rose-app-ozone_zonal_mean.conf create mode 100644 rose-stem/app/ancil_vertical_regrid/rose-app.conf create mode 100644 rose-stem/app/rose_ana/opt/rose-app-vertical_regrid.conf diff --git a/lib/ants/cli/ancil_vertical_regrid.py b/lib/ants/cli/ancil_vertical_regrid.py old mode 100644 new mode 100755 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..b00a8f0 --- /dev/null +++ b/rose-stem/app/ancil_vertical_regrid/opt/rose-app-ozone_zonal_mean.conf @@ -0,0 +1,5 @@ +[env] +output=/data/users/jennifer.hickson/ants_vertical_cli/ozone/$(ROSE_TASK_NAME) +source=/data/users/jennifer.hickson/ants_vertical_cli/ozone/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..5a01859 --- /dev/null +++ b/rose-stem/app/rose_ana/opt/rose-app-vertical_regrid.conf @@ -0,0 +1,3 @@ +[env] +filelist=ancil_vertical_regrid_ozone_zonal_mean + diff --git a/rose-stem/flow.cylc b/rose-stem/flow.cylc index 48941eb..fbfadab 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'] %} # This links a name specified on the command in rose-stem with a @@ -26,6 +27,7 @@ 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_ozone": "install_cold => ancil_vertical_regrid_ozone_zonal_mean => rose_ana_vertical_regrid:fail? => plot_comparisons_general_regrid", "build_documentation_graph": "install_cold => build_docs => linkcheck", "unittests_graph": "install_cold => unittests", "black_graph": "install_cold => black", @@ -55,6 +57,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_ozone"], "documentation": ["build_documentation_graph"], "unittests": ["unittests_graph", "black_graph", "flake8_graph", "isort_graph"], } @@ -272,6 +275,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/ + + + {% endfor %} + + # ################################## # Build Documentation # ################################## From 56b4e7e42afa513fa1119a0523a7c301718aa7d8 Mon Sep 17 00:00:00 2001 From: jennyhickson <61183013+jennyhickson@users.noreply.github.com> Date: Tue, 25 Aug 2026 15:19:16 +0100 Subject: [PATCH 05/11] KGO test setup - INCLUDES temporary filepaths --- .../ancil_vertical_regrid/opt/rose-app-ozone_zonal_mean.conf | 4 ++-- rose-stem/app/ancil_vertical_regrid/rose-app.conf | 2 +- rose-stem/app/rose_ana/opt/rose-app-vertical_regrid.conf | 2 +- rose-stem/flow.cylc | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) 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 index b00a8f0..e5b6a92 100644 --- 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 @@ -1,5 +1,5 @@ [env] -output=/data/users/jennifer.hickson/ants_vertical_cli/ozone/$(ROSE_TASK_NAME) -source=/data/users/jennifer.hickson/ants_vertical_cli/ozone/Ozone_CMIP5_SPARCex_M1994-2005_N36L85_li-ch_G.nc +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 index 1e7c219..23ff639 100644 --- a/rose-stem/app/ancil_vertical_regrid/rose-app.conf +++ b/rose-stem/app/ancil_vertical_regrid/rose-app.conf @@ -11,7 +11,7 @@ scheme=Linear [command] default=ants-launch ancil_vertical_regrid.py \ =${source} -o ${output} --ants-config ${ANTS_CONFIG} --target-${target_type} ${target} \ - =${begin} ${end} + =${begin} ${end} --netcdf-only [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 index 5a01859..492f0d5 100644 --- a/rose-stem/app/rose_ana/opt/rose-app-vertical_regrid.conf +++ b/rose-stem/app/rose_ana/opt/rose-app-vertical_regrid.conf @@ -1,3 +1,3 @@ [env] -filelist=ancil_vertical_regrid_ozone_zonal_mean +filelist=ancil_vertical_regrid_ozone_zonal_mean.nc diff --git a/rose-stem/flow.cylc b/rose-stem/flow.cylc index fbfadab..2d14bf9 100644 --- a/rose-stem/flow.cylc +++ b/rose-stem/flow.cylc @@ -289,7 +289,7 @@ fi [[[environment]]] ROSE_TASK_APP = rose_ana ROSE_APP_OPT_CONF_KEYS = vertical_regrid - ANTS_KGO_DIRECTORY_OVERRIDE = /data/users/jennifer.hickson/ants_vertical_cli/ + ANTS_KGO_DIRECTORY_OVERRIDE = /data/users/jennifer.hickson/ants_vertical_cli/kgo {% endfor %} From cc2008b36fc22f232e3d13214db30b3c618900b0 Mon Sep 17 00:00:00 2001 From: jennyhickson <61183013+jennyhickson@users.noreply.github.com> Date: Tue, 25 Aug 2026 16:10:18 +0100 Subject: [PATCH 06/11] #96 KGO test setup for aerosols - INCLUDES temporary filepaths --- .../app/ancil_vertical_regrid/opt/rose-app-aerosol_3d.conf | 5 +++++ rose-stem/app/rose_ana/opt/rose-app-vertical_regrid.conf | 1 + rose-stem/flow.cylc | 7 ++++--- 3 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 rose-stem/app/ancil_vertical_regrid/opt/rose-app-aerosol_3d.conf 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..1c2a5bf --- /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/qrclim.aerosols.nc +target=/data/users/jennifer.hickson/ants_vertical_cli/vertlevs_L70_50t_20s_80km +target_type=grid 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 index 492f0d5..d825a5f 100644 --- a/rose-stem/app/rose_ana/opt/rose-app-vertical_regrid.conf +++ b/rose-stem/app/rose_ana/opt/rose-app-vertical_regrid.conf @@ -1,3 +1,4 @@ [env] filelist=ancil_vertical_regrid_ozone_zonal_mean.nc + ancil_vertical_regrid_aerosol_3d.nc diff --git a/rose-stem/flow.cylc b/rose-stem/flow.cylc index 2d14bf9..b9545cd 100644 --- a/rose-stem/flow.cylc +++ b/rose-stem/flow.cylc @@ -5,7 +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'] %} +{% set vertical_regrid_source = ['ozone_zonal_mean', 'aerosol_3d'] %} # This links a name specified on the command in rose-stem with a @@ -27,7 +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_ozone": "install_cold => ancil_vertical_regrid_ozone_zonal_mean => rose_ana_vertical_regrid:fail? => plot_comparisons_general_regrid", + "vertical_regrid_zonal_mean": "install_cold => ancil_vertical_regrid_ozone_zonal_mean => rose_ana_vertical_regrid:fail? => plot_comparisons_general_regrid", + "vertical_regrid_3d": "install_cold => ancil_vertical_regrid_aerosol_3d => rose_ana_vertical_regrid:fail? => plot_comparisons_general_regrid", "build_documentation_graph": "install_cold => build_docs => linkcheck", "unittests_graph": "install_cold => unittests", "black_graph": "install_cold => black", @@ -57,7 +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_ozone"], + "vertical_regrid": ["vertical_regrid_zonal_mean", "vertical_regrid_3d"], "documentation": ["build_documentation_graph"], "unittests": ["unittests_graph", "black_graph", "flake8_graph", "isort_graph"], } From ec00ac7fb1efc4b3828a41ce51f98d505c94731e Mon Sep 17 00:00:00 2001 From: jennyhickson <61183013+jennyhickson@users.noreply.github.com> Date: Fri, 28 Aug 2026 13:27:41 +0100 Subject: [PATCH 07/11] #96 add check for vertical only and docs --- docs/source/ancil_vertica;_regrid.rst | 16 ++++++++++++ lib/ants/cli/ancil_vertical_regrid.py | 37 ++++++++++++++++++++++----- 2 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 docs/source/ancil_vertica;_regrid.rst diff --git a/docs/source/ancil_vertica;_regrid.rst b/docs/source/ancil_vertica;_regrid.rst new file mode 100644 index 0000000..d8ddae7 --- /dev/null +++ b/docs/source/ancil_vertica;_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/lib/ants/cli/ancil_vertical_regrid.py b/lib/ants/cli/ancil_vertical_regrid.py index 75cbccf..98e9b53 100755 --- a/lib/ants/cli/ancil_vertical_regrid.py +++ b/lib/ants/cli/ancil_vertical_regrid.py @@ -32,13 +32,41 @@ def load_data( 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(vertical_scheme='Linear') + scheme = ants.regrid.GeneralRegridScheme() for source in sources: results.append(source.regrid(target, scheme)) return results @@ -54,7 +82,7 @@ def main( netcdf_only, ): """ - General regrid application top level call function. + 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 @@ -91,11 +119,6 @@ def main( begin, end, ) - 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." - ) regridded_cubes = regrid(source_cubes, target_cube) From 0142f7bc0f644ad1445c50c5aab54807ac2d5973 Mon Sep 17 00:00:00 2001 From: jennyhickson <61183013+jennyhickson@users.noreply.github.com> Date: Wed, 2 Sep 2026 14:27:34 +0100 Subject: [PATCH 08/11] #96 linting --- ...;_regrid.rst => ancil_vertical_regrid.rst} | 0 lib/ants/cli/ancil_vertical_regrid.py | 25 ++++++++++++------- 2 files changed, 16 insertions(+), 9 deletions(-) rename docs/source/{ancil_vertica;_regrid.rst => ancil_vertical_regrid.rst} (100%) diff --git a/docs/source/ancil_vertica;_regrid.rst b/docs/source/ancil_vertical_regrid.rst similarity index 100% rename from docs/source/ancil_vertica;_regrid.rst rename to docs/source/ancil_vertical_regrid.rst diff --git a/lib/ants/cli/ancil_vertical_regrid.py b/lib/ants/cli/ancil_vertical_regrid.py index 98e9b53..83a982c 100755 --- a/lib/ants/cli/ancil_vertical_regrid.py +++ b/lib/ants/cli/ancil_vertical_regrid.py @@ -18,7 +18,7 @@ import ants.io.save as save import ants.utils from ants.utils.cube import create_time_constrained_cubes -from ants.fileformats._grid_extract import extract_grid + def load_data( source, @@ -36,9 +36,10 @@ def load_data( return source_cubes, target_cube + def check_target( - target_cube, - source_cubes, + target_cube, + source_cubes, ): """ Check the target cube against common pitfalls. This application is for @@ -52,15 +53,21 @@ def check_target( target_coords = [coord.name() for coord in target_cube.coords()] - if 'latitude' in target_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 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: + 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') + 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): From 712db4b35a72c458560afd0673bdcd1c8cb9b89f Mon Sep 17 00:00:00 2001 From: jennyhickson <61183013+jennyhickson@users.noreply.github.com> Date: Thu, 3 Sep 2026 07:59:03 +0100 Subject: [PATCH 09/11] #96 tidy docs, including fixing linkcheck error --- docs/source/ancil_vertical_regrid.rst | 4 ++-- docs/source/core_capabilities.rst | 7 +++++++ docs/source/release_notes/3.0.rst | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/source/ancil_vertical_regrid.rst b/docs/source/ancil_vertical_regrid.rst index d8ddae7..6d43392 100644 --- a/docs/source/ancil_vertical_regrid.rst +++ b/docs/source/ancil_vertical_regrid.rst @@ -3,9 +3,9 @@ :keywords: ANTS, utils :property=og:locale: en_GB -======================= +======================== ancil_vertical_regrid.py -======================= +======================== For a full description see :mod:`ants.cli.ancil_vertical_regrid`. 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 From 371f882e0c5c879912fc0f7fabb679b29b526942 Mon Sep 17 00:00:00 2001 From: jennyhickson <61183013+jennyhickson@users.noreply.github.com> Date: Thu, 10 Sep 2026 09:41:46 +0100 Subject: [PATCH 10/11] #96 add some KGO words --- docs/source/tutorial_KGO.rst | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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. From e2790624d0d160a53da299c177bc08ae8b15f922 Mon Sep 17 00:00:00 2001 From: jennyhickson <61183013+jennyhickson@users.noreply.github.com> Date: Wed, 16 Sep 2026 13:01:59 +0100 Subject: [PATCH 11/11] #96 adjust rose-stem testing with smaller input and UM-file output --- .../ancil_vertical_regrid/opt/rose-app-aerosol_3d.conf | 2 +- rose-stem/app/ancil_vertical_regrid/rose-app.conf | 2 +- rose-stem/app/rose_ana/opt/rose-app-vertical_regrid.conf | 4 +++- rose-stem/flow.cylc | 9 +++++++-- 4 files changed, 12 insertions(+), 5 deletions(-) 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 index 1c2a5bf..1ce4f57 100644 --- 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 @@ -1,5 +1,5 @@ [env] output=${ROSE_DATA}/${ROSE_TASK_NAME} -source=/data/users/jennifer.hickson/ants_vertical_cli/ancil_vertical_regrid_aerosol_3d/qrclim.aerosols.nc +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/rose-app.conf b/rose-stem/app/ancil_vertical_regrid/rose-app.conf index 23ff639..1e7c219 100644 --- a/rose-stem/app/ancil_vertical_regrid/rose-app.conf +++ b/rose-stem/app/ancil_vertical_regrid/rose-app.conf @@ -11,7 +11,7 @@ scheme=Linear [command] default=ants-launch ancil_vertical_regrid.py \ =${source} -o ${output} --ants-config ${ANTS_CONFIG} --target-${target_type} ${target} \ - =${begin} ${end} --netcdf-only + =${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 index d825a5f..486fdc4 100644 --- a/rose-stem/app/rose_ana/opt/rose-app-vertical_regrid.conf +++ b/rose-stem/app/rose_ana/opt/rose-app-vertical_regrid.conf @@ -1,4 +1,6 @@ [env] -filelist=ancil_vertical_regrid_ozone_zonal_mean.nc +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 b9545cd..67cce14 100644 --- a/rose-stem/flow.cylc +++ b/rose-stem/flow.cylc @@ -27,8 +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_general_regrid", - "vertical_regrid_3d": "install_cold => ancil_vertical_regrid_aerosol_3d => rose_ana_vertical_regrid:fail? => plot_comparisons_general_regrid", + "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", @@ -362,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