Repository: SciTools/iris (language: python)

Issue:
Mergeback v3.3.1
## 🚀 Pull Request

### Description

Merge the 3.3.x branch back into main after the release of 3.3.1.

### Note: to be merged without squashing!

---
[Consult Iris pull request check list]( https://scitools-iris.readthedocs.io/en/latest/developers_guide/contributing_pull_request_checklist.html)



Discussion / hints:

Fix patch:
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 500a2183d2..84af305034 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -4,3 +4,6 @@ contact_links:
   -  name: 💬 Iris GitHub Discussions
      url: https://github.com/SciTools/iris/discussions
      about: Engage with the Iris community to discuss your issue
+  -  name: ❓ Usage Question
+     url: https://github.com/SciTools/iris/discussions/categories/q-a
+     about: Raise a question about how to use Iris in the Q&A section of Discussions
diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml
index b197b58e80..9ae3534c76 100644
--- a/.github/workflows/benchmark.yml
+++ b/.github/workflows/benchmark.yml
@@ -6,6 +6,12 @@ on:
   schedule:
     # Runs every day at 23:00.
     - cron: "0 23 * * *"
+  workflow_dispatch:
+    inputs:
+      first_commit:
+        description: "Argument to be passed to the overnight benchmark script."
+        required: false
+        type: string
 
 jobs:
   benchmark:
@@ -15,7 +21,7 @@ jobs:
     env:
       IRIS_TEST_DATA_LOC_PATH: benchmarks
       IRIS_TEST_DATA_PATH: benchmarks/iris-test-data
-      IRIS_TEST_DATA_VERSION: "2.15"
+      IRIS_TEST_DATA_VERSION: "2.18"
       # Lets us manually bump the cache to rebuild
       ENV_CACHE_BUILD: "0"
       TEST_DATA_CACHE_BUILD: "2"
@@ -64,7 +70,12 @@ jobs:
 
       - name: Run overnight benchmarks
         run: |
-          first_commit=$(git log --after="$(date -d "1 day ago" +"%Y-%m-%d") 23:00:00" --pretty=format:"%h" | tail -n 1)
+          first_commit=${{ inputs.first_commit }}
+          if [ "$first_commit" == "" ]
+          then
+            first_commit=$(git log --after="$(date -d "1 day ago" +"%Y-%m-%d") 23:00:00" --pretty=format:"%h" | tail -n 1)
+          fi
+          
           if [ "$first_commit" != "" ]
           then
             nox --session="benchmarks(overnight)" -- $first_commit
diff --git a/.github/workflows/ci-wheels.yml b/.github/workflows/ci-wheels.yml
index 265489883f..a00833b118 100644
--- a/.github/workflows/ci-wheels.yml
+++ b/.github/workflows/ci-wheels.yml
@@ -163,4 +163,4 @@ jobs:
       with:
         user: __token__
         password: ${{ secrets.PYPI_API_TOKEN }}
-        print_hash: true
\ No newline at end of file
+        print_hash: true
diff --git a/.github/workflows/refresh-lockfiles.yml b/.github/workflows/refresh-lockfiles.yml
index 4e717be8a1..94c20aedb9 100644
--- a/.github/workflows/refresh-lockfiles.yml
+++ b/.github/workflows/refresh-lockfiles.yml
@@ -1,13 +1,5 @@
-# This workflow periodically creates new environment lock files based on the newest
-# available packages and dependencies.
-#
-# Environment specifications are given as conda environment.yml files found in
-# `requirements/ci/py**.yml`.  These state the packages required, the conda channels
-# that the packages will be pulled from, and any versions of packages that need to be
-# pinned at specific versions.
-#
-# For environments that have changed, a pull request will be made and submitted
-# to the main branch
+# Updates the environment lock files. See the called workflow in the
+# scitools/workflows repo for more details.
 
 name: Refresh Lockfiles
 
@@ -20,96 +12,7 @@ on:
     # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#onschedule
     - cron: "1 0 * * 6"
 
-
 jobs:
-
-  get_python_matrix:
-    # Determines which Python versions should be included in the matrix used in
-    # the gen_lockfiles job.
-    if: "github.repository == 'SciTools/iris'"
-    runs-on: ubuntu-latest
-    outputs:
-      matrix: ${{ steps.get_py.outputs.matrix }}
-    steps:
-      - uses: actions/checkout@v3
-      - id: get_py
-        run: echo "::set-output name=matrix::$(ls -1 requirements/ci/py*.yml | xargs -n1 basename | sed 's/....$//' | jq -cnR '[inputs]')"
-
-  gen_lockfiles:
-    # this is a matrix job: it splits to create new lockfiles for each
-    # of the CI test python versions.
-    if: "github.repository == 'SciTools/iris'"
-    runs-on: ubuntu-latest
-    needs: get_python_matrix
-
-    strategy:
-      matrix:
-        python: ${{ fromJSON(needs.get_python_matrix.outputs.matrix) }}
-
-    steps:
-      - uses: actions/checkout@v3
-      - name: install conda-lock
-        run: |
-          source $CONDA/bin/activate base
-          conda install -y -c conda-forge conda-lock
-      - name: generate lockfile
-        run: |
-          $CONDA/bin/conda-lock lock -k explicit -p linux-64 -f requirements/ci/${{matrix.python}}.yml
-          mv conda-linux-64.lock ${{matrix.python}}-linux-64.lock
-      - name: output lockfile
-        uses: actions/upload-artifact@v3
-        with:
-          path: ${{matrix.python}}-linux-64.lock
-
-  create_pr:
-    # once the matrix job has completed all the lock files will have been uploaded as artifacts.
-    # Download the artifacts, add them to the repo, and create a PR.
-    if: "github.repository == 'SciTools/iris'"
-    runs-on: ubuntu-latest
-    needs: gen_lockfiles
-
-    steps:
-      - uses: actions/checkout@v3
-      - name: get artifacts
-        uses: actions/download-artifact@v3
-        with:
-          path: artifacts
-
-      - name: Update lock files in repo
-        run: |
-          cp artifacts/artifact/*.lock requirements/ci/nox.lock
-          rm -r artifacts
-
-      - name: "Generate token"
-        uses: tibdex/github-app-token@v1
-        id: generate-token
-        with:
-          app_id: ${{ secrets.AUTH_APP_ID }}
-          private_key: ${{ secrets.AUTH_APP_PRIVATE_KEY }}
-
-      - name: Create Pull Request
-        id: cpr
-        uses: peter-evans/create-pull-request@18f90432bedd2afd6a825469ffd38aa24712a91d
-        with:
-          token: ${{ steps.generate-token.outputs.token }}
-          commit-message: Updated environment lockfiles
-          committer: "Lockfile bot <noreply@github.com>"
-          author: "Lockfile bot <noreply@github.com>"
-          delete-branch: true
-          branch: auto-update-lockfiles
-          title: "[iris.ci] environment lockfiles auto-update"
-          body: |
-            Lockfiles updated to the latest resolvable environment.
-
-            ### If the CI tasks fail, create a new branch based on this PR and add the required fixes to that branch.
-          labels: |
-            New: Pull Request
-            Bot
-
-      - name: Check Pull Request
-        if: steps.cpr.outputs.pull-request-number != ''
-        run: |
-          echo "pull-request #${{ steps.cpr.outputs.pull-request-number }}"
-          echo "pull-request URL ${{ steps.cpr.outputs.pull-request-url }}"
-          echo "pull-request operation [${{ steps.cpr.outputs.pull-request-operation }}]"
-          echo "pull-request head SHA ${{ steps.cpr.outputs.pull-request-head-sha }}"
+  refresh_lockfiles:
+    uses: scitools/workflows/.github/workflows/refresh-lockfiles.yml@main
+    secrets: inherit
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
index 008fe56deb..c65f37284f 100644
--- a/.github/workflows/stale.yml
+++ b/.github/workflows/stale.yml
@@ -14,7 +14,7 @@ jobs:
     if: "github.repository == 'SciTools/iris'"
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/stale@v5
+      - uses: actions/stale@v6
         with:
           repo-token: ${{ secrets.GITHUB_TOKEN }}
 
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index ab7e64b6a0..b7746edb43 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -29,7 +29,7 @@ repos:
     -   id: no-commit-to-branch
 
 -   repo: https://github.com/psf/black
-    rev: 22.6.0
+    rev: 22.10.0
     hooks:
     -   id: black
         pass_filenames: false
diff --git a/benchmarks/benchmarks/generate_data/__init__.py b/benchmarks/benchmarks/generate_data/__init__.py
index 78b971d9de..52a5aceca8 100644
--- a/benchmarks/benchmarks/generate_data/__init__.py
+++ b/benchmarks/benchmarks/generate_data/__init__.py
@@ -113,7 +113,7 @@ def load_realised():
     file loading, but some benchmarks are only meaningful if starting with real
     arrays.
     """
-    from iris.fileformats.netcdf import _get_cf_var_data as pre_patched
+    from iris.fileformats.netcdf.loader import _get_cf_var_data as pre_patched
 
     def patched(cf_var, filename):
         return as_concrete_data(pre_patched(cf_var, filename))
diff --git a/benchmarks/benchmarks/import_iris.py b/benchmarks/benchmarks/import_iris.py
index ad54c23122..fc32ac289b 100644
--- a/benchmarks/benchmarks/import_iris.py
+++ b/benchmarks/benchmarks/import_iris.py
@@ -5,10 +5,30 @@
 # licensing details.
 from importlib import import_module, reload
 
+################
+# Prepare info for reset_colormaps:
+
+# Import and capture colormaps.
+from matplotlib import colormaps  # isort:skip
+
+_COLORMAPS_ORIG = set(colormaps)
+
+# Import iris.palette, which modifies colormaps.
+import iris.palette
+
+# Derive which colormaps have been added by iris.palette.
+_COLORMAPS_MOD = set(colormaps)
+COLORMAPS_EXTRA = _COLORMAPS_MOD - _COLORMAPS_ORIG
+
+# Touch iris.palette to prevent linters complaining.
+_ = iris.palette
+
+################
+
 
 class Iris:
     @staticmethod
-    def _import(module_name):
+    def _import(module_name, reset_colormaps=False):
         """
         Have experimented with adding sleep() commands into the imported
         modules. The results reveal:
@@ -25,6 +45,13 @@ def _import(module_name):
         and the repetitions are therefore no faster than the first run.
         """
         mod = import_module(module_name)
+
+        if reset_colormaps:
+            # Needed because reload() will attempt to register new colormaps a
+            #  second time, which errors by default.
+            for cm_name in COLORMAPS_EXTRA:
+                colormaps.unregister(cm_name)
+
         reload(mod)
 
     def time_iris(self):
@@ -205,7 +232,7 @@ def time_iterate(self):
         self._import("iris.iterate")
 
     def time_palette(self):
-        self._import("iris.palette")
+        self._import("iris.palette", reset_colormaps=True)
 
     def time_plot(self):
         self._import("iris.plot")
diff --git a/benchmarks/benchmarks/regridding.py b/benchmarks/benchmarks/regridding.py
index c315119c11..44bd1b6c95 100644
--- a/benchmarks/benchmarks/regridding.py
+++ b/benchmarks/benchmarks/regridding.py
@@ -12,8 +12,11 @@
 # importing anything else
 from iris import tests  # isort:skip
 
+import numpy as np
+
 import iris
-from iris.analysis import AreaWeighted
+from iris.analysis import AreaWeighted, PointInCell
+from iris.coords import AuxCoord
 
 
 class HorizontalChunkedRegridding:
@@ -53,3 +56,48 @@ def time_regrid_area_w_new_grid(self) -> None:
         out = self.chunked_cube.regrid(self.template_cube, self.scheme_area_w)
         # Realise data
         out.data
+
+
+class CurvilinearRegridding:
+    def setup(self) -> None:
+        # Prepare a cube and a template
+
+        cube_file_path = tests.get_data_path(
+            ["NetCDF", "regrid", "regrid_xyt.nc"]
+        )
+        self.cube = iris.load_cube(cube_file_path)
+
+        # Make the source cube curvilinear
+        x_coord = self.cube.coord("longitude")
+        y_coord = self.cube.coord("latitude")
+        xx, yy = np.meshgrid(x_coord.points, y_coord.points)
+        self.cube.remove_coord(x_coord)
+        self.cube.remove_coord(y_coord)
+        x_coord_2d = AuxCoord(
+            xx,
+            standard_name=x_coord.standard_name,
+            units=x_coord.units,
+            coord_system=x_coord.coord_system,
+        )
+        y_coord_2d = AuxCoord(
+            yy,
+            standard_name=y_coord.standard_name,
+            units=y_coord.units,
+            coord_system=y_coord.coord_system,
+        )
+        self.cube.add_aux_coord(x_coord_2d, (1, 2))
+        self.cube.add_aux_coord(y_coord_2d, (1, 2))
+
+        template_file_path = tests.get_data_path(
+            ["NetCDF", "regrid", "regrid_template_global_latlon.nc"]
+        )
+        self.template_cube = iris.load_cube(template_file_path)
+
+        # Prepare a regridding scheme
+        self.scheme_pic = PointInCell()
+
+    def time_regrid_pic(self) -> None:
+        # Regrid the cube onto the template.
+        out = self.cube.regrid(self.template_cube, self.scheme_pic)
+        # Realise the data
+        out.data
diff --git a/docs/Makefile b/docs/Makefile
index f4c8d0b7f4..47f3e740fa 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -55,3 +55,8 @@ linkcheck:
 		echo "Running linkcheck in $$i..."; \
 		(cd $$i; $(MAKE) $(MFLAGS) $(MYMAKEFLAGS) linkcheck); done
 
+show:
+	@for i in $(SUBDIRS); do \
+		echo "Running show in $$i..."; \
+		(cd $$i; $(MAKE) $(MFLAGS) $(MYMAKEFLAGS) show); done
+
diff --git a/docs/gallery_code/general/plot_zonal_means.py b/docs/gallery_code/general/plot_zonal_means.py
new file mode 100644
index 0000000000..08a9578e63
--- /dev/null
+++ b/docs/gallery_code/general/plot_zonal_means.py
@@ -0,0 +1,89 @@
+"""
+Zonal Mean Diagram of Air Temperature
+=====================================
+This example demonstrates aligning a linear plot and a cartographic plot using Matplotlib.
+"""
+
+import cartopy.crs as ccrs
+import matplotlib.pyplot as plt
+from mpl_toolkits.axes_grid1 import make_axes_locatable
+import numpy as np
+
+import iris
+from iris.analysis import MEAN
+import iris.plot as iplt
+import iris.quickplot as qplt
+
+
+def main():
+
+    # Loads air_temp.pp and "collapses" longitude into a single, average value.
+    fname = iris.sample_data_path("air_temp.pp")
+    temperature = iris.load_cube(fname)
+    collapsed_temp = temperature.collapsed("longitude", MEAN)
+
+    # Set y-axes with -90 and 90 limits and steps of 15 per tick.
+    start, stop, step = -90, 90, 15
+    yticks = np.arange(start, stop + step, step)
+    ylim = [start, stop]
+
+    # Plot "temperature" on a cartographic plot and set the ticks and titles
+    # on the axes.
+    fig = plt.figure(figsize=[12, 4])
+
+    ax1 = fig.add_subplot(111, projection=ccrs.PlateCarree())
+    im = iplt.contourf(temperature, cmap="RdYlBu_r")
+    ax1.coastlines()
+    ax1.gridlines()
+    ax1.set_xticks([-180, -90, 0, 90, 180])
+    ax1.set_yticks(yticks)
+    ax1.set_title("Air Temperature")
+    ax1.set_ylabel(f"Latitude / {temperature.coord('latitude').units}")
+    ax1.set_xlabel(f"Longitude / {temperature.coord('longitude').units}")
+    ax1.set_ylim(*ylim)
+
+    # Create a Matplotlib AxesDivider object to allow alignment of other
+    # Axes objects.
+    divider = make_axes_locatable(ax1)
+
+    # Gives the air temperature bar size, colour and a title.
+    ax2 = divider.new_vertical(
+        size="5%", pad=0.5, axes_class=plt.Axes, pack_start=True
+    )  # creates 2nd axis
+    fig.add_axes(ax2)
+    cbar = plt.colorbar(
+        im, cax=ax2, orientation="horizontal"
+    )  # puts colour bar on second axis
+    cbar.ax.set_xlabel(f"{temperature.units}")  # labels colour bar
+
+    # Plot "collapsed_temp" on the mean graph and set the ticks and titles
+    # on the axes.
+    ax3 = divider.new_horizontal(
+        size="30%", pad=0.4, axes_class=plt.Axes
+    )  # create 3rd axis
+    fig.add_axes(ax3)
+    qplt.plot(
+        collapsed_temp, collapsed_temp.coord("latitude")
+    )  # plots temperature collapsed over longitude against latitude
+    ax3.axhline(0, color="k", linewidth=0.5)
+
+    # Creates zonal mean details
+    ax3.set_title("Zonal Mean")
+    ax3.yaxis.set_label_position("right")
+    ax3.yaxis.tick_right()
+    ax3.set_yticks(yticks)
+    ax3.grid()
+
+    # Round each tick for the third ax to the nearest 20 (ready for use).
+    data_max = collapsed_temp.data.max()
+    x_max = data_max - data_max % -20
+    data_min = collapsed_temp.data.min()
+    x_min = data_min - data_min % 20
+    ax3.set_xlim(x_min, x_max)
+    ax3.set_ylim(*ylim)
+
+    plt.show()
+
+
+if __name__ == "__main__":
+    main()
diff --git a/docs/src/Makefile b/docs/src/Makefile
index c693a2c900..37c2e9e3e6 100644
--- a/docs/src/Makefile
+++ b/docs/src/Makefile
@@ -16,7 +16,7 @@ PAPEROPT_a4     = -D latex_paper_size=a4
 PAPEROPT_letter = -D latex_paper_size=letter
 ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
 
-.PHONY: help clean html html-noplot dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
+.PHONY: help clean html html-noplot dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest show
 
 help:
 	@echo "Please use \`make <target>' where <target> is one of"
@@ -36,6 +36,7 @@ help:
 	@echo "  changes    to make an overview of all changed/added/deprecated items"
 	@echo "  linkcheck  to check all external links for integrity"
 	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
+	@echo "  show       to open the built documentation in the default browser"
 
 clean:
 	-rm -rf $(BUILDDIR)
@@ -153,3 +154,6 @@ doctest:
 	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
 	@echo "Testing of doctests in the sources finished, look at the " \
 	      "results in $(BUILDDIR)/doctest/output.txt."
+
+show:
+	@python -c "import webbrowser; webbrowser.open_new_tab('file://$(shell pwd)/$(BUILDDIR)/html/index.html')"
\ No newline at end of file
diff --git a/docs/src/_templates/layout.html b/docs/src/_templates/layout.html
index 7377e866b7..9a6fe46d01 100644
--- a/docs/src/_templates/layout.html
+++ b/docs/src/_templates/layout.html
@@ -10,9 +10,8 @@
     {% if on_rtd and rtd_version == 'latest' %}
         <div id="slim-red-box-banner">
             You are viewing the <b>latest</b> unreleased documentation
-            <b>v{{ version }}</b>.  You may prefer a
-            <a href="https://scitools-iris.readthedocs.io/en/stable/">stable</a>
-            version.
+            <b>v{{ version }}</b>. You can switch to a <b>stable</b> version
+            via the flyout menu in the bottom-right of the screen.
         </div>
         <p></p>
     {%- endif %}
diff --git a/docs/src/common_links.inc b/docs/src/common_links.inc
index 0b1017a7d8..530ebc4877 100644
--- a/docs/src/common_links.inc
+++ b/docs/src/common_links.inc
@@ -39,6 +39,7 @@
 .. _requirements/ci/: https://github.com/SciTools/iris/tree/main/requirements/ci
 .. _CF-UGRID: https://ugrid-conventions.github.io/ugrid-conventions/
 .. _issues on GitHub: https://github.com/SciTools/iris/issues?q=is%3Aopen+is%3Aissue+sort%3Areactions-%2B1-desc
+.. _python-stratify: https://github.com/SciTools/python-stratify
 
 
 .. comment
diff --git a/docs/src/developers_guide/contributing_documentation_full.rst b/docs/src/developers_guide/contributing_documentation_full.rst
index ac62a67373..41314e80ac 100755
--- a/docs/src/developers_guide/contributing_documentation_full.rst
+++ b/docs/src/developers_guide/contributing_documentation_full.rst
@@ -61,7 +61,10 @@ If you wish to run a full clean build you can run::
     make clean
     make html
 
-This is useful for a final test before committing your changes.
+This is useful for a final test before committing your changes. Having built
+the documentation, you can view them in your default browser via::
+
+    make show
 
 .. note:: In order to preserve a clean build for the html, all **warnings**
           have been promoted to be **errors** to ensure they are addressed.
diff --git a/docs/src/developers_guide/release.rst b/docs/src/developers_guide/release.rst
index 25a426e20b..de7aa6c719 100644
--- a/docs/src/developers_guide/release.rst
+++ b/docs/src/developers_guide/release.rst
@@ -209,6 +209,11 @@ branch, and then released by tagging ``v1.9.1``.
 
 New features shall not be included in a point release, these are for bug fixes.
 
+``whatsnew`` entries should be added to the existing 
+``docs/src/whatsnew/v1.9.rst`` file in a new ``v1.9.1`` section. A template for 
+this bugfix patches section can be found in the 
+``docs/src/whatsnew/latest.rst.template`` file.
+
 A point release does not require a release candidate, but the rest of the
 release process is to be followed, including the merge back of changes into
 ``main``.
@@ -224,17 +229,14 @@ These steps assume a release for ``1.9.0`` is to be created.
 Release Steps
 ~~~~~~~~~~~~~
 
-#. Create the release feature branch ``v1.9.x`` on `SciTools/iris`_.
-   The only exception is for a point/bugfix release, as it should already exist
-#. Update the ``iris.__init__.py`` version string e.g., to ``1.9.0``
 #. Update the ``whatsnew`` for the release:
 
    * Use ``git`` to rename ``docs/src/whatsnew/latest.rst`` to the release
      version file ``v1.9.rst``
-   * Update ``docs/src/whatsnews/index.rst`` to rename ``latest.rst`` in the
-     include statement and toctree.
    * Use ``git`` to delete the ``docs/src/whatsnew/latest.rst.template`` file
    * In ``v1.9.rst`` remove the ``[unreleased]`` caption from the page title.
+     Replace this with ``[release candidate]`` for the release candidate and
+     remove this for the actual release.
      Note that, the Iris version and release date are updated automatically
      when the documentation is built
    * Review the file for correctness
@@ -253,6 +255,9 @@ Release Steps
 #. Once all the above steps are complete, the release is cut, using
    the :guilabel:`Draft a new release` button on the
    `Iris release page <https://github.com/SciTools/iris/releases>`_
+   and targeting the release branch if it exists
+#. Create the release feature branch ``v1.9.x`` on `SciTools/iris`_ if it doesn't
+   already exist. For point/bugfix releases use the branch which already exists
 
 
 Post Release Steps
@@ -260,17 +265,18 @@ Post Release Steps
 
 #. Check the documentation has built on `Read The Docs`_.  The build is
    triggered by any commit to ``main``.  Additionally check that the versions
-   available in the pop out menu in the bottom left corner include the new
+   available in the pop out menu in the bottom right corner include the new
    release version.  If it is not present you will need to configure the
    versions available in the **admin** dashboard in `Read The Docs`_.
 #. Review the `Active Versions`_ for the ``scitools-iris`` project on
    `Read The Docs`_ to ensure that the appropriate versions are ``Active``
    and/or ``Hidden``. To do this ``Edit`` the appropriate version e.g.,
    see `Editing v3.0.0rc0`_ (must be logged into Read the Docs).
-#. Make a new ``latest.rst`` from ``latest.rst.template`` and update the include
-   statement and the toctree in ``index.rst`` to point at the new
+#. Merge back to ``main``. This should be done after all releases, including
+   the release candidate, and also after major changes to the release branch.
+#. On main, make a new ``latest.rst`` from ``latest.rst.template`` and update
+   the include statement and the toctree in ``index.rst`` to point at the new
    ``latest.rst``.
-#. Merge back to ``main``
 
 
 .. _SciTools/iris: https://github.com/SciTools/iris
@@ -285,4 +291,4 @@ Post Release Steps
 .. _Generating Distribution Archives: https://packaging.python.org/tutorials/packaging-projects/#generating-distribution-archives
 .. _Packaging Your Project: https://packaging.python.org/guides/distributing-packages-using-setuptools/#packaging-your-project
 .. _latest CF standard names: http://cfconventions.org/standard-names.html
-.. _setuptools-scm: https://github.com/pypa/setuptools_scm
\ No newline at end of file
+.. _setuptools-scm: https://github.com/pypa/setuptools_scm
diff --git a/docs/src/further_topics/ugrid/images/orca_grid.png b/docs/src/further_topics/ugrid/images/orca_grid.png
new file mode 100644
index 0000000000..6676e84fbb
Binary files /dev/null and b/docs/src/further_topics/ugrid/images/orca_grid.png differ
diff --git a/docs/src/further_topics/ugrid/other_meshes.rst b/docs/src/further_topics/ugrid/other_meshes.rst
index e6f477624e..38abeeca03 100644
--- a/docs/src/further_topics/ugrid/other_meshes.rst
+++ b/docs/src/further_topics/ugrid/other_meshes.rst
@@ -221,5 +221,140 @@ as the **nodes** when creating the Iris
                     <AuxCoord: longitude / (degrees)  [...]  shape(666328,)>
                     <AuxCoord: latitude / (degrees)  [...]  shape(666328,)>
 
+
+.. _ORCA_example:
+
+`NEMO`_ data on ORCA tripolar grid
+----------------------------------
+..  figure:: images/orca_grid.png
+    :width: 300
+    :alt: Plot of ORCA-gridded data from NEMO.
+
+NEMO can use various grids, but is frequently used with ORCA type grids.
+ORCA grids store global data in 2-dimensional ny * nx arrays.  All cells are
+four-sided.  The grids are based on tri-polar layouts, but X and Y spacings are
+irregular and not given by any defined functional forms.
+
+* arrays (ny, nx) of face-located data variables
+* arrays (ny, nx) of X+Y face centre coordinates
+* arrays (ny, nx, 4) of X+Y face corner coordinates
+  (all faces are quadrilaterals)
+
+For simplicity, we treat each face corner as an independent node, and use a face-node
+connectivity which simply lists the nodes in sequence,
+i.e. [[0, 1, 2, 3], [4, 5, 6, 7], ...].
+
+.. Note::
+    This is the simplest solution, but produces approx 4x more nodes than
+    necessary, since the coordinate bounds contain many duplicate locations.
+    Removing the duplicates is quite easy, but often not necessary.
+
+To make an unstructured cube, the data must be 'flattened' to convert the given X and Y
+dimensions into a single mesh dimension.  Since Iris cubes don't support a "reshape" or
+"flatten" operations, we create a new cube from the flattened data.
+
+.. dropdown:: :opticon:`code`
+
+    .. code-block:: python
+
+        >>> import numpy as np
+        >>> import iris
+        >>> from iris.coords import AuxCoord, CellMeasure
+        >>> from iris.cube import Cube
+        >>> from iris.experimental.ugrid.mesh import Mesh, Connectivity
+
+
+        >>> filepath = iris.sample_data_path('orca2_votemper.nc')
+        >>> cube = iris.load_cube(filepath)
+        >>> print(cube)
+        sea_water_potential_temperature / (degC) (-- : 148; -- : 180)
+            Auxiliary coordinates:
+                latitude                             x         x
+                longitude                            x         x
+            Scalar coordinates:
+                depth                            4.999938 m, bound=(0.0, 10.0) m
+                time                             0001-01-01 12:00:00
+            Cell methods:
+                mean                             time
+            Attributes:
+                Conventions                      'CF-1.5'
+
+
+        >>> co_x =  cube.coord("longitude")
+        >>> co_y = cube.coord("latitude")
+        >>> ny, nx = co_x.shape
+        >>> n_faces = ny * nx
+
+        >>> # Create face coords from flattened face-points
+        >>> face_x_co = AuxCoord(co_x.points.flatten())
+        >>> face_y_co = AuxCoord(co_y.points.flatten())
+        >>> assert face_x_co.shape == (n_faces,)
+        >>> face_x_co.metadata = co_x.metadata
+        >>> face_y_co.metadata = co_y.metadata
+
+        >>> # Create node coordinates from bound points.
+        >>> n_nodes = n_faces * 4
+        >>> node_x_co = AuxCoord(co_x.bounds.flatten())
+        >>> node_y_co = AuxCoord(co_y.bounds.flatten())
+        >>> assert node_x_co.shape == (n_nodes,)
+        >>> node_x_co.metadata = co_x.metadata
+        >>> node_y_co.metadata = co_y.metadata
+
+        >>> # Create a face-node Connectivity matching the order of nodes in the bounds array
+        >>> face_node_inds = np.arange(n_nodes).reshape((n_faces, 4))
+        >>> face_nodes_conn = Connectivity(
+        ...     indices=face_node_inds,
+        ...     cf_role='face_node_connectivity',
+        ...     long_name='face_inds', units='1',
+        ... )
+
+        >>> # Create a mesh object.
+        >>> mesh = Mesh(
+        ...     topology_dimension=2,
+        ...     node_coords_and_axes=[(node_x_co, 'x'), (node_y_co, 'y')],
+        ...     connectivities=face_nodes_conn,
+        ...     face_coords_and_axes=[(face_x_co, 'x'), (face_y_co, 'y')]
+        ... )
+        >>> print(mesh)
+        Mesh : 'unknown'
+            topology_dimension: 2
+            node
+                node_dimension: 'Mesh2d_node'
+                node coordinates
+                    <AuxCoord: longitude / (degrees)  [...]  shape(106560,)>
+                    <AuxCoord: latitude / (degrees)  [...]  shape(106560,)>
+            face
+                face_dimension: 'Mesh2d_face'
+                face_node_connectivity: <Connectivity: face_inds / (1)  [...]  shape(26640, 4)>
+                face coordinates
+                    <AuxCoord: longitude / (degrees)  [...]  shape(26640,)>
+                    <AuxCoord: latitude / (degrees)  [...]  shape(26640,)>
+
+
+        >>> # Create an unstructured version of the input with flattened data
+        >>> meshcube = Cube(cube.core_data().flatten())
+        >>> meshcube.metadata = cube.metadata
+
+        >>> # Attach the mesh by adding the mesh 'face' MeshCoords into the cube
+        >>> mesh_dim = meshcube.ndim - 1
+        >>> for co in mesh.to_MeshCoords('face'):
+        ...     meshcube.add_aux_coord(co, mesh_dim)
+        ...
+
+        >>> print(meshcube)
+        sea_water_potential_temperature / (degC) (-- : 26640)
+            Mesh coordinates:
+                latitude                             x
+                longitude                            x
+            Mesh:
+                name                             unknown
+                location                         face
+            Cell methods:
+                mean                             time
+            Attributes:
+                Conventions                      'CF-1.5'
+
+
 .. _WAVEWATCH III: https://github.com/NOAA-EMC/WW3
 .. _FESOM 1.4: https://fesom.de/models/fesom14/
+.. _NEMO: https://www.nemo-ocean.eu/
\ No newline at end of file
diff --git a/docs/src/userguide/cube_maths.rst b/docs/src/userguide/cube_maths.rst
index fe9a5d63d2..9c0898b62c 100644
--- a/docs/src/userguide/cube_maths.rst
+++ b/docs/src/userguide/cube_maths.rst
@@ -165,6 +165,12 @@ broadcasting behaviour::
     >>> print(result.summary(True))
     unknown / (K)                       (time: 240; latitude: 37; longitude: 49)
 
+
+.. seealso::
+
+    Relevant gallery example:
+    :ref:`sphx_glr_generated_gallery_general_plot_anomaly_log_colouring.py` (Anomaly)
+
 Combining Multiple Phenomena to Form a New One
 ----------------------------------------------
 
diff --git a/docs/src/userguide/cube_statistics.rst b/docs/src/userguide/cube_statistics.rst
index 980f1e132f..08297c2a51 100644
--- a/docs/src/userguide/cube_statistics.rst
+++ b/docs/src/userguide/cube_statistics.rst
@@ -4,6 +4,11 @@
 Cube Statistics
 ===============
 
+.. seealso::
+
+    Relevant gallery example:
+    :ref:`sphx_glr_generated_gallery_general_plot_zonal_means.py` (Collapsing)
+
 .. _cube-statistics-collapsing:
 
 Collapsing Entire Data Dimensions
diff --git a/docs/src/userguide/glossary.rst b/docs/src/userguide/glossary.rst
new file mode 100644
index 0000000000..818ef0c7ad
--- /dev/null
+++ b/docs/src/userguide/glossary.rst
@@ -0,0 +1,210 @@
+.. _glossary:
+
+Glossary
+=============
+
+.. glossary::
+
+   Cartopy
+        A python package for producing maps, and other geospatial data.
+        Allows plotting on these maps, over a range of projections.
+
+        | **Related:** :term:`Matplotlib`
+        | **More information:** `CartoPy Site <https://scitools.org.uk/cartopy/docs/latest/>`_
+        |
+
+   CF Conventions
+        Rules for storing meteorological Climate and Forecast data in
+        :term:`NetCDF Format` files, defining a standard metadata format to
+        describe what the data is.
+        This also forms the data model which iris is based on.
+
+        | **Related:** :term:`NetCDF Format`
+        | **More information:** `CF Conventions <http://cfconventions.org/>`_
+        |
+
+   Coordinate
+        A container for data points, comes in three main flavours.
+
+        - Dimensional Coordinate -
+            A coordinate that describes a single data dimension of a cube.
+            They can only contain numerical values, in a sorted order (ascending
+            or descending).
+        - Auxiliary Coordinate -
+            A coordinate that can map to multiple data dimensions. Can
+            contain any type of data.
+        - Scalar Coordinate -
+                A coordinate that is not mapped to any data dimension, instead
+                representing the cube as a whole.
+
+        | **Related:** :term:`Cube`
+        | **More information:** :doc:`iris_cubes`
+        |
+
+   Cube
+        Cubes are the main method of storing data in Iris. A cube can consist of:
+
+        - Array of :term:`Phenomenon` Data (Required)
+        - :term:`Coordinates <Coordinate>`
+        - :term:`Standard Name`
+        - :term:`Long Name`
+        - :term:`Unit`
+        - :term:`Cell Methods <Cell Method>`
+        - :term:`Coordinate Factories <Coordinate Factory>`
+
+        | **Related:** :term:`NumPy`
+        | **More information:** :doc:`iris_cubes`
+        |
+
+   Cell Method
+        A cell method represents that a cube's data has been derived from
+        a past statistical operation, such as a
+        MEAN or SUM operation.
+
+        | **Related:** :term:`Cube`
+        | **More information:** :doc:`iris_cubes`
+        |
+
+   Coordinate Factory
+        A coordinate factory derives coordinates (sometimes referred to as
+        derived coordinates) from the values of existing coordinates.
+        E.g. A hybrid height factory might use "height above sea level"
+        and "height at ground level" coordinate data to calculate a
+        "height above ground level" coordinate.
+
+        | **Related:** :term:`Cube`
+        | **More information:** :doc:`iris_cubes`
+        |
+
+
+   Dask
+        A data analytics python library. Iris predominantly uses Dask Arrays;
+        a collection of NumPy-esque arrays. The data is operated in batches,
+        so that not all data is in RAM at once.
+
+        | **Related:** :term:`Lazy Data` **|** :term:`NumPy`
+        | **More information:** :doc:`real_and_lazy_data`
+        |
+
+   Fields File (FF) Format
+        A meteorological file format, the output of the Unified Model.
+
+        | **Related:**  :term:`GRIB Format`
+         **|** :term:`Post Processing (PP) Format` **|** :term:`NetCDF Format`
+        | **More information:** `Unified Model <https://www.metoffice.gov.uk/research/approach/modelling-systems/unified-model/index>`_
+        |
+
+   GRIB Format
+        A WMO-standard meteorological file format.
+
+        | **Related:** :term:`Fields File (FF) Format`
+         **|** :term:`Post Processing (PP) Format` **|** :term:`NetCDF Format`
+        | **More information:** `GRIB 1 User Guide <https://old.wmo.int/extranet/pages/prog/www/WMOCodes/Guides/GRIB/GRIB1-Contents.html>`_
+         **|** `GRIB 2 User Guide.pdf <https://old.wmo.int/extranet/pages/prog/www/WMOCodes/Guides/GRIB/GRIB2_062006.pdf>`_
+        |
+
+   Lazy Data
+        Data stored in hard drive, and then temporarily loaded into RAM in
+        batches when needed. Allows of less memory usage and faster performance,
+        thanks to parallel processing.
+
+        | **Related:** :term:`Dask` **|** :term:`Real Data`
+        | **More information:** :doc:`real_and_lazy_data`
+        |
+
+   Long Name
+        A name describing a :term:`phenomenon`, not limited to the
+        the same restraints as :term:`standard name`.
+
+        | **Related:** :term:`Standard Name` **|** :term:`Cube`
+        | **More information:** :doc:`iris_cubes`
+        |
+
+   Matplotlib
+        A python package for plotting and projecting data in a wide variety
+        of formats.
+
+        | **Related:** :term:`CartoPy` **|** :term:`NumPy`
+        | **More information:** `Matplotlib <https://scitools.org.uk/cartopy/docs/latest/>`_
+        |
+
+   Metadata
+        The information which describes a phenomenon.
+        Within Iris specifically, all information which
+        distinguishes one phenomenon from another,
+        e.g. :term:`units <Unit>` or :term:`Cell Methods <Cell Method>`
+
+        | **Related:** :term:`Phenomenon` **|** :term:`Cube`
+        | **More information:** :doc:`../further_topics/metadata`
+        |
+
+   NetCDF Format
+        A flexible file format for storing multi-dimensional array-like data.
+        When Iris loads this format, it also especially recognises and interprets data
+        encoded according to the :term:`CF Conventions`.
+
+        | **Related:** :term:`Fields File (FF) Format`
+         **|** :term:`GRIB Format` **|** :term:`Post Processing (PP) Format`
+        | **More information:** `NetCDF-4 Python Git <https://github.com/Unidata/netcdf4-python>`_
+        |
+
+   NumPy
+        A mathematical Python library, predominantly based around
+        multi-dimensional arrays.
+
+        | **Related:** :term:`Dask`  **|** :term:`Cube`
+         **|** :term:`Xarray`
+        | **More information:** `NumPy.org <https://numpy.org/>`_
+        |
+
+   Phenomenon
+        The primary data which is measured, usually within a cube, e.g.
+        air temperature.
+
+        | **Related:** :term:`Metadata`
+         **|** :term:`Standard Name` **|** :term:`Cube`
+        | **More information:** :doc:`iris_cubes`
+        |
+
+   Post Processing (PP) Format
+        A meteorological file format, created from a post processed
+        :term:`Fields File (FF) Format`.
+
+        | **Related:** :term:`GRIB Format` **|** :term:`NetCDF Format`
+        | **More information:** `PP Wikipedia Page <https://en.wikipedia.org/wiki/PP-format>`_
+        |
+
+   Real Data
+        Data that has been loaded into RAM, as opposed to sitting
+        on the hard drive.
+
+        | **Related:** :term:`Lazy Data` **|** :term:`NumPy`
+        | **More information:** :doc:`real_and_lazy_data`
+        |
+
+   Standard Name
+        A name describing a :term:`phenomenon`,  one from a fixed list
+        defined at `CF Standard Names <http://cfconventions.org/standard-names.html>`_.
+
+        | **Related:** :term:`Long Name` **|** :term:`Cube`
+        | **More information:** :doc:`iris_cubes`
+        |
+
+   Unit
+        The unit with which the :term:`phenomenon` is measured e.g. m / sec.
+
+        | **Related:** :term:`Cube`
+        | **More information:** :doc:`iris_cubes`
+        |
+
+   Xarray
+        A python library for sophisticated labelled multi-dimensional operations.
+        Has a broader scope than Iris - it is not focused on meteorological data.
+
+        | **Related:** :term:`NumPy`
+        | **More information:** `Xarray Documentation <https://docs.xarray.dev/en/stable/index.html>`_
+        |
+
+----
+
+`To top <glossary_>`_
diff --git a/docs/src/userguide/index.rst b/docs/src/userguide/index.rst
index 08923e7662..fdd0c4d03e 100644
--- a/docs/src/userguide/index.rst
+++ b/docs/src/userguide/index.rst
@@ -35,6 +35,7 @@ they may serve as a useful reference for future exploration.
    cube_maths
    citation
    code_maintenance
+   glossary
 
 
 .. toctree::
diff --git a/docs/src/userguide/merge_and_concat.rst b/docs/src/userguide/merge_and_concat.rst
index 08c3ce9711..b521d49a59 100644
--- a/docs/src/userguide/merge_and_concat.rst
+++ b/docs/src/userguide/merge_and_concat.rst
@@ -253,6 +253,11 @@ which are described below.
 Using CubeList.concatenate
 ==========================
 
+.. seealso::
+
+    Relevant gallery example:
+    :ref:`sphx_glr_generated_gallery_general_plot_projections_and_annotations.py` (Brief concatenating examples)
+
 The :meth:`CubeList.concatenate <iris.cube.CubeList.concatenate>` method operates on a list
 of cubes and returns a new :class:`~iris.cube.CubeList` containing the cubes
 that have been concatenated.
diff --git a/docs/src/userguide/navigating_a_cube.rst b/docs/src/userguide/navigating_a_cube.rst
index c5924a61c6..b4c16b094b 100644
--- a/docs/src/userguide/navigating_a_cube.rst
+++ b/docs/src/userguide/navigating_a_cube.rst
@@ -110,6 +110,10 @@ cube with the :attr:`Cube.cell_methods <iris.cube.Cube.cell_methods>` attribute:
 
     print(cube.cell_methods)
 
+.. seealso::
+
+    Relevant gallery example:
+    :ref:`sphx_glr_generated_gallery_meteorology_plot_wind_barbs.py`
 
 Accessing Coordinates on the Cube
 ---------------------------------
@@ -176,6 +180,10 @@ We can add and remove coordinates via :func:`Cube.add_dim_coord<iris.cube.Cube.a
 
 The coordinate ``my_custom_coordinate`` now exists on the cube and is listed under the non-dimensioned single valued scalar coordinates.
 
+.. seealso::
+
+    Relevant gallery example:
+    :ref:`sphx_glr_generated_gallery_general_plot_custom_file_loading.py` (Adding Metadata)
 
 Adding and Removing Metadata to the Cube at Load Time
 -----------------------------------------------------
diff --git a/docs/src/userguide/plotting_a_cube.rst b/docs/src/userguide/plotting_a_cube.rst
index a2334367c5..70a1fbba91 100644
--- a/docs/src/userguide/plotting_a_cube.rst
+++ b/docs/src/userguide/plotting_a_cube.rst
@@ -10,6 +10,10 @@ high quality, production ready 1D and 2D plots.
 The functionality of the Matplotlib :py:mod:`~matplotlib.pyplot` module has 
 been extended within Iris to facilitate easy visualisation of a cube's data.
 
+.. seealso::
+
+    Relevant gallery example:
+    :ref:`sphx_glr_generated_gallery_general_plot_zonal_means.py` (Plotting with multiple axes)
 
 ***************************
 Matplotlib's Pyplot Basics
@@ -82,9 +86,9 @@ so ensure that interactive mode is turned off with::
 
         plt.interactive(False)
 
-
-Saving a Plot
-=============
+===============
+ Saving a Plot
+===============
 
 The :py:func:`matplotlib.pyplot.savefig` function is similar to **plt.show()** 
 in that they are both *non-interactive* visualisation modes. 
diff --git a/docs/src/userguide/subsetting_a_cube.rst b/docs/src/userguide/subsetting_a_cube.rst
index c4f55490af..019982ad6d 100644
--- a/docs/src/userguide/subsetting_a_cube.rst
+++ b/docs/src/userguide/subsetting_a_cube.rst
@@ -10,6 +10,13 @@ However it is often necessary to reduce the dimensionality of a cube down to som
 Iris provides several ways of reducing both the amount of data and/or the number of dimensions in your cube depending on the circumstance.
 In all cases **the subset of a valid cube is itself a valid cube**.
 
+
+.. seealso::
+
+    Relevant gallery examples:
+        - :ref:`sphx_glr_generated_gallery_general_plot_polynomial_fit.py` (Slices)
+        - :ref:`sphx_glr_generated_gallery_general_plot_anomaly_log_colouring.py` (Extraction)
+
 .. _cube_extraction:
 
 Cube Extraction
diff --git a/docs/src/whatsnew/3.3.rst b/docs/src/whatsnew/3.3.rst
index 57478c9645..c2e47f298a 100644
--- a/docs/src/whatsnew/3.3.rst
+++ b/docs/src/whatsnew/3.3.rst
@@ -1,7 +1,7 @@
 .. include:: ../common_links.inc
 
-v3.3 (18 Aug 2022) [release candidate]
-**************************************
+v3.3 (1 Sep 2022)
+*****************
 
 This document explains the changes made to Iris for this release
 (:doc:`View all changes <index>`.)
@@ -31,6 +31,36 @@ This document explains the changes made to Iris for this release
    any issues or feature requests for improving Iris. Enjoy!
 
 
+v3.3.1 (29 Sep 2022)
+====================
+
+.. dropdown:: :opticon:`alert` v3.3.1 Patches
+   :container: + shadow
+   :title: text-primary text-center font-weight-bold
+   :body: bg-light
+   :animate: fade-in
+
+   The patches in this release of Iris include:
+
+    #. `@pp-mo`_ fixed the Jupyter notebook display of :class:`~iris.cube.CubeList`.
+       (:issue:`4973`, :pull:`4976`)
+
+    #. `@pp-mo`_ fixed a bug in NAME loaders where data with no associated statistic would
+       load as a cube with invalid cell-methods, which cannot be printed or saved to netcdf.
+       (:issue:`3288`, :pull:`4933`)
+
+    #. `@pp-mo`_ ensured that :data:`iris.cube.Cube.cell_methods` must always be an iterable
+       of :class:`iris.coords.CellMethod` objects (:pull:`4933`).
+
+    #. `@trexfeathers`_ advanced the Cartopy pin to ``>=0.21``, as Cartopy's
+       change to default Transverse Mercator projection affects an Iris test.
+       See `SciTools/cartopy@fcb784d`_ and `SciTools/cartopy@8860a81`_ for more
+       details. (:pull:`4992`)
+
+    #. `@trexfeathers`_ introduced the ``netcdf4!=1.6.1`` pin to avoid a
+       problem with segfaults. (:pull:`4992`)
+
+
 📢 Announcements
 ================
 
@@ -201,7 +231,7 @@ This document explains the changes made to Iris for this release
 #. NumPy ``v1.23`` behaviour changes mean that
    :func:`iris.experimental.ugrid.utils.recombine_submeshes` now uses ~3x as
    much memory; testing shows a ~16-million point mesh will now use ~600MB.
-   Investigated by `@pp-mo` and `@trexfeathers`. (:issue:`4845`)
+   Investigated by `@pp-mo`_ and `@trexfeathers`_. (:issue:`4845`)
 
 
 🔥 Deprecations
@@ -339,3 +369,5 @@ This document explains the changes made to Iris for this release
 .. _PyData Sphinx Theme: https://pydata-sphinx-theme.readthedocs.io/en/stable/index.html
 .. _pytest: https://docs.pytest.org
 .. _setuptools-scm: https://github.com/pypa/setuptools_scm
+.. _SciTools/cartopy@fcb784d: https://github.com/SciTools/cartopy/commit/fcb784daa65d95ed9a74b02ca292801c02bc4108
+.. _SciTools/cartopy@8860a81: https://github.com/SciTools/cartopy/commit/8860a8186d4dc62478e74c83f3b2b3e8f791372e
diff --git a/lib/iris/_representation/cube_printout.py b/lib/iris/_representation/cube_printout.py
index b0b569512d..ea32fc5126 100644
--- a/lib/iris/_representation/cube_printout.py
+++ b/lib/iris/_representation/cube_printout.py
@@ -260,7 +260,10 @@ def add_scalar_row(name, value=""):
                 elif title in ("attributes:", "cell methods:", "mesh:"):
                     for title, value in zip(sect.names, sect.values):
                         add_scalar_row(title, value)
-                elif title == "scalar cell measures:":
+                elif title in (
+                    "scalar ancillary variables:",
+                    "scalar cell measures:",
+                ):
                     # These are just strings: nothing in the 'value' column.
                     for name in sect.contents:
                         add_scalar_row(name)
diff --git a/lib/iris/_representation/cube_summary.py b/lib/iris/_representation/cube_summary.py
index 885de9acf3..6b0d4cf0f3 100644
--- a/lib/iris/_representation/cube_summary.py
+++ b/lib/iris/_representation/cube_summary.py
@@ -219,6 +219,12 @@ def __init__(self, title, cell_measures):
         self.contents = [cm.name() for cm in cell_measures]
 
 
+class ScalarAncillaryVariableSection(Section):
+    def __init__(self, title, ancillary_variables):
+        self.title = title
+        self.contents = [av.name() for av in ancillary_variables]
+
+
 class AttributeSection(Section):
     def __init__(self, title, attributes):
         self.title = title
@@ -274,7 +280,7 @@ class CubeSummary:
 
     """
 
-    def __init__(self, cube, shorten=False, name_padding=35):
+    def __init__(self, cube, name_padding=35):
         self.header = FullHeader(cube, name_padding)
 
         # Cache the derived coords so we can rely on consistent
@@ -314,13 +320,23 @@ def __init__(self, cube, shorten=False, name_padding=35):
             if id(coord) not in scalar_coord_ids
         ]
 
-        # cell measures
-        vector_cell_measures = [
-            cm for cm in cube.cell_measures() if cm.shape != (1,)
-        ]
-
         # Ancillary Variables
-        vector_ancillary_variables = [av for av in cube.ancillary_variables()]
+        vector_ancillary_variables = []
+        scalar_ancillary_variables = []
+        for av, av_dims in cube._ancillary_variables_and_dims:
+            if av_dims:
+                vector_ancillary_variables.append(av)
+            else:
+                scalar_ancillary_variables.append(av)
+
+        # Cell Measures
+        vector_cell_measures = []
+        scalar_cell_measures = []
+        for cm, cm_dims in cube._cell_measures_and_dims:
+            if cm_dims:
+                vector_cell_measures.append(cm)
+            else:
+                scalar_cell_measures.append(cm)
 
         # Sort scalar coordinates by name.
         scalar_coords.sort(key=lambda coord: coord.name())
@@ -334,9 +350,6 @@ def __init__(self, cube, shorten=False, name_padding=35):
         vector_derived_coords.sort(
             key=lambda coord: (cube.coord_dims(coord), coord.name())
         )
-        scalar_cell_measures = [
-            cm for cm in cube.cell_measures() if cm.shape == (1,)
-        ]
 
         self.vector_sections = {}
 
@@ -369,6 +382,11 @@ def add_scalar_section(section_class, title, *args):
             "Scalar cell measures:",
             scalar_cell_measures,
         )
+        add_scalar_section(
+            ScalarAncillaryVariableSection,
+            "Scalar ancillary variables:",
+            scalar_ancillary_variables,
+        )
         add_scalar_section(
             CellMethodSection, "Cell methods:", cube.cell_methods
         )
diff --git a/lib/iris/analysis/__init__.py b/lib/iris/analysis/__init__.py
index 11810f2901..c0f9b5b0f8 100644
--- a/lib/iris/analysis/__init__.py
+++ b/lib/iris/analysis/__init__.py
@@ -1778,7 +1778,7 @@ def interp_order(length):
 
 .. seealso:: The :func:`~iris.analysis.PROPORTION` aggregator.
 
-This aggregator handles masked data.
+This aggregator handles masked data and lazy data.
 
 """
 
@@ -1808,7 +1808,7 @@ def interp_order(length):
     result = precip_cube.collapsed('time', iris.analysis.MAX_RUN,
                                    function=lambda values: values > 10)
 
-This aggregator handles masked data, which it treats as interrupting a run.
+This aggregator handles masked data, which it treats as interrupting a run, and lazy data.
 
 """
 MAX_RUN.name = lambda: "max_run"
@@ -1826,7 +1826,7 @@ def interp_order(length):
 
     result = cube.collapsed('longitude', iris.analysis.GMEAN)
 
-This aggregator handles masked data.
+This aggregator handles masked data, but NOT lazy data.
 
 """
 
@@ -1848,7 +1848,7 @@ def interp_order(length):
     The harmonic mean is only valid if all data values are greater
     than zero.
 
-This aggregator handles masked data.
+This aggregator handles masked data, but NOT lazy data.
 
 """
 
@@ -1914,7 +1914,7 @@ def interp_order(length):
 
     result = cube.collapsed('longitude', iris.analysis.MEDIAN)
 
-This aggregator handles masked data.
+This aggregator handles masked data, but NOT lazy data.
 
 """
 
@@ -1933,7 +1933,7 @@ def interp_order(length):
 
     result = cube.collapsed('longitude', iris.analysis.MIN)
 
-This aggregator handles masked data.
+This aggregator handles masked data and lazy data.
 
 """
 
@@ -1952,7 +1952,7 @@ def interp_order(length):
 
     result = cube.collapsed('longitude', iris.analysis.MAX)
 
-This aggregator handles masked data.
+This aggregator handles masked data and lazy data.
 
 """
 
@@ -1978,7 +1978,7 @@ def interp_order(length):
 
     result = cube.collapsed('time', iris.analysis.PEAK)
 
-This aggregator handles masked data.
+This aggregator handles masked data but NOT lazy data.
 
 """
 
@@ -2058,7 +2058,7 @@ def interp_order(length):
 
 .. seealso:: The :func:`~iris.analysis.COUNT` aggregator.
 
-This aggregator handles masked data.
+This aggregator handles masked data, but NOT lazy data.
 
 """
 
@@ -2084,7 +2084,7 @@ def interp_order(length):
 
     result = cube.collapsed('longitude', iris.analysis.RMS)
 
-This aggregator handles masked data.
+This aggregator handles masked data and lazy data.
 
 """
 
@@ -2118,7 +2118,7 @@ def interp_order(length):
 
 .. note::
 
-    Lazy operation is supported, via :func:`dask.array.nanstd`.
+    Lazy operation is supported, via :func:`dask.array.std`.
 
 This aggregator handles masked data.
 
@@ -2157,7 +2157,7 @@ def interp_order(length):
     result = cube.rolling_window('time', iris.analysis.SUM,
                                  len(weights), weights=weights)
 
-This aggregator handles masked data.
+This aggregator handles masked data and lazy data.
 
 """
 
@@ -2192,9 +2192,9 @@ def interp_order(length):
 
 .. note::
 
-    Lazy operation is supported, via :func:`dask.array.nanvar`.
+    Lazy operation is supported, via :func:`dask.array.var`.
 
-This aggregator handles masked data.
+This aggregator handles masked data and lazy data.
 
 """
 
@@ -2226,6 +2226,11 @@ def interp_order(length):
     :func:`scipy.interpolate.interp1d` Defaults to "linear", which is
     equivalent to alphap=0.5, betap=0.5 in `iris.analysis.PERCENTILE`
 
+Notes
+------
+This function does not maintain laziness when called; it realises data.
+See more at :doc:`/userguide/real_and_lazy_data`.
+
 """
 
 
@@ -2619,6 +2624,11 @@ def clear_phenomenon_identity(cube):
     Helper function to clear the standard_name, attributes, and
     cell_methods of a cube.
 
+
+    Notes
+    ------
+    This function maintains laziness when called; it does not realise data.
+    See more at :doc:`/userguide/real_and_lazy_data`.
     """
     cube.rename(None)
     cube.attributes.clear()
diff --git a/lib/iris/analysis/_area_weighted.py b/lib/iris/analysis/_area_weighted.py
index 8381185e58..edbfd41ef9 100644
--- a/lib/iris/analysis/_area_weighted.py
+++ b/lib/iris/analysis/_area_weighted.py
@@ -11,7 +11,7 @@
 
 from iris._lazy_data import map_complete_blocks
 from iris.analysis._interpolation import get_xy_dim_coords, snapshot_grid
-from iris.analysis._regrid import RectilinearRegridder
+from iris.analysis._regrid import RectilinearRegridder, _create_cube
 import iris.analysis.cartography
 import iris.coord_systems
 from iris.util import _meshgrid
@@ -1111,18 +1111,32 @@ def _regrid_area_weighted_rectilinear_src_and_grid__perform(
     )
 
     # Wrap up the data as a Cube.
-    regrid_callback = RectilinearRegridder._regrid
-    new_cube = RectilinearRegridder._create_cube(
+
+    _regrid_callback = functools.partial(
+        RectilinearRegridder._regrid,
+        src_x_coord=src_x,
+        src_y_coord=src_y,
+        sample_grid_x=meshgrid_x,
+        sample_grid_y=meshgrid_y,
+    )
+    # TODO: investigate if an area weighted callback would be more appropriate.
+    # _regrid_callback = functools.partial(
+    #     _regrid_area_weighted_array,
+    #     weights_info=weights_info,
+    #     index_info=index_info,
+    #     mdtol=mdtol,
+    # )
+
+    def regrid_callback(*args, **kwargs):
+        _data, dims = args
+        return _regrid_callback(_data, *dims, **kwargs)
+
+    new_cube = _create_cube(
         new_data,
         src_cube,
-        src_x_dim,
-        src_y_dim,
-        src_x,
-        src_y,
-        grid_x,
-        grid_y,
-        meshgrid_x,
-        meshgrid_y,
+        [src_x_dim, src_y_dim],
+        [grid_x, grid_y],
+        2,
         regrid_callback,
     )
 
diff --git a/lib/iris/analysis/_grid_angles.py b/lib/iris/analysis/_grid_angles.py
index 0b52f54568..4cb449ae51 100644
--- a/lib/iris/analysis/_grid_angles.py
+++ b/lib/iris/analysis/_grid_angles.py
@@ -449,6 +449,11 @@ def rotate_grid_vectors(
 
             Vector magnitudes will always be the same as the inputs.
 
+    .. note::
+
+        This function does not maintain laziness when called; it realises data.
+        See more at :doc:`/userguide/real_and_lazy_data`.
+
     """
     u_out, v_out = (cube.copy() for cube in (u_cube, v_cube))
     if not grid_angles_cube:
diff --git a/lib/iris/analysis/_regrid.py b/lib/iris/analysis/_regrid.py
index 5c7439b0ce..f1891a48e4 100644
--- a/lib/iris/analysis/_regrid.py
+++ b/lib/iris/analysis/_regrid.py
@@ -11,7 +11,6 @@
 import numpy as np
 import numpy.ma as ma
 from scipy.sparse import csc_matrix
-from scipy.sparse import diags as sparse_diags
 
 from iris._lazy_data import map_complete_blocks
 from iris.analysis._interpolation import (
@@ -21,7 +20,7 @@
     snapshot_grid,
 )
 from iris.analysis._scipy_interpolate import _RegularGridInterpolator
-from iris.util import _meshgrid
+from iris.util import _meshgrid, guess_coord_axis
 
 
 def _transform_xy_arrays(crs_from, x, y, crs_to):
@@ -52,18 +51,20 @@ def _regrid_weighted_curvilinear_to_rectilinear__prepare(
     First (setup) part of 'regrid_weighted_curvilinear_to_rectilinear'.
 
     Check inputs and calculate the sparse regrid matrix and related info.
-    The 'regrid info' returned can be re-used over many 2d slices.
+    The 'regrid info' returned can be re-used over many cubes.
 
     """
-    if src_cube.aux_factories:
-        msg = "All source cube derived coordinates will be ignored."
-        warnings.warn(msg)
 
     # Get the source cube x and y 2D auxiliary coordinates.
     sx, sy = src_cube.coord(axis="x"), src_cube.coord(axis="y")
     # Get the target grid cube x and y dimension coordinates.
     tx, ty = get_xy_dim_coords(grid_cube)
 
+    sl = [0] * grid_cube.ndim
+    sl[grid_cube.coord_dims(tx)[0]] = np.s_[:]
+    sl[grid_cube.coord_dims(ty)[0]] = np.s_[:]
+    grid_cube = grid_cube[tuple(sl)]
+
     if sx.units != sy.units:
         msg = (
             "The source cube x ({!r}) and y ({!r}) coordinates must "
@@ -287,83 +288,108 @@ def _regrid_indices(cells, depth, points):
     return regrid_info
 
 
-def _regrid_weighted_curvilinear_to_rectilinear__perform(
-    src_cube, regrid_info
+def _curvilinear_to_rectilinear_regrid_data(
+    data,
+    dims,
+    regrid_info,
 ):
     """
-    Second (regrid) part of 'regrid_weighted_curvilinear_to_rectilinear'.
+    Part of 'regrid_weighted_curvilinear_to_rectilinear' which acts on the data.
 
-    Perform the prepared regrid calculation on a single 2d cube.
+    Perform the prepared regrid calculation on an array.
 
     """
-    from iris.cube import Cube
-
     sparse_matrix, sum_weights, rows, grid_cube = regrid_info
 
+    inds = list(range(-len(dims), 0))
+    data = np.moveaxis(data, dims, inds)
+    data_shape = data.shape
+    grid_size = np.prod([data_shape[ind] for ind in inds])
+
     # Calculate the numerator of the weighted mean (M, 1).
-    is_masked = ma.isMaskedArray(src_cube.data)
+    is_masked = ma.isMaskedArray(data)
+    sum_weights = None
     if not is_masked:
-        data = src_cube.data
+        data = data
     else:
         # Use raw data array
-        data = src_cube.data.data
+        r_data = data.data
         # Check if there are any masked source points to take account of.
-        is_masked = np.ma.is_masked(src_cube.data)
+        is_masked = ma.is_masked(data)
         if is_masked:
             # Zero any masked source points so they add nothing in output sums.
-            mask = src_cube.data.mask
-            data[mask] = 0.0
+            mask = data.mask
+            r_data[mask] = 0.0
             # Calculate a new 'sum_weights' to allow for missing source points.
             # N.B. it is more efficient to use the original once-calculated
             # sparse matrix, but in this case we can't.
             # Hopefully, this post-multiplying by the validities is less costly
             # than repeating the whole sparse calculation.
-            valid_src_cells = ~mask.flat[:]
-            src_cell_validity_factors = sparse_diags(
-                np.array(valid_src_cells, dtype=int), 0
-            )
-            valid_weights = sparse_matrix * src_cell_validity_factors
-            sum_weights = valid_weights.sum(axis=1).getA()
-            # Work out where output cells are missing all contributions.
-            # This allows for where 'rows' contains output cells that have no
-            # data because of missing input points.
-            zero_sums = sum_weights == 0.0
-            # Make sure we can still divide by sum_weights[rows].
-            sum_weights[zero_sums] = 1.0
+            valid_src_cells = ~mask.reshape(-1, grid_size)
+            sum_weights = valid_src_cells @ sparse_matrix.T
+        data = r_data
+    if sum_weights is None:
+        sum_weights = (
+            np.ones(data_shape).reshape(-1, grid_size) @ sparse_matrix.T
+        )
+    # Work out where output cells are missing all contributions.
+    # This allows for where 'rows' contains output cells that have no
+    # data because of missing input points.
+    zero_sums = sum_weights == 0.0
+    # Make sure we can still divide by sum_weights[rows].
+    sum_weights[zero_sums] = 1.0
 
     # Calculate sum in each target cell, over contributions from each source
     # cell.
-    numerator = sparse_matrix * data.reshape(-1, 1)
-
-    # Create a template for the weighted mean result.
-    weighted_mean = ma.masked_all(numerator.shape, dtype=numerator.dtype)
-
-    # Calculate final results in all relevant places.
-    weighted_mean[rows] = numerator[rows] / sum_weights[rows]
-    if is_masked:
-        # Ensure masked points where relevant source cells were all missing.
-        if np.any(zero_sums):
-            # Make masked if it wasn't.
-            weighted_mean = np.ma.asarray(weighted_mean)
-            # Mask where contributing sums were zero.
-            weighted_mean[zero_sums] = np.ma.masked
-
-    # Construct the final regridded weighted mean cube.
+    numerator = data.reshape(-1, grid_size) @ sparse_matrix.T
+
+    weighted_mean = numerator / sum_weights
+    # Ensure masked points where relevant source cells were all missing.
+    weighted_mean = ma.asarray(weighted_mean)
+    if np.any(zero_sums):
+        # Mask where contributing sums were zero.
+        weighted_mean[zero_sums] = ma.masked
+
+    new_data_shape = list(data_shape)
+    for dim, length in zip(inds, grid_cube.shape):
+        new_data_shape[dim] = length
+    if len(dims) == 1:
+        new_data_shape.append(grid_cube.shape[1])
+        dims = (dims[0], dims[0] + 1)
+    if len(dims) > 2:
+        new_data_shape = new_data_shape[: 2 - len(dims)]
+        dims = dims[:2]
+
+    result = weighted_mean.reshape(new_data_shape)
+    result = np.moveaxis(result, [-2, -1], dims)
+    return result
+
+
+def _regrid_weighted_curvilinear_to_rectilinear__perform(
+    src_cube, regrid_info
+):
+    """
+    Second (regrid) part of 'regrid_weighted_curvilinear_to_rectilinear'.
+
+    Perform the prepared regrid calculation on a single cube.
+
+    """
+    dims = src_cube.coord_dims(
+        CurvilinearRegridder._get_horizontal_coord(src_cube, "x")
+    )
+    result_data = _curvilinear_to_rectilinear_regrid_data(
+        src_cube.data, dims, regrid_info
+    )
+    grid_cube = regrid_info[-1]
     tx = grid_cube.coord(axis="x", dim_coords=True)
     ty = grid_cube.coord(axis="y", dim_coords=True)
-    (tx_dim,) = grid_cube.coord_dims(tx)
-    (ty_dim,) = grid_cube.coord_dims(ty)
-    dim_coords_and_dims = list(zip((ty.copy(), tx.copy()), (ty_dim, tx_dim)))
-    cube = Cube(
-        weighted_mean.reshape(grid_cube.shape),
-        dim_coords_and_dims=dim_coords_and_dims,
+    regrid_callback = functools.partial(
+        _curvilinear_to_rectilinear_regrid_data, regrid_info=regrid_info
     )
-    cube.metadata = copy.deepcopy(src_cube.metadata)
-
-    for coord in src_cube.coords(dimensions=()):
-        cube.add_aux_coord(coord.copy())
-
-    return cube
+    result = _create_cube(
+        result_data, src_cube, dims, (ty.copy(), tx.copy()), 2, regrid_callback
+    )
+    return result
 
 
 class CurvilinearRegridder:
@@ -457,7 +483,7 @@ def __call__(self, src):
             point-in-cell regridding.
 
         """
-        from iris.cube import Cube, CubeList
+        from iris.cube import Cube
 
         # Validity checks.
         if not isinstance(src, Cube):
@@ -473,30 +499,18 @@ def __call__(self, src):
                 "The given cube is not defined on the same "
                 "source grid as this regridder."
             )
-
-        # Call the regridder function.
-        # This includes repeating over any non-XY dimensions, because the
-        # underlying routine does not support this.
-        # FOR NOW: we will use cube.slices and merge to achieve this,
-        # though that is not a terribly efficient method ...
-        # TODO: create a template result cube and paste data slices into it,
-        # which would be more efficient.
-        result_slices = CubeList([])
-        for slice_cube in src.slices(sx):
-            if self._regrid_info is None:
-                # Calculate the basic regrid info just once.
-                self._regrid_info = (
-                    _regrid_weighted_curvilinear_to_rectilinear__prepare(
-                        slice_cube, self.weights, self._target_cube
-                    )
-                )
-            slice_result = (
-                _regrid_weighted_curvilinear_to_rectilinear__perform(
-                    slice_cube, self._regrid_info
+        slice_cube = next(src.slices(sx))
+        if self._regrid_info is None:
+            # Calculate the basic regrid info just once.
+            self._regrid_info = (
+                _regrid_weighted_curvilinear_to_rectilinear__prepare(
+                    slice_cube, self.weights, self._target_cube
                 )
             )
-            result_slices.append(slice_result)
-        result = result_slices.merge_cube()
+        result = _regrid_weighted_curvilinear_to_rectilinear__perform(
+            src, self._regrid_info
+        )
+
         return result
 
 
@@ -688,11 +702,23 @@ def _regrid(
 
         # Prepare the result data array
         shape = list(src_data.shape)
-        assert shape[x_dim] == src_x_coord.shape[0]
-        assert shape[y_dim] == src_y_coord.shape[0]
-
-        shape[y_dim] = sample_grid_x.shape[0]
-        shape[x_dim] = sample_grid_x.shape[1]
+        final_shape = shape.copy()
+        if x_dim is not None:
+            assert shape[x_dim] == src_x_coord.shape[0]
+            shape[x_dim] = sample_grid_x.shape[1]
+            final_shape[x_dim] = shape[x_dim]
+        else:
+            shape.append(1)
+            x_dim = len(shape) - 1
+            src_data = np.expand_dims(src_data, -1)
+        if y_dim is not None:
+            assert shape[y_dim] == src_y_coord.shape[0]
+            shape[y_dim] = sample_grid_x.shape[0]
+            final_shape[y_dim] = shape[y_dim]
+        else:
+            shape.append(1)
+            y_dim = len(shape) - 1
+            src_data = np.expand_dims(src_data, -1)
 
         dtype = src_data.dtype
         if method == "linear":
@@ -714,7 +740,11 @@ def _regrid(
             if src_x_coord.points.size > 1
             else False
         )
-        reverse_y = src_y_coord.points[0] > src_y_coord.points[1]
+        reverse_y = (
+            src_y_coord.points[0] > src_y_coord.points[1]
+            if src_y_coord.points.size > 1
+            else False
+        )
         flip_index = [slice(None)] * src_data.ndim
         if reverse_x:
             src_x_coord = src_x_coord[::-1]
@@ -733,7 +763,7 @@ def _regrid(
 
         # Slice out the first full 2D piece of data for construction of the
         # interpolator.
-        index = [0] * src_data.ndim
+        index = [0] * len(shape)
         index[x_dim] = index[y_dim] = slice(None)
         initial_data = src_data[tuple(index)]
         if y_dim < x_dim:
@@ -808,166 +838,21 @@ def interpolate(data):
             if ma.isMaskedArray(data) or mode.force_mask:
                 # NB. np.ma.getmaskarray returns an array of `False` if
                 # `src_subset` is not a masked array.
-                src_mask = np.ma.getmaskarray(src_subset)
+                src_mask = ma.getmaskarray(src_subset)
                 interpolator.fill_value = mode.mask_fill_value
                 mask_fraction = interpolate(src_mask)
                 new_mask = mask_fraction > 0
 
-                if np.ma.isMaskedArray(data):
+                if ma.isMaskedArray(data):
                     data.mask[tuple(index)] = new_mask
                 elif np.any(new_mask):
                     # Set mask=False to ensure we have an expanded mask array.
-                    data = np.ma.MaskedArray(data, mask=False)
+                    data = ma.MaskedArray(data, mask=False)
                     data.mask[tuple(index)] = new_mask
 
+        data = data.reshape(final_shape)
         return data
 
-    @staticmethod
-    def _create_cube(
-        data,
-        src,
-        x_dim,
-        y_dim,
-        src_x_coord,
-        src_y_coord,
-        grid_x_coord,
-        grid_y_coord,
-        sample_grid_x,
-        sample_grid_y,
-        regrid_callback,
-    ):
-        """
-        Return a new Cube for the result of regridding the source Cube onto
-        the new grid.
-
-        All the metadata and coordinates of the result Cube are copied from
-        the source Cube, with two exceptions:
-            - Grid dimension coordinates are copied from the grid Cube.
-            - Auxiliary coordinates which span the grid dimensions are
-              ignored, except where they provide a reference surface for an
-              :class:`iris.aux_factory.AuxCoordFactory`.
-
-        Args:
-
-        * data:
-            The regridded data as an N-dimensional NumPy array.
-        * src:
-            The source Cube.
-        * x_dim:
-            The X dimension within the source Cube.
-        * y_dim:
-            The Y dimension within the source Cube.
-        * src_x_coord:
-            The X :class:`iris.coords.DimCoord`.
-        * src_y_coord:
-            The Y :class:`iris.coords.DimCoord`.
-        * grid_x_coord:
-            The :class:`iris.coords.DimCoord` for the new grid's X
-            coordinate.
-        * grid_y_coord:
-            The :class:`iris.coords.DimCoord` for the new grid's Y
-            coordinate.
-        * sample_grid_x:
-            A 2-dimensional array of sample X values.
-        * sample_grid_y:
-            A 2-dimensional array of sample Y values.
-        * regrid_callback:
-            The routine that will be used to calculate the interpolated
-            values of any reference surfaces.
-
-        Returns:
-            The new, regridded Cube.
-
-        """
-        from iris.cube import Cube
-
-        #
-        # XXX: At the moment requires to be a static method as used by
-        # experimental regrid_area_weighted_rectilinear_src_and_grid
-        #
-        # Create a result cube with the appropriate metadata
-        result = Cube(data)
-        result.metadata = copy.deepcopy(src.metadata)
-
-        # Copy across all the coordinates which don't span the grid.
-        # Record a mapping from old coordinate IDs to new coordinates,
-        # for subsequent use in creating updated aux_factories.
-        coord_mapping = {}
-
-        def copy_coords(src_coords, add_method):
-            for coord in src_coords:
-                dims = src.coord_dims(coord)
-                if coord == src_x_coord:
-                    coord = grid_x_coord
-                elif coord == src_y_coord:
-                    coord = grid_y_coord
-                elif x_dim in dims or y_dim in dims:
-                    continue
-                result_coord = coord.copy()
-                add_method(result_coord, dims)
-                coord_mapping[id(coord)] = result_coord
-
-        copy_coords(src.dim_coords, result.add_dim_coord)
-        copy_coords(src.aux_coords, result.add_aux_coord)
-
-        def regrid_reference_surface(
-            src_surface_coord,
-            surface_dims,
-            x_dim,
-            y_dim,
-            src_x_coord,
-            src_y_coord,
-            sample_grid_x,
-            sample_grid_y,
-            regrid_callback,
-        ):
-            # Determine which of the reference surface's dimensions span the X
-            # and Y dimensions of the source cube.
-            surface_x_dim = surface_dims.index(x_dim)
-            surface_y_dim = surface_dims.index(y_dim)
-            surface = regrid_callback(
-                src_surface_coord.points,
-                surface_x_dim,
-                surface_y_dim,
-                src_x_coord,
-                src_y_coord,
-                sample_grid_x,
-                sample_grid_y,
-            )
-            surface_coord = src_surface_coord.copy(surface)
-            return surface_coord
-
-        # Copy across any AuxFactory instances, and regrid their reference
-        # surfaces where required.
-        for factory in src.aux_factories:
-            for coord in factory.dependencies.values():
-                if coord is None:
-                    continue
-                dims = src.coord_dims(coord)
-                if x_dim in dims and y_dim in dims:
-                    result_coord = regrid_reference_surface(
-                        coord,
-                        dims,
-                        x_dim,
-                        y_dim,
-                        src_x_coord,
-                        src_y_coord,
-                        sample_grid_x,
-                        sample_grid_y,
-                        regrid_callback,
-                    )
-                    result.add_aux_coord(result_coord, dims)
-                    coord_mapping[id(coord)] = result_coord
-            try:
-                result.add_aux_factory(factory.updated(coord_mapping))
-            except KeyError:
-                msg = (
-                    "Cannot update aux_factory {!r} because of dropped"
-                    " coordinates.".format(factory.name())
-                )
-                warnings.warn(msg)
-        return result
-
     def _check_units(self, coord):
         from iris.coord_systems import GeogCS, RotatedGeogCS
 
@@ -1089,20 +974,168 @@ def __call__(self, src):
         )
 
         # Wrap up the data as a Cube.
-        regrid_callback = functools.partial(
-            self._regrid, method=self._method, extrapolation_mode="nan"
+        _regrid_callback = functools.partial(
+            self._regrid,
+            src_x_coord=src_x_coord,
+            src_y_coord=src_y_coord,
+            sample_grid_x=sample_grid_x,
+            sample_grid_y=sample_grid_y,
+            method=self._method,
+            extrapolation_mode="nan",
         )
-        result = self._create_cube(
+
+        def regrid_callback(*args, **kwargs):
+            _data, dims = args
+            return _regrid_callback(_data, *dims, **kwargs)
+
+        result = _create_cube(
             data,
             src,
-            x_dim,
-            y_dim,
-            src_x_coord,
-            src_y_coord,
-            grid_x_coord,
-            grid_y_coord,
-            sample_grid_x,
-            sample_grid_y,
+            [x_dim, y_dim],
+            [grid_x_coord, grid_y_coord],
+            2,
             regrid_callback,
         )
         return result
+
+
+def _create_cube(
+    data, src, src_dims, tgt_coords, num_tgt_dims, regrid_callback
+):
+    r"""
+    Return a new cube for the result of regridding.
+    Returned cube represents the result of regridding the source cube
+    onto the horizontal coordinates (e.g. latitude) of the target cube.
+    All the metadata and coordinates of the result cube are copied from
+    the source cube, with two exceptions:
+        - Horizontal coordinates are copied from the target cube.
+        - Auxiliary coordinates which span the grid dimensions are
+          ignored.
+    Parameters
+    ----------
+    data : array
+        The regridded data as an N-dimensional NumPy array.
+    src : cube
+        The source Cube.
+    src_dims : tuple of int
+        The dimensions of the X and Y coordinate within the source Cube.
+    tgt_coords : tuple of :class:`iris.coords.Coord`\\ 's
+        Either two 1D :class:`iris.coords.DimCoord`\\ 's, two 1D
+        :class:`iris.experimental.ugrid.DimCoord`\\ 's or two ND
+        :class:`iris.coords.AuxCoord`\\ 's representing the new grid's
+        X and Y coordinates.
+    num_tgt_dims : int
+        The number of dimensions that the `tgt_coords` span.
+    regrid_callback : callable
+        The routine that will be used to calculate the interpolated
+        values of any reference surfaces.
+    Returns
+    -------
+    cube
+        A new iris.cube.Cube instance.
+    """
+    from iris.coords import DimCoord
+    from iris.cube import Cube
+
+    result = Cube(data)
+
+    if len(src_dims) >= 2:
+        grid_dim_x, grid_dim_y = src_dims[:2]
+    elif len(src_dims) == 1:
+        grid_dim_x = src_dims[0]
+        grid_dim_y = grid_dim_x + 1
+
+    if num_tgt_dims == 1:
+        grid_dim_x = grid_dim_y = min(src_dims)
+    for tgt_coord, dim in zip(tgt_coords, (grid_dim_x, grid_dim_y)):
+        if len(tgt_coord.shape) == 1:
+            if isinstance(tgt_coord, DimCoord) and dim is not None:
+                result.add_dim_coord(tgt_coord, dim)
+            else:
+                result.add_aux_coord(tgt_coord, dim)
+        else:
+            result.add_aux_coord(tgt_coord, (grid_dim_y, grid_dim_x))
+
+    result.metadata = copy.deepcopy(src.metadata)
+
+    # Copy across all the coordinates which don't span the grid.
+    # Record a mapping from old coordinate IDs to new coordinates,
+    # for subsequent use in creating updated aux_factories.
+
+    coord_mapping = {}
+
+    def copy_coords(src_coords, add_method):
+        for coord in src_coords:
+            dims = src.coord_dims(coord)
+            if set(src_dims).intersection(set(dims)):
+                continue
+            if guess_coord_axis(coord) in ["X", "Y"]:
+                continue
+
+            def dim_offset(dim):
+                offset = sum(
+                    [
+                        d <= dim
+                        for d in (grid_dim_x, grid_dim_y)
+                        if d is not None
+                    ]
+                )
+                if offset and num_tgt_dims == 1:
+                    offset -= 1
+                offset -= sum([d <= dim for d in src_dims if d is not None])
+                return dim + offset
+
+            dims = [dim_offset(dim) for dim in dims]
+            result_coord = coord.copy()
+            # Add result_coord to the owner of add_method.
+            add_method(result_coord, dims)
+            coord_mapping[id(coord)] = result_coord
+
+    copy_coords(src.dim_coords, result.add_dim_coord)
+    copy_coords(src.aux_coords, result.add_aux_coord)
+
+    def regrid_reference_surface(
+        src_surface_coord,
+        surface_dims,
+        src_dims,
+        regrid_callback,
+    ):
+        # Determine which of the reference surface's dimensions span the X
+        # and Y dimensions of the source cube.
+        relative_surface_dims = [
+            surface_dims.index(dim) if dim is not None else None
+            for dim in src_dims
+        ]
+        surface = regrid_callback(
+            src_surface_coord.points,
+            relative_surface_dims,
+        )
+        surface_coord = src_surface_coord.copy(surface)
+        return surface_coord
+
+    # Copy across any AuxFactory instances, and regrid their reference
+    # surfaces where required.
+    for factory in src.aux_factories:
+        for coord in factory.dependencies.values():
+            if coord is None:
+                continue
+            dims = src.coord_dims(coord)
+            if set(src_dims).intersection(dims):
+                result_coord = regrid_reference_surface(
+                    coord,
+                    dims,
+                    src_dims,
+                    regrid_callback,
+                )
+                result.add_aux_coord(result_coord, dims)
+                coord_mapping[id(coord)] = result_coord
+        try:
+            result.add_aux_factory(factory.updated(coord_mapping))
+        except KeyError:
+            msg = (
+                "Cannot update aux_factory {!r} because of dropped"
+                " coordinates.".format(factory.name())
+            )
+            warnings.warn(msg)
+
+    return result
diff --git a/lib/iris/analysis/calculus.py b/lib/iris/analysis/calculus.py
index 4630f47967..c530dbd216 100644
--- a/lib/iris/analysis/calculus.py
+++ b/lib/iris/analysis/calculus.py
@@ -147,6 +147,12 @@ def cube_delta(cube, coord):
 
     .. note:: Missing data support not yet implemented.
 
+    .. note::
+
+            This function does not maintain laziness when called; it realises data.
+            See more at :doc:`/userguide/real_and_lazy_data`.
+
+
     """
     # handle the case where a user passes a coordinate name
     if isinstance(coord, str):
@@ -251,6 +257,11 @@ def differentiate(cube, coord_to_differentiate):
 
     .. note:: Spherical differentiation does not occur in this routine.
 
+    .. note::
+
+            This function does not maintain laziness when called; it realises data.
+            See more at :doc:`/userguide/real_and_lazy_data`.
+
     """
     # Get the delta cube in the required differential direction.
     # This operation results in a copy of the original cube.
@@ -532,6 +543,12 @@ def curl(i_cube, j_cube, k_cube=None):
 
             where phi is longitude, theta is latitude.
 
+    .. note::
+
+            This function does not maintain laziness when called; it realises data.
+            See more at :doc:`/userguide/real_and_lazy_data`.
+
+
     """
     # Get the vector quantity names.
     # (i.e. ['easterly', 'northerly', 'vertical'])
@@ -741,6 +758,12 @@ def spatial_vectors_with_phenom_name(i_cube, j_cube, k_cube=None):
 #doctest: +SKIP
         (['u', 'v', 'w'], 'wind')
 
+    Notes
+    ------
+            This function maintains laziness when called; it does not realise data.
+            See more at :doc:`/userguide/real_and_lazy_data`.
+
+
     """
     directional_names = (
         ("u", "v", "w"),
diff --git a/lib/iris/analysis/cartography.py b/lib/iris/analysis/cartography.py
index 44129ff175..a8e90a63ad 100644
--- a/lib/iris/analysis/cartography.py
+++ b/lib/iris/analysis/cartography.py
@@ -66,6 +66,10 @@ def wrap_lons(lons, base, period):
         >>> print(wrap_lons(np.array([185, 30, -200, 75]), -180, 360))
         [-175.   30.  160.   75.]
 
+    Notes
+    ------
+    This function maintains laziness when called; it does not realise data.
+    See more at :doc:`/userguide/real_and_lazy_data`.
     """
     # It is important to use 64bit floating precision when changing a floats
     # numbers range.
@@ -169,20 +173,25 @@ def rotate_pole(lons, lats, pole_lon, pole_lat):
 
 
 def _get_lon_lat_coords(cube):
-    lat_coords = [
-        coord for coord in cube.coords() if "latitude" in coord.name()
-    ]
-    lon_coords = [
-        coord for coord in cube.coords() if "longitude" in coord.name()
-    ]
+    def search_for_coord(coord_iterable, coord_name):
+        return [
+            coord for coord in coord_iterable if coord_name in coord.name()
+        ]
+
+    lat_coords = search_for_coord(
+        cube.dim_coords, "latitude"
+    ) or search_for_coord(cube.coords(), "latitude")
+    lon_coords = search_for_coord(
+        cube.dim_coords, "longitude"
+    ) or search_for_coord(cube.coords(), "longitude")
     if len(lat_coords) > 1 or len(lon_coords) > 1:
         raise ValueError(
-            "Calling `_get_lon_lat_coords` with multiple lat or lon coords"
+            "Calling `_get_lon_lat_coords` with multiple same-type (i.e. dim/aux) lat or lon coords"
             " is currently disallowed"
         )
     lat_coord = lat_coords[0]
     lon_coord = lon_coords[0]
-    return (lon_coord, lat_coord)
+    return lon_coord, lat_coord
 
 
 def _xy_range(cube, mode=None):
@@ -266,6 +275,10 @@ def get_xy_grids(cube):
 
         x, y = get_xy_grids(cube)
 
+    Notes
+    ------
+    This function maintains laziness when called; it does not realise data.
+    See more at :doc:`/userguide/real_and_lazy_data`.
     """
     x_coord, y_coord = cube.coord(axis="X"), cube.coord(axis="Y")
 
@@ -294,6 +307,11 @@ def get_xy_contiguous_bounded_grids(cube):
 
         xs, ys = get_xy_contiguous_bounded_grids(cube)
 
+    Notes
+    ------
+    This function maintains laziness when called; it does not realise data.
+    See more at :doc:`/userguide/real_and_lazy_data`.
+
     """
     x_coord, y_coord = cube.coord(axis="X"), cube.coord(axis="Y")
 
@@ -493,6 +511,10 @@ def cosine_latitude_weights(cube):
         cube = iris.load_cube(iris.sample_data_path('air_temp.pp'))
         weights = np.sqrt(cosine_latitude_weights(cube))
 
+    Notes
+    ------
+    This function maintains laziness when called; it does not realise data.
+    See more at :doc:`/userguide/real_and_lazy_data`.
     """
     # Find all latitude coordinates, we want one and only one.
     lat_coords = [
@@ -578,6 +600,11 @@ def project(cube, target_proj, nx=None, ny=None):
         An instance of :class:`iris.cube.Cube` and a list describing the
         extent of the projection.
 
+    .. note::
+
+        If there are both dim and aux latitude-longitude coordinates, only
+        the dim coordinates will be used.
+
     .. note::
 
         This function assumes global data and will if necessary extrapolate
@@ -591,6 +618,11 @@ def project(cube, target_proj, nx=None, ny=None):
         resulting nearest neighbour values.  If masked, the value in the
         resulting cube is set to 0.
 
+    .. note::
+
+        This function does not maintain laziness when called; it realises data.
+        See more at :doc:`/userguide/real_and_lazy_data`.
+
     .. warning::
 
         This function uses a nearest neighbour approach rather than any form
@@ -1008,8 +1040,8 @@ def _transform_distance_vectors_tolerance_mask(
     u_one_t, v_zero_t = _transform_distance_vectors(ones, zeros, ds, dx2, dy2)
     u_zero_t, v_one_t = _transform_distance_vectors(zeros, ones, ds, dx2, dy2)
     # Squared magnitudes should be equal to one within acceptable tolerance.
-    # A value of atol=2e-3 is used, which corresponds to a change in magnitude
-    # of approximately 0.1%.
+    # A value of atol=2e-3 is used, which masks any magnitude changes >0.5%
+    #  (approx percentage - based on experimenting).
     sqmag_1_0 = u_one_t**2 + v_zero_t**2
     sqmag_0_1 = u_zero_t**2 + v_one_t**2
     mask = np.logical_not(
@@ -1065,6 +1097,11 @@ def rotate_winds(u_cube, v_cube, target_cs):
         The names of the output cubes are those of the inputs, prefixed with
         'transformed\_' (e.g. 'transformed_x_wind').
 
+    .. note::
+
+            This function does not maintain laziness when called; it realises data.
+            See more at :doc:`/userguide/real_and_lazy_data`.
+
     .. warning::
 
         Conversion between rotated-pole and non-rotated systems can be
diff --git a/lib/iris/analysis/geometry.py b/lib/iris/analysis/geometry.py
index a412a26ebc..b246b518d4 100644
--- a/lib/iris/analysis/geometry.py
+++ b/lib/iris/analysis/geometry.py
@@ -160,6 +160,11 @@ def geometry_area_weights(cube, geometry, normalize=False):
         calculation might be wrong. In this case, a UserWarning will
         be issued.
 
+    .. note::
+
+        This function does not maintain laziness when called; it realises data.
+        See more at :doc:`/userguide/real_and_lazy_data`.
+
     Args:
 
     * cube (:class:`iris.cube.Cube`):
diff --git a/lib/iris/analysis/maths.py b/lib/iris/analysis/maths.py
index 468847bca2..09a02ad51c 100644
--- a/lib/iris/analysis/maths.py
+++ b/lib/iris/analysis/maths.py
@@ -115,6 +115,11 @@ def abs(cube, in_place=False):
     Returns:
         An instance of :class:`iris.cube.Cube`.
 
+    Notes
+    ------
+    This function maintains laziness when called; it does not realise data.
+    See more at :doc:`/userguide/real_and_lazy_data`.
+
     """
     _assert_is_cube(cube)
     new_dtype = _output_dtype(np.abs, cube.dtype, in_place=in_place)
@@ -160,6 +165,11 @@ def intersection_of_cubes(cube, other_cube):
           intersections = cubes.extract_overlapping(coords)
           cube1, cube2 = (intersections[0], intersections[1])
 
+    Notes
+    ------
+    This function maintains laziness when called; it does not realise data.
+    See more at :doc:`/userguide/real_and_lazy_data`.
+
     """
     wmsg = (
         "iris.analysis.maths.intersection_of_cubes has been deprecated and will "
@@ -243,6 +253,11 @@ def add(cube, other, dim=None, in_place=False):
     Returns:
         An instance of :class:`iris.cube.Cube`.
 
+    Notes
+    ------
+    This function maintains laziness when called; it does not realise data.
+    See more at :doc:`/userguide/real_and_lazy_data`.
+
     """
     _assert_is_cube(cube)
     new_dtype = _output_dtype(
@@ -292,6 +307,11 @@ def subtract(cube, other, dim=None, in_place=False):
     Returns:
         An instance of :class:`iris.cube.Cube`.
 
+    Notes
+    ------
+    This function maintains laziness when called; it does not realise data.
+    See more at :doc:`/userguide/real_and_lazy_data`.
+
     """
     _assert_is_cube(cube)
     new_dtype = _output_dtype(
@@ -383,6 +403,10 @@ def multiply(cube, other, dim=None, in_place=False):
     Returns:
         An instance of :class:`iris.cube.Cube`.
 
+    Notes
+    ------
+    This function maintains laziness when called; it does not realise data.
+    See more at :doc:`/userguide/real_and_lazy_data`.
     """
     _assert_is_cube(cube)
 
@@ -456,6 +480,10 @@ def divide(cube, other, dim=None, in_place=False):
     Returns:
         An instance of :class:`iris.cube.Cube`.
 
+    Notes
+    ------
+    This function maintains laziness when called; it does not realise data.
+    See more at :doc:`/userguide/real_and_lazy_data`.
     """
     _assert_is_cube(cube)
 
@@ -519,6 +547,10 @@ def exponentiate(cube, exponent, in_place=False):
     Returns:
         An instance of :class:`iris.cube.Cube`.
 
+    Notes
+    ------
+    This function maintains laziness when called; it does not realise data.
+    See more at :doc:`/userguide/real_and_lazy_data`.
     """
     _assert_is_cube(cube)
     new_dtype = _output_dtype(
@@ -567,6 +599,11 @@ def exp(cube, in_place=False):
     Returns:
         An instance of :class:`iris.cube.Cube`.
 
+    Notes
+    ------
+    This function maintains laziness when called; it does not realise data.
+    See more at :doc:`/userguide/real_and_lazy_data`.
+
     """
     _assert_is_cube(cube)
     new_dtype = _output_dtype(np.exp, cube.dtype, in_place=in_place)
@@ -593,6 +630,11 @@ def log(cube, in_place=False):
     Returns:
         An instance of :class:`iris.cube.Cube`.
 
+    Notes
+    ------
+    This function maintains laziness when called; it does not realise data.
+    See more at :doc:`/userguide/real_and_lazy_data`.
+
     """
     _assert_is_cube(cube)
     new_dtype = _output_dtype(np.log, cube.dtype, in_place=in_place)
@@ -623,6 +665,11 @@ def log2(cube, in_place=False):
     Returns:
         An instance of :class:`iris.cube.Cube`.
 
+    Notes
+    ------
+    This function maintains laziness when called; it does not realise data.
+    See more at :doc:`/userguide/real_and_lazy_data`.
+
     """
     _assert_is_cube(cube)
     new_dtype = _output_dtype(np.log2, cube.dtype, in_place=in_place)
@@ -649,6 +696,11 @@ def log10(cube, in_place=False):
     Returns:
         An instance of :class:`iris.cube.Cube`.
 
+    Notes
+    ------
+    This function maintains laziness when called; it does not realise data.
+    See more at :doc:`/userguide/real_and_lazy_data`.
+
     """
     _assert_is_cube(cube)
     new_dtype = _output_dtype(np.log10, cube.dtype, in_place=in_place)
@@ -703,6 +755,12 @@ def apply_ufunc(
 
         cube = apply_ufunc(numpy.sin, cube, in_place=True)
 
+    .. note::
+
+        This function maintains laziness when called; it does not realise data. This is dependent on `ufunc` argument
+        being a numpy operation that is compatible with lazy operation.
+        See more at :doc:`/userguide/real_and_lazy_data`.
+
     """
 
     if not isinstance(ufunc, np.ufunc):
diff --git a/lib/iris/analysis/trajectory.py b/lib/iris/analysis/trajectory.py
index 946ae1cb2c..c21d71d48c 100644
--- a/lib/iris/analysis/trajectory.py
+++ b/lib/iris/analysis/trajectory.py
@@ -216,6 +216,10 @@ def interpolate(cube, sample_points, method=None):
         ('longitude', [-60, -50, -40])]
         interpolated_cube = interpolate(cube, sample_points)
 
+    Notes
+    ------
+    This function does not maintain laziness when called; it realises data.
+    See more at :doc:`/userguide/real_and_lazy_data`.
     """
     from iris.analysis import Linear
 
@@ -443,21 +447,7 @@ def interpolate(cube, sample_points, method=None):
         ]
 
         # Apply the fancy indexing to get all the result data points.
-        source_data = source_data[tuple(fancy_source_indices)]
-
-        # "Fix" problems with missing datapoints producing odd values
-        # when copied from a masked into an unmasked array.
-        # TODO: proper masked data handling.
-        if np.ma.isMaskedArray(source_data):
-            # This is **not** proper mask handling, because we cannot produce a
-            # masked result, but it ensures we use a "filled" version of the
-            # input in this case.
-            source_data = source_data.filled()
-        new_cube.data[:] = source_data
-        # NOTE: we assign to "new_cube.data[:]" and *not* just "new_cube.data",
-        # because the existing code produces a default dtype from 'np.empty'
-        # instead of preserving the input dtype.
-        # TODO: maybe this should be fixed -- i.e. to preserve input dtype ??
+        new_cube.data = source_data[tuple(fancy_source_indices)]
 
         # Fill in the empty squashed (non derived) coords.
         column_coords = [
diff --git a/lib/iris/coords.py b/lib/iris/coords.py
index d0d471a634..cfcdcd92a0 100644
--- a/lib/iris/coords.py
+++ b/lib/iris/coords.py
@@ -18,7 +18,6 @@
 import warnings
 import zlib
 
-import cftime
 import dask.array as da
 import numpy as np
 import numpy.ma as ma
@@ -1345,7 +1344,14 @@ def __add__(self, mod):
         return Cell(point, bound)
 
     def __hash__(self):
-        return super().__hash__()
+        # See __eq__ for the definition of when two cells are equal.
+        if self.bound is None:
+            return hash(self.point)
+        bound = self.bound
+        rbound = bound[::-1]
+        if rbound < bound:
+            bound = rbound
+        return hash((self.point, bound))
 
     def __eq__(self, other):
         """
@@ -1411,16 +1417,6 @@ def __common_cmp__(self, other, operator_method):
         ):
             raise ValueError("Unexpected operator_method")
 
-        # Prevent silent errors resulting from missing cftime
-        # behaviour.
-        if isinstance(other, cftime.datetime) or (
-            isinstance(self.point, cftime.datetime)
-            and not isinstance(other, iris.time.PartialDateTime)
-        ):
-            raise TypeError(
-                "Cannot determine the order of " "cftime.datetime objects"
-            )
-
         if isinstance(other, Cell):
             # Cell vs Cell comparison for providing a strict sort order
             if self.bound is None:
@@ -1485,19 +1481,7 @@ def __common_cmp__(self, other, operator_method):
                 else:
                     me = max(self.bound)
 
-            # Work around to handle cftime.datetime comparison, which
-            # doesn't return NotImplemented on failure in some versions of the
-            # library
-            try:
-                result = operator_method(me, other)
-            except TypeError:
-                rop = {
-                    operator.lt: operator.gt,
-                    operator.gt: operator.lt,
-                    operator.le: operator.ge,
-                    operator.ge: operator.le,
-                }[operator_method]
-                result = rop(other, me)
+            result = operator_method(me, other)
 
         return result
 
@@ -1895,7 +1879,22 @@ def cells(self):
               ...
 
         """
-        return _CellIterator(self)
+        if self.ndim != 1:
+            raise iris.exceptions.CoordinateMultiDimError(self)
+
+        points = self.points
+        bounds = self.bounds
+        if self.units.is_time_reference():
+            points = self.units.num2date(points)
+            if self.has_bounds():
+                bounds = self.units.num2date(bounds)
+
+        if self.has_bounds():
+            for point, bound in zip(points, bounds):
+                yield Cell(point, bound)
+        else:
+            for point in points:
+                yield Cell(point)
 
     def _sanity_check_bounds(self):
         if self.ndim == 1:
@@ -2382,18 +2381,16 @@ def intersect(self, other, return_indices=False):
             )
             raise ValueError(msg)
 
-        # Cache self.cells for speed. We can also use the index operation on a
-        # list conveniently.
-        self_cells = [cell for cell in self.cells()]
+        # Cache self.cells for speed. We can also use the dict for fast index
+        # lookup.
+        self_cells = {cell: idx for idx, cell in enumerate(self.cells())}
 
         # Maintain a list of indices on self for which cells exist in both self
         # and other.
         self_intersect_indices = []
         for cell in other.cells():
-            try:
-                self_intersect_indices.append(self_cells.index(cell))
-            except ValueError:
-                pass
+            if cell in self_cells:
+                self_intersect_indices.append(self_cells[cell])
 
         if return_indices is False and self_intersect_indices == []:
             raise ValueError(
@@ -3137,22 +3134,6 @@ def xml_element(self, doc):
         return cellMethod_xml_element
 
 
-# See Coord.cells() for the description/context.
-class _CellIterator(Iterator):
-    def __init__(self, coord):
-        self._coord = coord
-        if coord.ndim != 1:
-            raise iris.exceptions.CoordinateMultiDimError(coord)
-        self._indices = iter(range(coord.shape[0]))
-
-    def __next__(self):
-        # NB. When self._indices runs out it will raise StopIteration for us.
-        i = next(self._indices)
-        return self._coord.cell(i)
-
-    next = __next__
-
-
 # See ExplicitCoord._group() for the description/context.
 class _GroupIterator(Iterator):
     def __init__(self, points):
diff --git a/lib/iris/cube.py b/lib/iris/cube.py
index 8879ade621..be01bc9e5d 100644
--- a/lib/iris/cube.py
+++ b/lib/iris/cube.py
@@ -185,6 +185,12 @@ def _assert_is_cube(obj):
             )
             raise ValueError(msg)
 
+    def _repr_html_(self):
+        from iris.experimental.representation import CubeListRepresentation
+
+        representer = CubeListRepresentation(self)
+        return representer.repr_html()
+
     # TODO #370 Which operators need overloads?
 
     def __add__(self, other):
@@ -1152,7 +1158,9 @@ def add_aux_coord(self, coord, data_dims=None):
 
         """
         if self.coords(coord):  # TODO: just fail on duplicate object
-            raise ValueError("Duplicate coordinates are not permitted.")
+            raise iris.exceptions.CannotAddError(
+                "Duplicate coordinates are not permitted."
+            )
         self._add_unique_aux_coord(coord, data_dims)
 
     def _check_multi_dim_metadata(self, metadata, data_dims):
@@ -1172,7 +1180,7 @@ def _check_multi_dim_metadata(self, metadata, data_dims):
                         len(data_dims), metadata.ndim, metadata.name()
                     )
                 )
-                raise ValueError(msg)
+                raise iris.exceptions.CannotAddError(msg)
             # Check compatibility with the shape of the data
             for i, dim in enumerate(data_dims):
                 if metadata.shape[i] != self.shape[dim]:
@@ -1180,7 +1188,7 @@ def _check_multi_dim_metadata(self, metadata, data_dims):
                         "Unequal lengths. Cube dimension {} => {};"
                         " metadata {!r} dimension {} => {}."
                     )
-                    raise ValueError(
+                    raise iris.exceptions.CannotAddError(
                         msg.format(
                             dim,
                             self.shape[dim],
@@ -1192,7 +1200,7 @@ def _check_multi_dim_metadata(self, metadata, data_dims):
         elif metadata.shape != (1,):
             msg = "Missing data dimensions for multi-valued {} {!r}"
             msg = msg.format(metadata.__class__.__name__, metadata.name())
-            raise ValueError(msg)
+            raise iris.exceptions.CannotAddError(msg)
         return data_dims
 
     def _add_unique_aux_coord(self, coord, data_dims):
@@ -1206,7 +1214,7 @@ def _add_unique_aux_coord(self, coord, data_dims):
                     "cube {item} of {ownval!r}."
                 )
                 if coord.mesh != mesh:
-                    raise ValueError(
+                    raise iris.exceptions.CannotAddError(
                         msg.format(
                             item="mesh",
                             coord=coord,
@@ -1216,7 +1224,7 @@ def _add_unique_aux_coord(self, coord, data_dims):
                     )
                 location = self.location
                 if coord.location != location:
-                    raise ValueError(
+                    raise iris.exceptions.CannotAddError(
                         msg.format(
                             item="location",
                             coord=coord,
@@ -1226,7 +1234,7 @@ def _add_unique_aux_coord(self, coord, data_dims):
                     )
                 mesh_dims = (self.mesh_dim(),)
                 if data_dims != mesh_dims:
-                    raise ValueError(
+                    raise iris.exceptions.CannotAddError(
                         msg.format(
                             item="mesh dimension",
                             coord=coord,
@@ -1266,7 +1274,9 @@ def coordsonly(coords_and_dims):
             ref_coord = aux_factory.dependencies[dependency]
             if ref_coord is not None and ref_coord not in cube_coords:
                 msg = "{} coordinate for factory is not present on cube {}"
-                raise ValueError(msg.format(ref_coord.name(), self.name()))
+                raise iris.exceptions.CannotAddError(
+                    msg.format(ref_coord.name(), self.name())
+                )
         self._aux_factories.append(aux_factory)
 
     def add_cell_measure(self, cell_measure, data_dims=None):
@@ -1293,7 +1303,9 @@ def add_cell_measure(self, cell_measure, data_dims=None):
 
         """
         if self.cell_measures(cell_measure):
-            raise ValueError("Duplicate cell_measures are not permitted.")
+            raise iris.exceptions.CannotAddError(
+                "Duplicate cell_measures are not permitted."
+            )
         data_dims = self._check_multi_dim_metadata(cell_measure, data_dims)
         self._cell_measures_and_dims.append((cell_measure, data_dims))
         self._cell_measures_and_dims.sort(
@@ -1321,7 +1333,9 @@ def add_ancillary_variable(self, ancillary_variable, data_dims=None):
         """
 
         if self.ancillary_variables(ancillary_variable):
-            raise ValueError("Duplicate ancillary variables not permitted")
+            raise iris.exceptions.CannotAddError(
+                "Duplicate ancillary variables not permitted"
+            )
 
         data_dims = self._check_multi_dim_metadata(
             ancillary_variable, data_dims
@@ -1352,13 +1366,13 @@ def add_dim_coord(self, dim_coord, data_dim):
 
         """
         if self.coords(dim_coord):
-            raise ValueError(
+            raise iris.exceptions.CannotAddError(
                 "The coordinate already exists on the cube. "
                 "Duplicate coordinates are not permitted."
             )
         # Check dimension is available
         if self.coords(dimensions=data_dim, dim_coords=True):
-            raise ValueError(
+            raise iris.exceptions.CannotAddError(
                 "A dim_coord is already associated with "
                 "dimension %d." % data_dim
             )
@@ -1366,12 +1380,14 @@ def add_dim_coord(self, dim_coord, data_dim):
 
     def _add_unique_dim_coord(self, dim_coord, data_dim):
         if isinstance(dim_coord, iris.coords.AuxCoord):
-            raise ValueError("The dim_coord may not be an AuxCoord instance.")
+            raise iris.exceptions.CannotAddError(
+                "The dim_coord may not be an AuxCoord instance."
+            )
 
         # Convert data_dim to a single integer
         if isinstance(data_dim, Container):
             if len(data_dim) != 1:
-                raise ValueError(
+                raise iris.exceptions.CannotAddError(
                     "The supplied data dimension must be a" " single number."
                 )
             data_dim = int(list(data_dim)[0])
@@ -1380,7 +1396,7 @@ def _add_unique_dim_coord(self, dim_coord, data_dim):
 
         # Check data_dim value is valid
         if data_dim < 0 or data_dim >= self.ndim:
-            raise ValueError(
+            raise iris.exceptions.CannotAddError(
                 "The cube does not have the specified dimension "
                 "(%d)" % data_dim
             )
@@ -1388,7 +1404,7 @@ def _add_unique_dim_coord(self, dim_coord, data_dim):
         # Check compatibility with the shape of the data
         if dim_coord.shape[0] != self.shape[data_dim]:
             msg = "Unequal lengths. Cube dimension {} => {}; coord {!r} => {}."
-            raise ValueError(
+            raise iris.exceptions.CannotAddError(
                 msg.format(
                     data_dim,
                     self.shape[data_dim],
@@ -1984,6 +2000,12 @@ def coord(
             if name_or_coord is not None:
                 if not isinstance(name_or_coord, str):
                     _name = name_or_coord.name()
+                    emsg = (
+                        "Expected to find exactly 1 coordinate matching the given "
+                        f"{_name!r} coordinate's metadata, but found none."
+                    )
+                    raise iris.exceptions.CoordinateNotFoundError(emsg)
+
             bad_name = _name or standard_name or long_name or ""
             emsg = (
                 f"Expected to find exactly 1 {bad_name!r} coordinate, "
@@ -2188,9 +2210,15 @@ def cell_measure(self, name_or_cell_measure=None):
                 bad_name = (
                     name_or_cell_measure and name_or_cell_measure.name()
                 ) or ""
+                if name_or_cell_measure is not None:
+                    emsg = (
+                        "Expected to find exactly 1 cell measure matching the given "
+                        f"{bad_name!r} cell measure's metadata, but found none."
+                    )
+                    raise iris.exceptions.CellMeasureNotFoundError(emsg)
             msg = (
-                "Expected to find exactly 1 %s cell_measure, but found "
-                "none." % bad_name
+                f"Expected to find exactly 1 {bad_name!r} cell measure, "
+                "but found none."
             )
             raise iris.exceptions.CellMeasureNotFoundError(msg)
 
@@ -2275,9 +2303,16 @@ def ancillary_variable(self, name_or_ancillary_variable=None):
                     name_or_ancillary_variable
                     and name_or_ancillary_variable.name()
                 ) or ""
+                if name_or_ancillary_variable is not None:
+                    emsg = (
+                        "Expected to find exactly 1 ancillary_variable matching the "
+                        f"given {bad_name!r} ancillary_variable's metadata, but found "
+                        "none."
+                    )
+                    raise iris.exceptions.AncillaryVariableNotFoundError(emsg)
             msg = (
-                "Expected to find exactly 1 {!s} ancillary_variable, but "
-                "found none.".format(bad_name)
+                f"Expected to find exactly 1 {bad_name!r} ancillary_variable, "
+                "but found none."
             )
             raise iris.exceptions.AncillaryVariableNotFoundError(msg)
 
@@ -2293,10 +2328,23 @@ def cell_methods(self):
         return self._metadata_manager.cell_methods
 
     @cell_methods.setter
-    def cell_methods(self, cell_methods):
-        self._metadata_manager.cell_methods = (
-            tuple(cell_methods) if cell_methods else tuple()
-        )
+    def cell_methods(self, cell_methods: Iterable):
+        if not cell_methods:
+            # For backwards compatibility: Empty or null value is equivalent to ().
+            cell_methods = ()
+        else:
+            # Can supply any iterable, which is converted (copied) to a tuple.
+            cell_methods = tuple(cell_methods)
+            for cell_method in cell_methods:
+                # All contents should be CellMethods.  Requiring class membership is
+                # somewhat non-Pythonic, but simple, and not a problem for now.
+                if not isinstance(cell_method, iris.coords.CellMethod):
+                    msg = (
+                        f"Cube.cell_methods assigned value includes {cell_method}, "
+                        "which is not an iris.coords.CellMethod."
+                    )
+                    raise ValueError(msg)
+        self._metadata_manager.cell_methods = cell_methods
 
     def core_data(self):
         """
@@ -4038,8 +4086,9 @@ def aggregated_by(
         # coordinate dimension.
         shared_coords = list(
             filter(
-                lambda coord_: coord_ not in groupby_coords,
-                self.coords(contains_dimension=dimension_to_groupby),
+                lambda coord_: coord_ not in groupby_coords
+                and dimension_to_groupby in self.coord_dims(coord_),
+                self.dim_coords + self.aux_coords,
             )
         )
 
@@ -4071,6 +4120,11 @@ def aggregated_by(
         for coord in groupby_coords + shared_coords:
             aggregateby_cube.remove_coord(coord)
 
+        coord_mapping = {}
+        for coord in aggregateby_cube.coords():
+            orig_id = id(self.coord(coord))
+            coord_mapping[orig_id] = coord
+
         # Determine the group-by cube data shape.
         data_shape = list(self.shape + aggregator.aggregate_shape(**kwargs))
         data_shape[dimension_to_groupby] = len(groupby)
@@ -4199,6 +4253,11 @@ def aggregated_by(
                 aggregateby_cube.add_aux_coord(
                     new_coord, self.coord_dims(lookup_coord)
                 )
+            coord_mapping[id(self.coord(lookup_coord))] = new_coord
+
+        aggregateby_cube._aux_factories = []
+        for factory in self.aux_factories:
+            aggregateby_cube.add_aux_factory(factory.updated(coord_mapping))
 
         # Attach the aggregate-by data into the aggregate-by cube.
         if aggregateby_weights is None:
diff --git a/lib/iris/exceptions.py b/lib/iris/exceptions.py
index 12d24ef70f..5d3da3349e 100644
--- a/lib/iris/exceptions.py
+++ b/lib/iris/exceptions.py
@@ -174,3 +174,9 @@ class UnitConversionError(IrisError):
     """Raised when Iris is unable to convert a unit."""
 
     pass
+
+
+class CannotAddError(ValueError):
+    """Raised when an object (e.g. coord) cannot be added to a :class:`~iris.cube.Cube`."""
+
+    pass
diff --git a/lib/iris/experimental/animate.py b/lib/iris/experimental/animate.py
index fb2e2af590..1b6c2d46be 100644
--- a/lib/iris/experimental/animate.py
+++ b/lib/iris/experimental/animate.py
@@ -6,118 +6,32 @@
 """
 Wrapper for animating iris cubes using iris or matplotlib plotting functions
 
-"""
-
-import warnings
+Notes
+-----
+.. deprecated:: 3.4.0
 
-import matplotlib.animation as animation
-import matplotlib.pyplot as plt
+``iris.experimental.animate.animate()`` has been moved to
+:func:`iris.plot.animate`. This module will therefore be removed in a future
+release.
 
-import iris
+"""
 
 
 def animate(cube_iterator, plot_func, fig=None, **kwargs):
     """
     Animates the given cube iterator.
 
-    Args:
-
-    * cube_iterator (iterable of :class:`iris.cube.Cube` objects):
-        Each animation frame corresponds to each :class:`iris.cube.Cube`
-        object. See :meth:`iris.cube.Cube.slices`.
-
-    * plot_func (:mod:`iris.plot` or :mod:`iris.quickplot` plotting function):
-        Plotting function used to animate. Must accept the signature
-        ``plot_func(cube, vmin=vmin, vmax=vmax, coords=coords)``.
-        :func:`~iris.plot.contourf`, :func:`~iris.plot.contour`,
-        :func:`~iris.plot.pcolor` and :func:`~iris.plot.pcolormesh`
-        all conform to this signature.
-
-    Kwargs:
-
-    * fig (:class:`matplotlib.figure.Figure` instance):
-        By default, the current figure will be used or a new figure instance
-        created if no figure is available. See :func:`matplotlib.pyplot.gcf`.
-
-    * coords (list of :class:`~iris.coords.Coord` objects or coordinate names):
-        Use the given coordinates as the axes for the plot. The order of the
-        given coordinates indicates which axis to use for each, where the first
-        element is the horizontal axis of the plot and the second element is
-        the vertical axis of the plot.
-
-    * interval (int, float or long):
-        Defines the time interval in milliseconds between successive frames.
-        A default interval of 100ms is set.
-
-    * vmin, vmax (int, float or long):
-        Color scaling values, see :class:`matplotlib.colors.Normalize` for
-        further details. Default values are determined by the min-max across
-        the data set over the entire sequence.
-
-    See :class:`matplotlib.animation.FuncAnimation` for details of other valid
-    keyword arguments.
+    Warnings
+    --------
+    This function is now **disabled**.
 
-    Returns:
-        :class:`~matplotlib.animation.FuncAnimation` object suitable for
-        saving and or plotting.
-
-    For example, to animate along a set of cube slices::
-
-        cube_iter = cubes.slices(('grid_longitude', 'grid_latitude'))
-        ani = animate(cube_iter, qplt.contourf)
-        plt.show()
+    The functionality has been moved to :func:`iris.plot.animate`.
 
     """
-    kwargs.setdefault("interval", 100)
-    coords = kwargs.pop("coords", None)
-
-    if fig is None:
-        fig = plt.gcf()
-
-    def update_animation_iris(i, cubes, vmin, vmax, coords):
-        # Clearing the figure is currently necessary for compatibility with
-        # the iris quickploting module - due to the colorbar.
-        plt.gcf().clf()
-        plot_func(cubes[i], vmin=vmin, vmax=vmax, coords=coords)
-
-    # Turn cube iterator into a list to determine plot ranges.
-    # NOTE: we check that we are not providing a cube as this has a deprecated
-    # iter special method.
-    if hasattr(cube_iterator, "__iter__") and not isinstance(
-        cube_iterator, iris.cube.Cube
-    ):
-        cubes = iris.cube.CubeList(cube_iterator)
-    else:
-        msg = "iterable type object required for animation, {} given".format(
-            type(cube_iterator)
-        )
-        raise TypeError(msg)
-
-    supported = ["iris.plot", "iris.quickplot"]
-    if plot_func.__module__ not in supported:
-        msg = (
-            'Given plotting module "{}" may not be supported, intended '
-            "use: {}."
-        )
-        msg = msg.format(plot_func.__module__, supported)
-        warnings.warn(msg, UserWarning)
-
-    supported = ["contour", "contourf", "pcolor", "pcolormesh"]
-    if plot_func.__name__ not in supported:
-        msg = (
-            'Given plotting function "{}" may not be supported, intended '
-            "use: {}."
-        )
-        msg = msg.format(plot_func.__name__, supported)
-        warnings.warn(msg, UserWarning)
-
-    # Determine plot range.
-    vmin = kwargs.pop("vmin", min([cc.data.min() for cc in cubes]))
-    vmax = kwargs.pop("vmax", max([cc.data.max() for cc in cubes]))
-
-    update = update_animation_iris
-    frames = range(len(cubes))
-
-    return animation.FuncAnimation(
-        fig, update, frames=frames, fargs=(cubes, vmin, vmax, coords), **kwargs
+    msg = (
+        "The function 'iris.experimental.animate.animate()' has been moved, "
+        "and is now at 'iris.plot.animate()'.\n"
+        "Please replace 'iris.experimental.animate.animate' with "
+        "'iris.plot.animate'."
     )
+    raise Exception(msg)
diff --git a/lib/iris/experimental/regrid_conservative.py b/lib/iris/experimental/regrid_conservative.py
index bfa048ddf0..fdc23c7bc4 100644
--- a/lib/iris/experimental/regrid_conservative.py
+++ b/lib/iris/experimental/regrid_conservative.py
@@ -17,13 +17,15 @@
 
 """
 
+import functools
+
 import cartopy.crs as ccrs
 import numpy as np
 
 import iris
 from iris._deprecation import warn_deprecated
 from iris.analysis._interpolation import get_xy_dim_coords
-from iris.analysis._regrid import RectilinearRegridder
+from iris.analysis._regrid import RectilinearRegridder, _create_cube
 from iris.util import _meshgrid
 
 wmsg = (
@@ -329,16 +331,23 @@ def _valid_units(coord):
 
     # Return result as a new cube based on the source.
     # TODO: please tidy this interface !!!
-    return RectilinearRegridder._create_cube(
-        fullcube_data,
-        src=source_cube,
-        x_dim=src_dims_xy[0],
-        y_dim=src_dims_xy[1],
+    _regrid_callback = functools.partial(
+        RectilinearRegridder._regrid,
         src_x_coord=src_coords[0],
         src_y_coord=src_coords[1],
-        grid_x_coord=dst_coords[0],
-        grid_y_coord=dst_coords[1],
         sample_grid_x=sample_grid_x,
         sample_grid_y=sample_grid_y,
-        regrid_callback=RectilinearRegridder._regrid,
+    )
+
+    def regrid_callback(*args, **kwargs):
+        _data, dims = args
+        return _regrid_callback(_data, *dims, **kwargs)
+
+    return _create_cube(
+        fullcube_data,
+        source_cube,
+        [src_dims_xy[0], src_dims_xy[1]],
+        [dst_coords[0], dst_coords[1]],
+        2,
+        regrid_callback,
     )
diff --git a/lib/iris/experimental/ugrid/load.py b/lib/iris/experimental/ugrid/load.py
index 6c802e00d4..a522d91313 100644
--- a/lib/iris/experimental/ugrid/load.py
+++ b/lib/iris/experimental/ugrid/load.py
@@ -8,8 +8,7 @@
 Extensions to Iris' NetCDF loading to allow the construction of
 :class:`~iris.experimental.ugrid.mesh.Mesh`\\ es from UGRID data in the file.
 
-Eventual destination: :mod:`iris.fileformats.netcdf` (plan to split that module
-into ``load`` and ``save`` in future).
+Eventual destination: :mod:`iris.fileformats.netcdf`.
 
 """
 from contextlib import contextmanager
@@ -19,8 +18,8 @@
 
 from ...config import get_logger
 from ...coords import AuxCoord
-from ...fileformats import netcdf
 from ...fileformats._nc_load_rules.helpers import get_attr_units, get_names
+from ...fileformats.netcdf import loader as nc_loader
 from ...io import decode_uri, expand_filespecs
 from ...util import guess_coord_axis
 from .cf import (
@@ -202,7 +201,7 @@ def load_meshes(uris, var_name=None):
             else:
                 handling_format_spec = FORMAT_AGENT.get_spec(source, None)
 
-            if handling_format_spec.handler == netcdf.load_cubes:
+            if handling_format_spec.handler == nc_loader.load_cubes:
                 valid_sources.append(source)
             else:
                 message = f"Ignoring non-NetCDF file: {source}"
@@ -239,7 +238,7 @@ def _build_aux_coord(coord_var, file_path):
     assert isinstance(coord_var, CFUGridAuxiliaryCoordinateVariable)
     attributes = {}
     attr_units = get_attr_units(coord_var, attributes)
-    points_data = netcdf._get_cf_var_data(coord_var, file_path)
+    points_data = nc_loader._get_cf_var_data(coord_var, file_path)
 
     # Bounds will not be loaded:
     # Bounds may be present, but the UGRID conventions state this would
@@ -293,7 +292,7 @@ def _build_connectivity(connectivity_var, file_path, element_dims):
     assert isinstance(connectivity_var, CFUGridConnectivityVariable)
     attributes = {}
     attr_units = get_attr_units(connectivity_var, attributes)
-    indices_data = netcdf._get_cf_var_data(connectivity_var, file_path)
+    indices_data = nc_loader._get_cf_var_data(connectivity_var, file_path)
 
     cf_role = connectivity_var.cf_role
     start_index = connectivity_var.start_index
@@ -462,7 +461,7 @@ def _build_mesh(cf, mesh_var, file_path):
     )
     mesh_elements = filter(None, mesh_elements)
     for iris_object in mesh_elements:
-        netcdf._add_unused_attributes(
+        nc_loader._add_unused_attributes(
             iris_object, cf.cf_group[iris_object.var_name]
         )
 
diff --git a/lib/iris/experimental/ugrid/mesh.py b/lib/iris/experimental/ugrid/mesh.py
index 974a563046..4fd09175af 100644
--- a/lib/iris/experimental/ugrid/mesh.py
+++ b/lib/iris/experimental/ugrid/mesh.py
@@ -2841,16 +2841,60 @@ def __init__(
 
         # Get the 'coord identity' metadata from the relevant node-coordinate.
         node_coord = self.mesh.coord(include_nodes=True, axis=self.axis)
+        node_metadict = node_coord.metadata._asdict()
+        # Use node metadata, unless location is face/edge.
+        use_metadict = node_metadict.copy()
+        if location != "node":
+            # Location is either "edge" or "face" - get the relevant coord.
+            kwargs = {f"include_{location}s": True, "axis": axis}
+            location_coord = self.mesh.coord(**kwargs)
+
+            # Take the MeshCoord metadata from the 'location' coord.
+            use_metadict = location_coord.metadata._asdict()
+            unit_unknown = Unit(None)
+
+            # N.B. at present, coords in a Mesh are stored+accessed by 'axis', which
+            # means they must have a standard_name.  So ...
+            # (a) the 'location' (face/edge) coord *always* has a useable phenomenon
+            #     identity.
+            # (b) we still want to check that location+node coords have the same
+            #     phenomenon (i.e. physical meaning identity + units), **but** ...
+            # (c) we will accept/ignore some differences : not just "var_name", but
+            #     also "long_name" *and* "attributes".  So it is *only* "standard_name"
+            #     and "units" that cause an error if they differ.
+            for key in ("standard_name", "units"):
+                bounds_value = use_metadict[key]
+                nodes_value = node_metadict[key]
+                if key == "units" and (
+                    bounds_value == unit_unknown or nodes_value == unit_unknown
+                ):
+                    # Allow "any" unit to match no-units (for now)
+                    continue
+                if bounds_value != nodes_value:
+
+                    def fix_repr(val):
+                        # Tidy values appearance by converting Unit to string, and
+                        # wrapping strings in '', but leaving other types as a
+                        # plain str() representation.
+                        if isinstance(val, Unit):
+                            val = str(val)
+                        if isinstance(val, str):
+                            val = repr(val)
+                        return val
+
+                    nodes_value, bounds_value = [
+                        fix_repr(val) for val in (nodes_value, bounds_value)
+                    ]
+                    msg = (
+                        f"Node coordinate {node_coord!r} disagrees with the "
+                        f"{location} coordinate {location_coord!r}, "
+                        f'in having a "{key}" value of {nodes_value} '
+                        f"instead of {bounds_value}."
+                    )
+                    raise ValueError(msg)
+
         # Call parent constructor to handle the common constructor args.
-        super().__init__(
-            points,
-            bounds=bounds,
-            standard_name=node_coord.standard_name,
-            long_name=node_coord.long_name,
-            var_name=None,  # We *don't* "represent" the underlying node var
-            units=node_coord.units,
-            attributes=node_coord.attributes,
-        )
+        super().__init__(points, bounds=bounds, **use_metadict)
 
     # Define accessors for MeshCoord-specific properties mesh/location/axis.
     # These are all read-only.
diff --git a/lib/iris/experimental/ugrid/save.py b/lib/iris/experimental/ugrid/save.py
index 8a5934b939..3c42137905 100644
--- a/lib/iris/experimental/ugrid/save.py
+++ b/lib/iris/experimental/ugrid/save.py
@@ -8,8 +8,7 @@
 Extensions to Iris' NetCDF saving to allow
 :class:`~iris.experimental.ugrid.mesh.Mesh` saving in UGRID format.
 
-Eventual destination: :mod:`iris.fileformats.netcdf` (plan to split that module
-into ``load`` and ``save`` in future).
+Eventual destination: :mod:`iris.fileformats.netcdf`.
 
 """
 from collections.abc import Iterable
diff --git a/lib/iris/fileformats/_nc_load_rules/helpers.py b/lib/iris/fileformats/_nc_load_rules/helpers.py
index d50d3f324a..35163c47d5 100644
--- a/lib/iris/fileformats/_nc_load_rules/helpers.py
+++ b/lib/iris/fileformats/_nc_load_rules/helpers.py
@@ -13,7 +13,6 @@
 build routines, and which it does not use.
 
 """
-
 import warnings
 
 import cf_units
@@ -31,12 +30,14 @@
 import iris.fileformats.netcdf
 from iris.fileformats.netcdf import (
     UnknownCellMethodWarning,
-    _get_cf_var_data,
     parse_cell_methods,
 )
+from iris.fileformats.netcdf.loader import _get_cf_var_data
 import iris.std_names
 import iris.util
 
+# TODO: should un-addable coords / cell measures / etcetera be skipped? iris#5068.
+
 #
 # UD Units Constants (based on Unidata udunits.dat definition file)
 #
@@ -853,6 +854,12 @@ def build_dimension_coordinate(
         cf_coord_var, coord_name, attributes
     )
 
+    coord_skipped_msg = (
+        f"{cf_coord_var.cf_name} coordinate not added to Cube: "
+    )
+    coord_skipped_msg += "{error}"
+    coord_skipped = False
+
     # Create the coordinate.
     try:
         coord = iris.coords.DimCoord(
@@ -869,6 +876,11 @@ def build_dimension_coordinate(
         )
     except ValueError as e_msg:
         # Attempt graceful loading.
+        msg = (
+            "Failed to create {name!r} dimension coordinate: {error}\n"
+            "Gracefully creating {name!r} auxiliary coordinate instead."
+        )
+        warnings.warn(msg.format(name=str(cf_coord_var.cf_name), error=e_msg))
         coord = iris.coords.AuxCoord(
             points_data,
             standard_name=standard_name,
@@ -880,22 +892,26 @@ def build_dimension_coordinate(
             coord_system=coord_system,
             climatological=climatological,
         )
-        cube.add_aux_coord(coord, data_dims)
-        msg = (
-            "Failed to create {name!r} dimension coordinate: {error}\n"
-            "Gracefully creating {name!r} auxiliary coordinate instead."
-        )
-        warnings.warn(msg.format(name=str(cf_coord_var.cf_name), error=e_msg))
+        try:
+            cube.add_aux_coord(coord, data_dims)
+        except iris.exceptions.CannotAddError as e_msg:
+            warnings.warn(coord_skipped_msg.format(error=e_msg))
+            coord_skipped = True
     else:
         # Add the dimension coordinate to the cube.
-        if data_dims:
-            cube.add_dim_coord(coord, data_dims)
-        else:
-            # Scalar coords are placed in the aux_coords container.
-            cube.add_aux_coord(coord, data_dims)
+        try:
+            if data_dims:
+                cube.add_dim_coord(coord, data_dims)
+            else:
+                # Scalar coords are placed in the aux_coords container.
+                cube.add_aux_coord(coord, data_dims)
+        except iris.exceptions.CannotAddError as e_msg:
+            warnings.warn(coord_skipped_msg.format(error=e_msg))
+            coord_skipped = True
 
-    # Update the coordinate to CF-netCDF variable mapping.
-    engine.cube_parts["coordinates"].append((coord, cf_coord_var.cf_name))
+    if not coord_skipped:
+        # Update the coordinate to CF-netCDF variable mapping.
+        engine.cube_parts["coordinates"].append((coord, cf_coord_var.cf_name))
 
 
 ################################################################################
@@ -964,10 +980,14 @@ def build_auxiliary_coordinate(
     )
 
     # Add it to the cube
-    cube.add_aux_coord(coord, data_dims)
-
-    # Make a list with names, stored on the engine, so we can find them all later.
-    engine.cube_parts["coordinates"].append((coord, cf_coord_var.cf_name))
+    try:
+        cube.add_aux_coord(coord, data_dims)
+    except iris.exceptions.CannotAddError as e_msg:
+        msg = "{name!r} coordinate not added to Cube: {error}"
+        warnings.warn(msg.format(name=str(cf_coord_var.cf_name), error=e_msg))
+    else:
+        # Make a list with names, stored on the engine, so we can find them all later.
+        engine.cube_parts["coordinates"].append((coord, cf_coord_var.cf_name))
 
 
 ################################################################################
@@ -1011,12 +1031,16 @@ def build_cell_measures(engine, cf_cm_var):
     )
 
     # Add it to the cube
-    cube.add_cell_measure(cell_measure, data_dims)
-
-    # Make a list with names, stored on the engine, so we can find them all later.
-    engine.cube_parts["cell_measures"].append(
-        (cell_measure, cf_cm_var.cf_name)
-    )
+    try:
+        cube.add_cell_measure(cell_measure, data_dims)
+    except iris.exceptions.CannotAddError as e_msg:
+        msg = "{name!r} cell measure not added to Cube: {error}"
+        warnings.warn(msg.format(name=str(cf_cm_var.cf_name), error=e_msg))
+    else:
+        # Make a list with names, stored on the engine, so we can find them all later.
+        engine.cube_parts["cell_measures"].append(
+            (cell_measure, cf_cm_var.cf_name)
+        )
 
 
 ################################################################################
@@ -1056,10 +1080,16 @@ def build_ancil_var(engine, cf_av_var):
     )
 
     # Add it to the cube
-    cube.add_ancillary_variable(av, data_dims)
-
-    # Make a list with names, stored on the engine, so we can find them all later.
-    engine.cube_parts["ancillary_variables"].append((av, cf_av_var.cf_name))
+    try:
+        cube.add_ancillary_variable(av, data_dims)
+    except iris.exceptions.CannotAddError as e_msg:
+        msg = "{name!r} ancillary variable not added to Cube: {error}"
+        warnings.warn(msg.format(name=str(cf_av_var.cf_name), error=e_msg))
+    else:
+        # Make a list with names, stored on the engine, so we can find them all later.
+        engine.cube_parts["ancillary_variables"].append(
+            (av, cf_av_var.cf_name)
+        )
 
 
 ################################################################################
diff --git a/lib/iris/fileformats/name_loaders.py b/lib/iris/fileformats/name_loaders.py
index 3aaba3679e..d15a3717d0 100644
--- a/lib/iris/fileformats/name_loaders.py
+++ b/lib/iris/fileformats/name_loaders.py
@@ -571,7 +571,9 @@ def _generate_cubes(
                 cube.attributes[key] = value
 
         if cell_methods is not None:
-            cube.add_cell_method(cell_methods[i])
+            cell_method = cell_methods[i]
+            if cell_method is not None:
+                cube.add_cell_method(cell_method)
 
         yield cube
 
@@ -610,7 +612,7 @@ def _build_cell_methods(av_or_ints, coord):
             cell_method = None
             msg = "Unknown {} statistic: {!r}. Unable to create cell method."
             warnings.warn(msg.format(coord, av_or_int))
-        cell_methods.append(cell_method)
+        cell_methods.append(cell_method)  # NOTE: this can be a None
     return cell_methods
 
 
diff --git a/lib/iris/fileformats/netcdf/__init__.py b/lib/iris/fileformats/netcdf/__init__.py
new file mode 100644
index 0000000000..505e173b0b
--- /dev/null
+++ b/lib/iris/fileformats/netcdf/__init__.py
@@ -0,0 +1,49 @@
+# Copyright Iris contributors
+#
+# This file is part of Iris and is released under the LGPL license.
+# See COPYING and COPYING.LESSER in the root of the repository for full
+# licensing details.
+"""
+Module to support the loading and saving of NetCDF files, also using the CF conventions
+for metadata interpretation.
+
+See : `NetCDF User's Guide <https://docs.unidata.ucar.edu/nug/current/>`_
+and `netCDF4 python module <https://github.com/Unidata/netcdf4-python>`_.
+
+Also : `CF Conventions <https://cfconventions.org/>`_.
+
+"""
+import iris.config
+
+# Note: *must* be done before importing from submodules, as they also use this !
+logger = iris.config.get_logger(__name__)
+
+from .loader import DEBUG, NetCDFDataProxy, load_cubes
+from .saver import (
+    CF_CONVENTIONS_VERSION,
+    MESH_ELEMENTS,
+    SPATIO_TEMPORAL_AXES,
+    CFNameCoordMap,
+    Saver,
+    UnknownCellMethodWarning,
+    parse_cell_methods,
+    save,
+)
+
+# Export all public elements from the loader and saver submodules.
+# NOTE: the separation is purely for neatness and developer convenience; from
+# the user point of view, it is still all one module.
+__all__ = (
+    "CFNameCoordMap",
+    "CF_CONVENTIONS_VERSION",
+    "DEBUG",
+    "MESH_ELEMENTS",
+    "NetCDFDataProxy",
+    "SPATIO_TEMPORAL_AXES",
+    "Saver",
+    "UnknownCellMethodWarning",
+    "load_cubes",
+    "logger",
+    "parse_cell_methods",
+    "save",
+)
diff --git a/lib/iris/fileformats/netcdf/loader.py b/lib/iris/fileformats/netcdf/loader.py
new file mode 100644
index 0000000000..95f394c70d
--- /dev/null
+++ b/lib/iris/fileformats/netcdf/loader.py
@@ -0,0 +1,594 @@
+# Copyright Iris contributors
+#
+# This file is part of Iris and is released under the LGPL license.
+# See COPYING and COPYING.LESSER in the root of the repository for full
+# licensing details.
+"""
+Module to support the loading of Iris cubes from NetCDF files, also using the CF
+conventions for metadata interpretation.
+
+See : `NetCDF User's Guide <https://docs.unidata.ucar.edu/nug/current/>`_
+and `netCDF4 python module <https://github.com/Unidata/netcdf4-python>`_.
+
+Also : `CF Conventions <https://cfconventions.org/>`_.
+
+"""
+import warnings
+
+import netCDF4
+import numpy as np
+
+from iris._lazy_data import as_lazy_data
+from iris.aux_factory import (
+    AtmosphereSigmaFactory,
+    HybridHeightFactory,
+    HybridPressureFactory,
+    OceanSFactory,
+    OceanSg1Factory,
+    OceanSg2Factory,
+    OceanSigmaFactory,
+    OceanSigmaZFactory,
+)
+import iris.config
+import iris.coord_systems
+import iris.coords
+import iris.exceptions
+import iris.fileformats.cf
+from iris.fileformats.netcdf.saver import _CF_ATTRS
+import iris.io
+import iris.util
+
+# Show actions activation statistics.
+DEBUG = False
+
+# Get the logger : shared logger for all in 'iris.fileformats.netcdf'.
+from . import logger
+
+
+def _actions_engine():
+    # Return an 'actions engine', which provides a pyke-rules-like interface to
+    # the core cf translation code.
+    # Deferred import to avoid circularity.
+    import iris.fileformats._nc_load_rules.engine as nc_actions_engine
+
+    engine = nc_actions_engine.Engine()
+    return engine
+
+
+class NetCDFDataProxy:
+    """A reference to the data payload of a single NetCDF file variable."""
+
+    __slots__ = ("shape", "dtype", "path", "variable_name", "fill_value")
+
+    def __init__(self, shape, dtype, path, variable_name, fill_value):
+        self.shape = shape
+        self.dtype = dtype
+        self.path = path
+        self.variable_name = variable_name
+        self.fill_value = fill_value
+
+    @property
+    def ndim(self):
+        return len(self.shape)
+
+    def __getitem__(self, keys):
+        dataset = netCDF4.Dataset(self.path)
+        try:
+            variable = dataset.variables[self.variable_name]
+            # Get the NetCDF variable data and slice.
+            var = variable[keys]
+        finally:
+            dataset.close()
+        return np.asanyarray(var)
+
+    def __repr__(self):
+        fmt = (
+            "<{self.__class__.__name__} shape={self.shape}"
+            " dtype={self.dtype!r} path={self.path!r}"
+            " variable_name={self.variable_name!r}>"
+        )
+        return fmt.format(self=self)
+
+    def __getstate__(self):
+        return {attr: getattr(self, attr) for attr in self.__slots__}
+
+    def __setstate__(self, state):
+        for key, value in state.items():
+            setattr(self, key, value)
+
+
+def _assert_case_specific_facts(engine, cf, cf_group):
+    # Initialise a data store for built cube elements.
+    # This is used to patch element attributes *not* setup by the actions
+    # process, after the actions code has run.
+    engine.cube_parts["coordinates"] = []
+    engine.cube_parts["cell_measures"] = []
+    engine.cube_parts["ancillary_variables"] = []
+
+    # Assert facts for CF coordinates.
+    for cf_name in cf_group.coordinates.keys():
+        engine.add_case_specific_fact("coordinate", (cf_name,))
+
+    # Assert facts for CF auxiliary coordinates.
+    for cf_name in cf_group.auxiliary_coordinates.keys():
+        engine.add_case_specific_fact("auxiliary_coordinate", (cf_name,))
+
+    # Assert facts for CF cell measures.
+    for cf_name in cf_group.cell_measures.keys():
+        engine.add_case_specific_fact("cell_measure", (cf_name,))
+
+    # Assert facts for CF ancillary variables.
+    for cf_name in cf_group.ancillary_variables.keys():
+        engine.add_case_specific_fact("ancillary_variable", (cf_name,))
+
+    # Assert facts for CF grid_mappings.
+    for cf_name in cf_group.grid_mappings.keys():
+        engine.add_case_specific_fact("grid_mapping", (cf_name,))
+
+    # Assert facts for CF labels.
+    for cf_name in cf_group.labels.keys():
+        engine.add_case_specific_fact("label", (cf_name,))
+
+    # Assert facts for CF formula terms associated with the cf_group
+    # of the CF data variable.
+
+    # Collect varnames of formula-root variables as we go.
+    # NOTE: use dictionary keys as an 'OrderedSet'
+    #   - see: https://stackoverflow.com/a/53657523/2615050
+    # This is to ensure that we can handle the resulting facts in a definite
+    # order, as using a 'set' led to indeterminate results.
+    formula_root = {}
+    for cf_var in cf.cf_group.formula_terms.values():
+        for cf_root, cf_term in cf_var.cf_terms_by_root.items():
+            # Only assert this fact if the formula root variable is
+            # defined in the CF group of the CF data variable.
+            if cf_root in cf_group:
+                formula_root[cf_root] = True
+                engine.add_case_specific_fact(
+                    "formula_term",
+                    (cf_var.cf_name, cf_root, cf_term),
+                )
+
+    for cf_root in formula_root.keys():
+        engine.add_case_specific_fact("formula_root", (cf_root,))
+
+
+def _actions_activation_stats(engine, cf_name):
+    print("-" * 80)
+    print("CF Data Variable: %r" % cf_name)
+
+    engine.print_stats()
+
+    print("Rules Triggered:")
+
+    for rule in sorted(list(engine.rules_triggered)):
+        print("\t%s" % rule)
+
+    print("Case Specific Facts:")
+    kb_facts = engine.get_kb()
+
+    for key in kb_facts.entity_lists.keys():
+        for arg in kb_facts.entity_lists[key].case_specific_facts:
+            print("\t%s%s" % (key, arg))
+
+
+def _set_attributes(attributes, key, value):
+    """Set attributes dictionary, converting unicode strings appropriately."""
+
+    if isinstance(value, str):
+        try:
+            attributes[str(key)] = str(value)
+        except UnicodeEncodeError:
+            attributes[str(key)] = value
+    else:
+        attributes[str(key)] = value
+
+
+def _add_unused_attributes(iris_object, cf_var):
+    """
+    Populate the attributes of a cf element with the "unused" attributes
+    from the associated CF-netCDF variable. That is, all those that aren't CF
+    reserved terms.
+
+    """
+
+    def attribute_predicate(item):
+        return item[0] not in _CF_ATTRS
+
+    tmpvar = filter(attribute_predicate, cf_var.cf_attrs_unused())
+    for attr_name, attr_value in tmpvar:
+        _set_attributes(iris_object.attributes, attr_name, attr_value)
+
+
+def _get_actual_dtype(cf_var):
+    # Figure out what the eventual data type will be after any scale/offset
+    # transforms.
+    dummy_data = np.zeros(1, dtype=cf_var.dtype)
+    if hasattr(cf_var, "scale_factor"):
+        dummy_data = cf_var.scale_factor * dummy_data
+    if hasattr(cf_var, "add_offset"):
+        dummy_data = cf_var.add_offset + dummy_data
+    return dummy_data.dtype
+
+
+def _get_cf_var_data(cf_var, filename):
+    # Get lazy chunked data out of a cf variable.
+    dtype = _get_actual_dtype(cf_var)
+
+    # Create cube with deferred data, but no metadata
+    fill_value = getattr(
+        cf_var.cf_data,
+        "_FillValue",
+        netCDF4.default_fillvals[cf_var.dtype.str[1:]],
+    )
+    proxy = NetCDFDataProxy(
+        cf_var.shape, dtype, filename, cf_var.cf_name, fill_value
+    )
+    # Get the chunking specified for the variable : this is either a shape, or
+    # maybe the string "contiguous".
+    chunks = cf_var.cf_data.chunking()
+    # In the "contiguous" case, pass chunks=None to 'as_lazy_data'.
+    if chunks == "contiguous":
+        chunks = None
+    return as_lazy_data(proxy, chunks=chunks)
+
+
+class _OrderedAddableList(list):
+    """
+    A custom container object for actions recording.
+
+    Used purely in actions debugging, to accumulate a record of which actions
+    were activated.
+
+    It replaces a set, so as to preserve the ordering of operations, with
+    possible repeats, and it also numbers the entries.
+
+    The actions routines invoke an 'add' method, so this effectively replaces
+    a set.add with a list.append.
+
+    """
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self._n_add = 0
+
+    def add(self, msg):
+        self._n_add += 1
+        n_add = self._n_add
+        self.append(f"#{n_add:03d} : {msg}")
+
+
+def _load_cube(engine, cf, cf_var, filename):
+    from iris.cube import Cube
+
+    """Create the cube associated with the CF-netCDF data variable."""
+    data = _get_cf_var_data(cf_var, filename)
+    cube = Cube(data)
+
+    # Reset the actions engine.
+    engine.reset()
+
+    # Initialise engine rule processing hooks.
+    engine.cf_var = cf_var
+    engine.cube = cube
+    engine.cube_parts = {}
+    engine.requires = {}
+    engine.rules_triggered = _OrderedAddableList()
+    engine.filename = filename
+
+    # Assert all the case-specific facts.
+    # This extracts 'facts' specific to this data-variable (aka cube), from
+    # the info supplied in the CFGroup object.
+    _assert_case_specific_facts(engine, cf, cf_var.cf_group)
+
+    # Run the actions engine.
+    # This creates various cube elements and attaches them to the cube.
+    # It also records various other info on the engine, to be processed later.
+    engine.activate()
+
+    # Having run the rules, now add the "unused" attributes to each cf element.
+    def fix_attributes_all_elements(role_name):
+        elements_and_names = engine.cube_parts.get(role_name, [])
+
+        for iris_object, cf_var_name in elements_and_names:
+            _add_unused_attributes(iris_object, cf.cf_group[cf_var_name])
+
+    # Populate the attributes of all coordinates, cell-measures and ancillary-vars.
+    fix_attributes_all_elements("coordinates")
+    fix_attributes_all_elements("ancillary_variables")
+    fix_attributes_all_elements("cell_measures")
+
+    # Also populate attributes of the top-level cube itself.
+    _add_unused_attributes(cube, cf_var)
+
+    # Work out reference names for all the coords.
+    names = {
+        coord.var_name: coord.standard_name or coord.var_name or "unknown"
+        for coord in cube.coords()
+    }
+
+    # Add all the cube cell methods.
+    cube.cell_methods = [
+        iris.coords.CellMethod(
+            method=method.method,
+            intervals=method.intervals,
+            comments=method.comments,
+            coords=[
+                names[coord_name] if coord_name in names else coord_name
+                for coord_name in method.coord_names
+            ],
+        )
+        for method in cube.cell_methods
+    ]
+
+    if DEBUG:
+        # Show activation statistics for this data-var (i.e. cube).
+        _actions_activation_stats(engine, cf_var.cf_name)
+
+    return cube
+
+
+def _load_aux_factory(engine, cube):
+    """
+    Convert any CF-netCDF dimensionless coordinate to an AuxCoordFactory.
+
+    """
+    formula_type = engine.requires.get("formula_type")
+    if formula_type in [
+        "atmosphere_sigma_coordinate",
+        "atmosphere_hybrid_height_coordinate",
+        "atmosphere_hybrid_sigma_pressure_coordinate",
+        "ocean_sigma_z_coordinate",
+        "ocean_sigma_coordinate",
+        "ocean_s_coordinate",
+        "ocean_s_coordinate_g1",
+        "ocean_s_coordinate_g2",
+    ]:
+
+        def coord_from_term(term):
+            # Convert term names to coordinates (via netCDF variable names).
+            name = engine.requires["formula_terms"].get(term, None)
+            if name is not None:
+                for coord, cf_var_name in engine.cube_parts["coordinates"]:
+                    if cf_var_name == name:
+                        return coord
+                warnings.warn(
+                    "Unable to find coordinate for variable "
+                    "{!r}".format(name)
+                )
+
+        if formula_type == "atmosphere_sigma_coordinate":
+            pressure_at_top = coord_from_term("ptop")
+            sigma = coord_from_term("sigma")
+            surface_air_pressure = coord_from_term("ps")
+            factory = AtmosphereSigmaFactory(
+                pressure_at_top, sigma, surface_air_pressure
+            )
+        elif formula_type == "atmosphere_hybrid_height_coordinate":
+            delta = coord_from_term("a")
+            sigma = coord_from_term("b")
+            orography = coord_from_term("orog")
+            factory = HybridHeightFactory(delta, sigma, orography)
+        elif formula_type == "atmosphere_hybrid_sigma_pressure_coordinate":
+            # Hybrid pressure has two valid versions of its formula terms:
+            # "p0: var1 a: var2 b: var3 ps: var4" or
+            # "ap: var1 b: var2 ps: var3" where "ap = p0 * a"
+            # Attempt to get the "ap" term.
+            delta = coord_from_term("ap")
+            if delta is None:
+                # The "ap" term is unavailable, so try getting terms "p0"
+                # and "a" terms in order to derive an "ap" equivalent term.
+                coord_p0 = coord_from_term("p0")
+                if coord_p0 is not None:
+                    if coord_p0.shape != (1,):
+                        msg = (
+                            "Expecting {!r} to be a scalar reference "
+                            "pressure coordinate, got shape {!r}".format(
+                                coord_p0.var_name, coord_p0.shape
+                            )
+                        )
+                        raise ValueError(msg)
+                    if coord_p0.has_bounds():
+                        msg = (
+                            "Ignoring atmosphere hybrid sigma pressure "
+                            "scalar coordinate {!r} bounds.".format(
+                                coord_p0.name()
+                            )
+                        )
+                        warnings.warn(msg)
+                    coord_a = coord_from_term("a")
+                    if coord_a is not None:
+                        if coord_a.units.is_unknown():
+                            # Be graceful, and promote unknown to dimensionless units.
+                            coord_a.units = "1"
+                        delta = coord_a * coord_p0.points[0]
+                        delta.units = coord_a.units * coord_p0.units
+                        delta.rename("vertical pressure")
+                        delta.var_name = "ap"
+                        cube.add_aux_coord(delta, cube.coord_dims(coord_a))
+
+            sigma = coord_from_term("b")
+            surface_air_pressure = coord_from_term("ps")
+            factory = HybridPressureFactory(delta, sigma, surface_air_pressure)
+        elif formula_type == "ocean_sigma_z_coordinate":
+            sigma = coord_from_term("sigma")
+            eta = coord_from_term("eta")
+            depth = coord_from_term("depth")
+            depth_c = coord_from_term("depth_c")
+            nsigma = coord_from_term("nsigma")
+            zlev = coord_from_term("zlev")
+            factory = OceanSigmaZFactory(
+                sigma, eta, depth, depth_c, nsigma, zlev
+            )
+        elif formula_type == "ocean_sigma_coordinate":
+            sigma = coord_from_term("sigma")
+            eta = coord_from_term("eta")
+            depth = coord_from_term("depth")
+            factory = OceanSigmaFactory(sigma, eta, depth)
+        elif formula_type == "ocean_s_coordinate":
+            s = coord_from_term("s")
+            eta = coord_from_term("eta")
+            depth = coord_from_term("depth")
+            a = coord_from_term("a")
+            depth_c = coord_from_term("depth_c")
+            b = coord_from_term("b")
+            factory = OceanSFactory(s, eta, depth, a, b, depth_c)
+        elif formula_type == "ocean_s_coordinate_g1":
+            s = coord_from_term("s")
+            c = coord_from_term("c")
+            eta = coord_from_term("eta")
+            depth = coord_from_term("depth")
+            depth_c = coord_from_term("depth_c")
+            factory = OceanSg1Factory(s, c, eta, depth, depth_c)
+        elif formula_type == "ocean_s_coordinate_g2":
+            s = coord_from_term("s")
+            c = coord_from_term("c")
+            eta = coord_from_term("eta")
+            depth = coord_from_term("depth")
+            depth_c = coord_from_term("depth_c")
+            factory = OceanSg2Factory(s, c, eta, depth, depth_c)
+        cube.add_aux_factory(factory)
+
+
+def _translate_constraints_to_var_callback(constraints):
+    """
+    Translate load constraints into a simple data-var filter function, if possible.
+
+    Returns:
+         * function(cf_var:CFDataVariable): --> bool,
+            or None.
+
+    For now, ONLY handles a single NameConstraint with no 'STASH' component.
+
+    """
+    import iris._constraints
+
+    constraints = iris._constraints.list_of_constraints(constraints)
+    result = None
+    if len(constraints) == 1:
+        (constraint,) = constraints
+        if (
+            isinstance(constraint, iris._constraints.NameConstraint)
+            and constraint.STASH == "none"
+        ):
+            # As long as it doesn't use a STASH match, then we can treat it as
+            # a testing against name properties of cf_var.
+            # That's just like testing against name properties of a cube, except that they may not all exist.
+            def inner(cf_datavar):
+                match = True
+                for name in constraint._names:
+                    expected = getattr(constraint, name)
+                    if name != "STASH" and expected != "none":
+                        attr_name = "cf_name" if name == "var_name" else name
+                        # Fetch property : N.B. CFVariable caches the property values
+                        # The use of a default here is the only difference from the code in NameConstraint.
+                        if not hasattr(cf_datavar, attr_name):
+                            continue
+                        actual = getattr(cf_datavar, attr_name, "")
+                        if actual != expected:
+                            match = False
+                            break
+                return match
+
+            result = inner
+    return result
+
+
+def load_cubes(filenames, callback=None, constraints=None):
+    """
+    Loads cubes from a list of NetCDF filenames/OPeNDAP URLs.
+
+    Args:
+
+    * filenames (string/list):
+        One or more NetCDF filenames/OPeNDAP URLs to load from.
+
+    Kwargs:
+
+    * callback (callable function):
+        Function which can be passed on to :func:`iris.io.run_callback`.
+
+    Returns:
+        Generator of loaded NetCDF :class:`iris.cube.Cube`.
+
+    """
+    # TODO: rationalise UGRID/mesh handling once experimental.ugrid is folded
+    #  into standard behaviour.
+    # Deferred import to avoid circular imports.
+    from iris.experimental.ugrid.cf import CFUGridReader
+    from iris.experimental.ugrid.load import (
+        PARSE_UGRID_ON_LOAD,
+        _build_mesh_coords,
+        _meshes_from_cf,
+    )
+    from iris.io import run_callback
+
+    # Create a low-level data-var filter from the original load constraints, if they are suitable.
+    var_callback = _translate_constraints_to_var_callback(constraints)
+
+    # Create an actions engine.
+    engine = _actions_engine()
+
+    if isinstance(filenames, str):
+        filenames = [filenames]
+
+    for filename in filenames:
+        # Ingest the netCDF file.
+        meshes = {}
+        if PARSE_UGRID_ON_LOAD:
+            cf = CFUGridReader(filename)
+            meshes = _meshes_from_cf(cf)
+        else:
+            cf = iris.fileformats.cf.CFReader(filename)
+
+        # Process each CF data variable.
+        data_variables = list(cf.cf_group.data_variables.values()) + list(
+            cf.cf_group.promoted.values()
+        )
+        for cf_var in data_variables:
+            if var_callback and not var_callback(cf_var):
+                # Deliver only selected results.
+                continue
+
+            # cf_var-specific mesh handling, if a mesh is present.
+            # Build the mesh_coords *before* loading the cube - avoids
+            # mesh-related attributes being picked up by
+            # _add_unused_attributes().
+            mesh_name = None
+            mesh = None
+            mesh_coords, mesh_dim = [], None
+            if PARSE_UGRID_ON_LOAD:
+                mesh_name = getattr(cf_var, "mesh", None)
+            if mesh_name is not None:
+                try:
+                    mesh = meshes[mesh_name]
+                except KeyError:
+                    message = (
+                        f"File does not contain mesh: '{mesh_name}' - "
+                        f"referenced by variable: '{cf_var.cf_name}' ."
+                    )
+                    logger.debug(message)
+            if mesh is not None:
+                mesh_coords, mesh_dim = _build_mesh_coords(mesh, cf_var)
+
+            cube = _load_cube(engine, cf, cf_var, filename)
+
+            # Attach the mesh (if present) to the cube.
+            for mesh_coord in mesh_coords:
+                cube.add_aux_coord(mesh_coord, mesh_dim)
+
+            # Process any associated formula terms and attach
+            # the corresponding AuxCoordFactory.
+            try:
+                _load_aux_factory(engine, cube)
+            except ValueError as e:
+                warnings.warn("{}".format(e))
+
+            # Perform any user registered callback function.
+            cube = run_callback(callback, cube, cf_var, filename)
+
+            # Callback mechanism may return None, which must not be yielded
+            if cube is None:
+                continue
+
+            yield cube
diff --git a/lib/iris/fileformats/netcdf.py b/lib/iris/fileformats/netcdf/saver.py
similarity index 83%
rename from lib/iris/fileformats/netcdf.py
rename to lib/iris/fileformats/netcdf/saver.py
index 6a7b37a1cc..650c5e3338 100644
--- a/lib/iris/fileformats/netcdf.py
+++ b/lib/iris/fileformats/netcdf/saver.py
@@ -4,16 +4,16 @@
 # See COPYING and COPYING.LESSER in the root of the repository for full
 # licensing details.
 """
-Module to support the loading of a NetCDF file into an Iris cube.
+Module to support the saving of Iris cubes to a NetCDF file, also using the CF
+conventions for metadata interpretation.
 
-See also: `netCDF4 python <https://github.com/Unidata/netcdf4-python>`_
+See : `NetCDF User's Guide <https://docs.unidata.ucar.edu/nug/current/>`_
+and `netCDF4 python module <https://github.com/Unidata/netcdf4-python>`_.
 
-Also refer to document 'NetCDF Climate and Forecast (CF) Metadata Conventions'.
+Also : `CF Conventions <https://cfconventions.org/>`_.
 
 """
-
 import collections
-import collections.abc
 from itertools import repeat, zip_longest
 import os
 import os.path
@@ -28,7 +28,7 @@
 import numpy as np
 import numpy.ma as ma
 
-from iris._lazy_data import _co_realise_lazy_arrays, as_lazy_data, is_lazy_data
+from iris._lazy_data import _co_realise_lazy_arrays, is_lazy_data
 from iris.aux_factory import (
     AtmosphereSigmaFactory,
     HybridHeightFactory,
@@ -48,28 +48,17 @@
 import iris.io
 import iris.util
 
-# Show actions activation statistics.
-DEBUG = False
+# Get the logger : shared logger for all in 'iris.fileformats.netcdf'.
+from . import logger
 
-# Configure the logger.
-logger = iris.config.get_logger(__name__)
+# Avoid warning about unused import.
+# We could use an __all__, but we don't want to maintain one here
+logger
 
 # Standard CML spatio-temporal axis names.
 SPATIO_TEMPORAL_AXES = ["t", "z", "y", "x"]
 
-# Pass through CF attributes:
-#  - comment
-#  - Conventions
-#  - flag_masks
-#  - flag_meanings
-#  - flag_values
-#  - history
-#  - institution
-#  - reference
-#  - source
-#  - title
-#  - positive
-#
+# The CF-meaningful attributes which may appear on a data variable.
 _CF_ATTRS = [
     "add_offset",
     "ancillary_variables",
@@ -447,555 +436,6 @@ def coord(self, name):
         return result
 
 
-def _actions_engine():
-    # Return an 'actions engine', which provides a pyke-rules-like interface to
-    # the core cf translation code.
-    # Deferred import to avoid circularity.
-    import iris.fileformats._nc_load_rules.engine as nc_actions_engine
-
-    engine = nc_actions_engine.Engine()
-    return engine
-
-
-class NetCDFDataProxy:
-    """A reference to the data payload of a single NetCDF file variable."""
-
-    __slots__ = ("shape", "dtype", "path", "variable_name", "fill_value")
-
-    def __init__(self, shape, dtype, path, variable_name, fill_value):
-        self.shape = shape
-        self.dtype = dtype
-        self.path = path
-        self.variable_name = variable_name
-        self.fill_value = fill_value
-
-    @property
-    def ndim(self):
-        return len(self.shape)
-
-    def __getitem__(self, keys):
-        dataset = netCDF4.Dataset(self.path)
-        try:
-            variable = dataset.variables[self.variable_name]
-            # Get the NetCDF variable data and slice.
-            var = variable[keys]
-        finally:
-            dataset.close()
-        return np.asanyarray(var)
-
-    def __repr__(self):
-        fmt = (
-            "<{self.__class__.__name__} shape={self.shape}"
-            " dtype={self.dtype!r} path={self.path!r}"
-            " variable_name={self.variable_name!r}>"
-        )
-        return fmt.format(self=self)
-
-    def __getstate__(self):
-        return {attr: getattr(self, attr) for attr in self.__slots__}
-
-    def __setstate__(self, state):
-        for key, value in state.items():
-            setattr(self, key, value)
-
-
-def _assert_case_specific_facts(engine, cf, cf_group):
-    # Initialise a data store for built cube elements.
-    # This is used to patch element attributes *not* setup by the actions
-    # process, after the actions code has run.
-    engine.cube_parts["coordinates"] = []
-    engine.cube_parts["cell_measures"] = []
-    engine.cube_parts["ancillary_variables"] = []
-
-    # Assert facts for CF coordinates.
-    for cf_name in cf_group.coordinates.keys():
-        engine.add_case_specific_fact("coordinate", (cf_name,))
-
-    # Assert facts for CF auxiliary coordinates.
-    for cf_name in cf_group.auxiliary_coordinates.keys():
-        engine.add_case_specific_fact("auxiliary_coordinate", (cf_name,))
-
-    # Assert facts for CF cell measures.
-    for cf_name in cf_group.cell_measures.keys():
-        engine.add_case_specific_fact("cell_measure", (cf_name,))
-
-    # Assert facts for CF ancillary variables.
-    for cf_name in cf_group.ancillary_variables.keys():
-        engine.add_case_specific_fact("ancillary_variable", (cf_name,))
-
-    # Assert facts for CF grid_mappings.
-    for cf_name in cf_group.grid_mappings.keys():
-        engine.add_case_specific_fact("grid_mapping", (cf_name,))
-
-    # Assert facts for CF labels.
-    for cf_name in cf_group.labels.keys():
-        engine.add_case_specific_fact("label", (cf_name,))
-
-    # Assert facts for CF formula terms associated with the cf_group
-    # of the CF data variable.
-
-    # Collect varnames of formula-root variables as we go.
-    # NOTE: use dictionary keys as an 'OrderedSet'
-    #   - see: https://stackoverflow.com/a/53657523/2615050
-    # This is to ensure that we can handle the resulting facts in a definite
-    # order, as using a 'set' led to indeterminate results.
-    formula_root = {}
-    for cf_var in cf.cf_group.formula_terms.values():
-        for cf_root, cf_term in cf_var.cf_terms_by_root.items():
-            # Only assert this fact if the formula root variable is
-            # defined in the CF group of the CF data variable.
-            if cf_root in cf_group:
-                formula_root[cf_root] = True
-                engine.add_case_specific_fact(
-                    "formula_term",
-                    (cf_var.cf_name, cf_root, cf_term),
-                )
-
-    for cf_root in formula_root.keys():
-        engine.add_case_specific_fact("formula_root", (cf_root,))
-
-
-def _actions_activation_stats(engine, cf_name):
-    print("-" * 80)
-    print("CF Data Variable: %r" % cf_name)
-
-    engine.print_stats()
-
-    print("Rules Triggered:")
-
-    for rule in sorted(list(engine.rules_triggered)):
-        print("\t%s" % rule)
-
-    print("Case Specific Facts:")
-    kb_facts = engine.get_kb()
-
-    for key in kb_facts.entity_lists.keys():
-        for arg in kb_facts.entity_lists[key].case_specific_facts:
-            print("\t%s%s" % (key, arg))
-
-
-def _set_attributes(attributes, key, value):
-    """Set attributes dictionary, converting unicode strings appropriately."""
-
-    if isinstance(value, str):
-        try:
-            attributes[str(key)] = str(value)
-        except UnicodeEncodeError:
-            attributes[str(key)] = value
-    else:
-        attributes[str(key)] = value
-
-
-def _add_unused_attributes(iris_object, cf_var):
-    """
-    Populate the attributes of a cf element with the "unused" attributes
-    from the associated CF-netCDF variable. That is, all those that aren't CF
-    reserved terms.
-
-    """
-
-    def attribute_predicate(item):
-        return item[0] not in _CF_ATTRS
-
-    tmpvar = filter(attribute_predicate, cf_var.cf_attrs_unused())
-    for attr_name, attr_value in tmpvar:
-        _set_attributes(iris_object.attributes, attr_name, attr_value)
-
-
-def _get_actual_dtype(cf_var):
-    # Figure out what the eventual data type will be after any scale/offset
-    # transforms.
-    dummy_data = np.zeros(1, dtype=cf_var.dtype)
-    if hasattr(cf_var, "scale_factor"):
-        dummy_data = cf_var.scale_factor * dummy_data
-    if hasattr(cf_var, "add_offset"):
-        dummy_data = cf_var.add_offset + dummy_data
-    return dummy_data.dtype
-
-
-def _get_cf_var_data(cf_var, filename):
-    # Get lazy chunked data out of a cf variable.
-    dtype = _get_actual_dtype(cf_var)
-
-    # Create cube with deferred data, but no metadata
-    fill_value = getattr(
-        cf_var.cf_data,
-        "_FillValue",
-        netCDF4.default_fillvals[cf_var.dtype.str[1:]],
-    )
-    proxy = NetCDFDataProxy(
-        cf_var.shape, dtype, filename, cf_var.cf_name, fill_value
-    )
-    # Get the chunking specified for the variable : this is either a shape, or
-    # maybe the string "contiguous".
-    chunks = cf_var.cf_data.chunking()
-    # In the "contiguous" case, pass chunks=None to 'as_lazy_data'.
-    if chunks == "contiguous":
-        chunks = None
-    return as_lazy_data(proxy, chunks=chunks)
-
-
-class _OrderedAddableList(list):
-    """
-    A custom container object for actions recording.
-
-    Used purely in actions debugging, to accumulate a record of which actions
-    were activated.
-
-    It replaces a set, so as to preserve the ordering of operations, with
-    possible repeats, and it also numbers the entries.
-
-    The actions routines invoke an 'add' method, so this effectively replaces
-    a set.add with a list.append.
-
-    """
-
-    def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
-        self._n_add = 0
-
-    def add(self, msg):
-        self._n_add += 1
-        n_add = self._n_add
-        self.append(f"#{n_add:03d} : {msg}")
-
-
-def _load_cube(engine, cf, cf_var, filename):
-    from iris.cube import Cube
-
-    """Create the cube associated with the CF-netCDF data variable."""
-    data = _get_cf_var_data(cf_var, filename)
-    cube = Cube(data)
-
-    # Reset the actions engine.
-    engine.reset()
-
-    # Initialise engine rule processing hooks.
-    engine.cf_var = cf_var
-    engine.cube = cube
-    engine.cube_parts = {}
-    engine.requires = {}
-    engine.rules_triggered = _OrderedAddableList()
-    engine.filename = filename
-
-    # Assert all the case-specific facts.
-    # This extracts 'facts' specific to this data-variable (aka cube), from
-    # the info supplied in the CFGroup object.
-    _assert_case_specific_facts(engine, cf, cf_var.cf_group)
-
-    # Run the actions engine.
-    # This creates various cube elements and attaches them to the cube.
-    # It also records various other info on the engine, to be processed later.
-    engine.activate()
-
-    # Having run the rules, now add the "unused" attributes to each cf element.
-    def fix_attributes_all_elements(role_name):
-        elements_and_names = engine.cube_parts.get(role_name, [])
-
-        for iris_object, cf_var_name in elements_and_names:
-            _add_unused_attributes(iris_object, cf.cf_group[cf_var_name])
-
-    # Populate the attributes of all coordinates, cell-measures and ancillary-vars.
-    fix_attributes_all_elements("coordinates")
-    fix_attributes_all_elements("ancillary_variables")
-    fix_attributes_all_elements("cell_measures")
-
-    # Also populate attributes of the top-level cube itself.
-    _add_unused_attributes(cube, cf_var)
-
-    # Work out reference names for all the coords.
-    names = {
-        coord.var_name: coord.standard_name or coord.var_name or "unknown"
-        for coord in cube.coords()
-    }
-
-    # Add all the cube cell methods.
-    cube.cell_methods = [
-        iris.coords.CellMethod(
-            method=method.method,
-            intervals=method.intervals,
-            comments=method.comments,
-            coords=[
-                names[coord_name] if coord_name in names else coord_name
-                for coord_name in method.coord_names
-            ],
-        )
-        for method in cube.cell_methods
-    ]
-
-    if DEBUG:
-        # Show activation statistics for this data-var (i.e. cube).
-        _actions_activation_stats(engine, cf_var.cf_name)
-
-    return cube
-
-
-def _load_aux_factory(engine, cube):
-    """
-    Convert any CF-netCDF dimensionless coordinate to an AuxCoordFactory.
-
-    """
-    formula_type = engine.requires.get("formula_type")
-    if formula_type in [
-        "atmosphere_sigma_coordinate",
-        "atmosphere_hybrid_height_coordinate",
-        "atmosphere_hybrid_sigma_pressure_coordinate",
-        "ocean_sigma_z_coordinate",
-        "ocean_sigma_coordinate",
-        "ocean_s_coordinate",
-        "ocean_s_coordinate_g1",
-        "ocean_s_coordinate_g2",
-    ]:
-
-        def coord_from_term(term):
-            # Convert term names to coordinates (via netCDF variable names).
-            name = engine.requires["formula_terms"].get(term, None)
-            if name is not None:
-                for coord, cf_var_name in engine.cube_parts["coordinates"]:
-                    if cf_var_name == name:
-                        return coord
-                warnings.warn(
-                    "Unable to find coordinate for variable "
-                    "{!r}".format(name)
-                )
-
-        if formula_type == "atmosphere_sigma_coordinate":
-            pressure_at_top = coord_from_term("ptop")
-            sigma = coord_from_term("sigma")
-            surface_air_pressure = coord_from_term("ps")
-            factory = AtmosphereSigmaFactory(
-                pressure_at_top, sigma, surface_air_pressure
-            )
-        elif formula_type == "atmosphere_hybrid_height_coordinate":
-            delta = coord_from_term("a")
-            sigma = coord_from_term("b")
-            orography = coord_from_term("orog")
-            factory = HybridHeightFactory(delta, sigma, orography)
-        elif formula_type == "atmosphere_hybrid_sigma_pressure_coordinate":
-            # Hybrid pressure has two valid versions of its formula terms:
-            # "p0: var1 a: var2 b: var3 ps: var4" or
-            # "ap: var1 b: var2 ps: var3" where "ap = p0 * a"
-            # Attempt to get the "ap" term.
-            delta = coord_from_term("ap")
-            if delta is None:
-                # The "ap" term is unavailable, so try getting terms "p0"
-                # and "a" terms in order to derive an "ap" equivalent term.
-                coord_p0 = coord_from_term("p0")
-                if coord_p0 is not None:
-                    if coord_p0.shape != (1,):
-                        msg = (
-                            "Expecting {!r} to be a scalar reference "
-                            "pressure coordinate, got shape {!r}".format(
-                                coord_p0.var_name, coord_p0.shape
-                            )
-                        )
-                        raise ValueError(msg)
-                    if coord_p0.has_bounds():
-                        msg = (
-                            "Ignoring atmosphere hybrid sigma pressure "
-                            "scalar coordinate {!r} bounds.".format(
-                                coord_p0.name()
-                            )
-                        )
-                        warnings.warn(msg)
-                    coord_a = coord_from_term("a")
-                    if coord_a is not None:
-                        if coord_a.units.is_unknown():
-                            # Be graceful, and promote unknown to dimensionless units.
-                            coord_a.units = "1"
-                        delta = coord_a * coord_p0.points[0]
-                        delta.units = coord_a.units * coord_p0.units
-                        delta.rename("vertical pressure")
-                        delta.var_name = "ap"
-                        cube.add_aux_coord(delta, cube.coord_dims(coord_a))
-
-            sigma = coord_from_term("b")
-            surface_air_pressure = coord_from_term("ps")
-            factory = HybridPressureFactory(delta, sigma, surface_air_pressure)
-        elif formula_type == "ocean_sigma_z_coordinate":
-            sigma = coord_from_term("sigma")
-            eta = coord_from_term("eta")
-            depth = coord_from_term("depth")
-            depth_c = coord_from_term("depth_c")
-            nsigma = coord_from_term("nsigma")
-            zlev = coord_from_term("zlev")
-            factory = OceanSigmaZFactory(
-                sigma, eta, depth, depth_c, nsigma, zlev
-            )
-        elif formula_type == "ocean_sigma_coordinate":
-            sigma = coord_from_term("sigma")
-            eta = coord_from_term("eta")
-            depth = coord_from_term("depth")
-            factory = OceanSigmaFactory(sigma, eta, depth)
-        elif formula_type == "ocean_s_coordinate":
-            s = coord_from_term("s")
-            eta = coord_from_term("eta")
-            depth = coord_from_term("depth")
-            a = coord_from_term("a")
-            depth_c = coord_from_term("depth_c")
-            b = coord_from_term("b")
-            factory = OceanSFactory(s, eta, depth, a, b, depth_c)
-        elif formula_type == "ocean_s_coordinate_g1":
-            s = coord_from_term("s")
-            c = coord_from_term("c")
-            eta = coord_from_term("eta")
-            depth = coord_from_term("depth")
-            depth_c = coord_from_term("depth_c")
-            factory = OceanSg1Factory(s, c, eta, depth, depth_c)
-        elif formula_type == "ocean_s_coordinate_g2":
-            s = coord_from_term("s")
-            c = coord_from_term("c")
-            eta = coord_from_term("eta")
-            depth = coord_from_term("depth")
-            depth_c = coord_from_term("depth_c")
-            factory = OceanSg2Factory(s, c, eta, depth, depth_c)
-        cube.add_aux_factory(factory)
-
-
-def _translate_constraints_to_var_callback(constraints):
-    """
-    Translate load constraints into a simple data-var filter function, if possible.
-
-    Returns:
-         * function(cf_var:CFDataVariable): --> bool,
-            or None.
-
-    For now, ONLY handles a single NameConstraint with no 'STASH' component.
-
-    """
-    import iris._constraints
-
-    constraints = iris._constraints.list_of_constraints(constraints)
-    result = None
-    if len(constraints) == 1:
-        (constraint,) = constraints
-        if (
-            isinstance(constraint, iris._constraints.NameConstraint)
-            and constraint.STASH == "none"
-        ):
-            # As long as it doesn't use a STASH match, then we can treat it as
-            # a testing against name properties of cf_var.
-            # That's just like testing against name properties of a cube, except that they may not all exist.
-            def inner(cf_datavar):
-                match = True
-                for name in constraint._names:
-                    expected = getattr(constraint, name)
-                    if name != "STASH" and expected != "none":
-                        attr_name = "cf_name" if name == "var_name" else name
-                        # Fetch property : N.B. CFVariable caches the property values
-                        # The use of a default here is the only difference from the code in NameConstraint.
-                        if not hasattr(cf_datavar, attr_name):
-                            continue
-                        actual = getattr(cf_datavar, attr_name, "")
-                        if actual != expected:
-                            match = False
-                            break
-                return match
-
-            result = inner
-    return result
-
-
-def load_cubes(filenames, callback=None, constraints=None):
-    """
-    Loads cubes from a list of NetCDF filenames/OPeNDAP URLs.
-
-    Args:
-
-    * filenames (string/list):
-        One or more NetCDF filenames/OPeNDAP URLs to load from.
-
-    Kwargs:
-
-    * callback (callable function):
-        Function which can be passed on to :func:`iris.io.run_callback`.
-
-    Returns:
-        Generator of loaded NetCDF :class:`iris.cube.Cube`.
-
-    """
-    # TODO: rationalise UGRID/mesh handling once experimental.ugrid is folded
-    #  into standard behaviour.
-    # Deferred import to avoid circular imports.
-    from iris.experimental.ugrid.cf import CFUGridReader
-    from iris.experimental.ugrid.load import (
-        PARSE_UGRID_ON_LOAD,
-        _build_mesh_coords,
-        _meshes_from_cf,
-    )
-    from iris.io import run_callback
-
-    # Create a low-level data-var filter from the original load constraints, if they are suitable.
-    var_callback = _translate_constraints_to_var_callback(constraints)
-
-    # Create an actions engine.
-    engine = _actions_engine()
-
-    if isinstance(filenames, str):
-        filenames = [filenames]
-
-    for filename in filenames:
-        # Ingest the netCDF file.
-        meshes = {}
-        if PARSE_UGRID_ON_LOAD:
-            cf = CFUGridReader(filename)
-            meshes = _meshes_from_cf(cf)
-        else:
-            cf = iris.fileformats.cf.CFReader(filename)
-
-        # Process each CF data variable.
-        data_variables = list(cf.cf_group.data_variables.values()) + list(
-            cf.cf_group.promoted.values()
-        )
-        for cf_var in data_variables:
-            if var_callback and not var_callback(cf_var):
-                # Deliver only selected results.
-                continue
-
-            # cf_var-specific mesh handling, if a mesh is present.
-            # Build the mesh_coords *before* loading the cube - avoids
-            # mesh-related attributes being picked up by
-            # _add_unused_attributes().
-            mesh_name = None
-            mesh = None
-            mesh_coords, mesh_dim = [], None
-            if PARSE_UGRID_ON_LOAD:
-                mesh_name = getattr(cf_var, "mesh", None)
-            if mesh_name is not None:
-                try:
-                    mesh = meshes[mesh_name]
-                except KeyError:
-                    message = (
-                        f"File does not contain mesh: '{mesh_name}' - "
-                        f"referenced by variable: '{cf_var.cf_name}' ."
-                    )
-                    logger.debug(message)
-            if mesh is not None:
-                mesh_coords, mesh_dim = _build_mesh_coords(mesh, cf_var)
-
-            cube = _load_cube(engine, cf, cf_var, filename)
-
-            # Attach the mesh (if present) to the cube.
-            for mesh_coord in mesh_coords:
-                cube.add_aux_coord(mesh_coord, mesh_dim)
-
-            # Process any associated formula terms and attach
-            # the corresponding AuxCoordFactory.
-            try:
-                _load_aux_factory(engine, cube)
-            except ValueError as e:
-                warnings.warn("{}".format(e))
-
-            # Perform any user registered callback function.
-            cube = run_callback(callback, cube, cf_var, filename)
-
-            # Callback mechanism may return None, which must not be yielded
-            if cube is None:
-                continue
-
-            yield cube
-
-
 def _bytes_if_ascii(string):
     """
     Convert the given string to a byte string (str in py2k, bytes in py3k)
@@ -1837,7 +1277,9 @@ def _get_dim_names(self, cube_or_mesh):
 
         """
 
-        def record_dimension(names_list, dim_name, length, matching_coords=[]):
+        def record_dimension(
+            names_list, dim_name, length, matching_coords=None
+        ):
             """
             Record a file dimension, its length and associated "coordinates"
             (which may in fact also be connectivities).
@@ -1846,6 +1288,8 @@ def record_dimension(names_list, dim_name, length, matching_coords=[]):
             matches the earlier finding.
 
             """
+            if matching_coords is None:
+                matching_coords = []
             if dim_name not in self._existing_dim:
                 self._existing_dim[dim_name] = length
             else:
diff --git a/lib/iris/fileformats/pp.py b/lib/iris/fileformats/pp.py
index 1fb7d4e178..bc35acb3b3 100644
--- a/lib/iris/fileformats/pp.py
+++ b/lib/iris/fileformats/pp.py
@@ -767,6 +767,18 @@ def _data_bytes_to_shaped_array(
 
     else:
         # Reform in row-column order
+        actual_length = np.prod(data.shape)
+        if (expected_length := np.prod(data_shape)) != actual_length:
+            if (expected_length < actual_length) and (data.ndim == 1):
+                # known use case where mule adds padding to data payload
+                # for a collapsed field.
+                data = data[:expected_length]
+            else:
+                emsg = (
+                    f"PP field data containing {actual_length} words does not "
+                    f"match expected length of {expected_length} words."
+                )
+                raise ValueError(emsg)
         data.shape = data_shape
 
     # Mask the array
diff --git a/lib/iris/fileformats/pp_save_rules.py b/lib/iris/fileformats/pp_save_rules.py
index e6b3748f9b..0369fc9fd0 100644
--- a/lib/iris/fileformats/pp_save_rules.py
+++ b/lib/iris/fileformats/pp_save_rules.py
@@ -422,7 +422,16 @@ def _grid_and_pole_rules(cube, pp):
     lat_coord = vector_coord(cube, "latitude")
     grid_lat_coord = vector_coord(cube, "grid_latitude")
 
-    if lon_coord and not is_regular(lon_coord):
+    scalar_lon_coord = scalar_coord(cube, "longitude")
+
+    if lon_coord is None and grid_lon_coord is None and scalar_lon_coord:
+        # default value of 360.0 degrees to specify a circular wrap of
+        # the collapsed scalar longitude coordinate, based on examples
+        # of model output for several different diagnostics
+        pp.bdx = (unit := scalar_lon_coord.units) and unit.modulus or 360.0
+        pp.bzx = scalar_lon_coord.points[0] - pp.bdx
+        pp.lbnpt = scalar_lon_coord.shape[0]
+    elif lon_coord and not is_regular(lon_coord):
         pp.bzx = 0
         pp.bdx = 0
         pp.lbnpt = lon_coord.shape[0]
diff --git a/lib/iris/fileformats/um_cf_map.py b/lib/iris/fileformats/um_cf_map.py
index 8aee67ae3e..01539960a5 100644
--- a/lib/iris/fileformats/um_cf_map.py
+++ b/lib/iris/fileformats/um_cf_map.py
@@ -16,10 +16,12 @@
 LBFC_TO_CF = {
     5: CFName('atmosphere_boundary_layer_thickness', None, 'm'),
     16: CFName('air_temperature', None, 'K'),
+    22: CFName('wet_bulb_potential_temperature', None, 'K'),
     23: CFName('soil_temperature', None, 'K'),
     27: CFName('air_density', None, 'kg m-3'),
     36: CFName('land_area_fraction', None, '1'),
     37: CFName('sea_ice_area_fraction', None, '1'),
+    42: CFName('upward_air_velocity', None, 'm s-1'),
     50: CFName('wind_speed', None, 'm s-1'),
     56: CFName('x_wind', None, 'm s-1'),
     57: CFName('y_wind', None, 'm s-1'),
@@ -28,11 +30,16 @@
     83: CFName('potential_vorticity_of_atmosphere_layer', None, 'Pa-1 s-1'),
     94: CFName('convective_rainfall_amount', None, 'kg m-2'),
     97: CFName('rainfall_flux', None, 'kg m-2 s-1'),
+    98: CFName('convective_rainfall_flux', None, 'kg m-2 s-1'),
+    99: CFName('stratiform_rainfall_flux', None, 'kg m-2 s-1'),
     102: CFName('stratiform_rainfall_amount', None, 'kg m-2'),
+    106: CFName('soil_moisture_content', None, 'kg m-2'),
     108: CFName('snowfall_flux', None, 'kg m-2 s-1'),
     111: CFName('surface_runoff_amount', None, 'kg m-2'),
     116: CFName('stratiform_snowfall_amount', None, 'kg m-2'),
     117: CFName('convective_snowfall_amount', None, 'kg m-2'),
+    118: CFName('stratiform_snowfall_flux', None, 'kg m-2 s-1'),
+    119: CFName('convective_snowfall_flux', None, 'kg m-2 s-1'),
     122: CFName('moisture_content_of_soil_layer', None, 'kg m-2'),
     183: CFName('wind_speed', None, 'm s-1'),
     200: CFName('toa_incoming_shortwave_flux', None, 'W m-2'),
@@ -899,7 +906,7 @@
     'm01s30i301': CFName(None, 'Heavyside function on pressure levels', '1'),
     'm01s30i302': CFName('virtual_temperature', None, 'K'),
     'm01s30i310': CFName('northward_transformed_eulerian_mean_air_velocity', None, 'm s-1'),
-    'm01s30i311': CFName('northward_transformed_eulerian_mean_air_velocity', None, 'm s-1'),
+    'm01s30i311': CFName('upward_transformed_eulerian_mean_air_velocity', None, 'm s-1'),
     'm01s30i312': CFName('northward_eliassen_palm_flux_in_air', None, 'kg s-2'),
     'm01s30i313': CFName('upward_eliassen_palm_flux_in_air', None, 'kg s-2'),
     'm01s30i314': CFName('tendency_of_eastward_wind_due_to_eliassen_palm_flux_divergence', None, 'm s-2'),
@@ -1157,7 +1164,9 @@
     CFName('cloud_area_fraction_in_atmosphere_layer', None, '1'): 1720,
     CFName('convective_cloud_area_fraction', None, '1'): 34,
     CFName('convective_rainfall_amount', None, 'kg m-2'): 94,
+    CFName('convective_rainfall_flux', None, 'kg m-2 s-1'): 98,
     CFName('convective_snowfall_amount', None, 'kg m-2'): 117,
+    CFName('convective_snowfall_flux', None, 'kg m-2 s-1'): 119,
     CFName('dimensionless_exner_function', None, '1'): 7,
     CFName('divergence_of_wind', None, 's-1'): 74,
     CFName('downward_heat_flux_in_sea_ice', None, 'W m-2'): 261,
@@ -1203,6 +1212,7 @@
     CFName('soil_albedo', None, '1'): 1395,
     CFName('soil_carbon_content', None, 'kg m-2'): 1397,
     CFName('soil_hydraulic_conductivity_at_saturation', None, 'm s-1'): 333,
+    CFName('soil_moisture_content', None, 'kg m-2'): 106,
     CFName('soil_moisture_content_at_field_capacity', None, 'kg m-2'): 1559,
     CFName('soil_porosity', None, '1'): 332,
     CFName('soil_suction_at_saturation', None, 'Pa'): 342,
@@ -1212,8 +1222,10 @@
     CFName('specific_kinetic_energy_of_air', None, 'm2 s-2'): 60,
     CFName('stratiform_cloud_area_fraction_in_atmosphere_layer', None, '1'): 220,
     CFName('stratiform_rainfall_amount', None, 'kg m-2'): 102,
+    CFName('stratiform_rainfall_flux', None, 'kg m-2 s-1'): 99,
     CFName('stratiform_rainfall_rate', None, 'kg m-2 s-1'): 99,
     CFName('stratiform_snowfall_amount', None, 'kg m-2'): 116,
+    CFName('stratiform_snowfall_flux', None, 'kg m-2 s-1'): 118,
     CFName('subsurface_runoff_amount', None, 'kg m-2'): 112,
     CFName('subsurface_runoff_flux', None, 'kg m-2 s-1'): 1533,
     CFName('surface_albedo_assuming_deep_snow', None, '1'): 328,
@@ -1260,6 +1272,7 @@
     CFName('volume_fraction_of_condensed_water_in_soil_at_critical_point', None, '1'): 330,
     CFName('volume_fraction_of_condensed_water_in_soil_at_wilting_point', None, '1'): 329,
     CFName('water_potential_evaporation_flux', None, 'kg m-2 s-1'): 115,
+    CFName('wet_bulb_potential_temperature', None, 'K'): 22,
     CFName('wind_mixing_energy_flux_into_sea_water', None, 'W m-2'): 182,
     CFName('wind_speed', None, 'm s-1'): 50,
     CFName('x_wind', None, 'm s-1'): 56,
diff --git a/lib/iris/io/__init__.py b/lib/iris/io/__init__.py
index 4659f70ae3..7dd08c723c 100644
--- a/lib/iris/io/__init__.py
+++ b/lib/iris/io/__init__.py
@@ -59,6 +59,11 @@ def run_callback(callback, cube, field, filename):
         It is possible that this function returns None for certain callbacks,
         the caller of this function should handle this case.
 
+    .. note::
+
+        This function maintains laziness when called; it does not realise data.
+        See more at :doc:`/userguide/real_and_lazy_data`.
+
     """
     from iris.cube import Cube
 
@@ -424,6 +429,11 @@ def save(source, target, saver=None, **kwargs):
     >>> # Save a cube list to netCDF, using the NETCDF3_CLASSIC storage option
     >>> iris.save(my_cube_list, "myfile.nc", netcdf_format="NETCDF3_CLASSIC")
 
+    Notes
+    ------
+
+    This function maintains laziness when called; it does not realise data.
+    See more at :doc:`/userguide/real_and_lazy_data`.
 
     """
     from iris.cube import Cube, CubeList
diff --git a/lib/iris/iterate.py b/lib/iris/iterate.py
index 636635ee78..d6bac77d3b 100644
--- a/lib/iris/iterate.py
+++ b/lib/iris/iterate.py
@@ -58,6 +58,10 @@ def izip(*cubes, **kwargs):
         ...                                                 'grid_longitude']):
         ...    pass
 
+    Notes
+    ------
+    This function maintains laziness when called; it does not realise data.
+    See more at :doc:`/userguide/real_and_lazy_data`.
     """
     if not cubes:
         raise TypeError("Expected one or more cubes.")
diff --git a/lib/iris/palette.py b/lib/iris/palette.py
index 626ae4e341..a1c0a1e878 100644
--- a/lib/iris/palette.py
+++ b/lib/iris/palette.py
@@ -15,6 +15,7 @@
 import re
 
 import cf_units
+from matplotlib import colormaps as mpl_colormaps
 import matplotlib.cm as mpl_cm
 import matplotlib.colors as mpl_colors
 import numpy as np
@@ -120,6 +121,11 @@ def cmap_norm(cube):
         Tuple of :class:`matplotlib.colors.LinearSegmentedColormap` and
         :class:`iris.palette.SymmetricNormalize`
 
+    Notes
+    ------
+    This function maintains laziness when called; it does not realise data.
+    See more at :doc:`/userguide/real_and_lazy_data`.
+
     """
     args, kwargs = _default_cmap_norm((cube,), {})
     return kwargs.get("cmap"), kwargs.get("norm")
@@ -337,7 +343,7 @@ def _load_palette():
             )
 
         # Register the color map for use.
-        mpl_cm.register_cmap(cmap=cmap)
+        mpl_colormaps.register(cmap)
 
 
 # Ensure to load the color map palettes.
diff --git a/lib/iris/plot.py b/lib/iris/plot.py
index 2da91e8c67..8cd849b716 100644
--- a/lib/iris/plot.py
+++ b/lib/iris/plot.py
@@ -13,11 +13,13 @@
 
 import collections
 import datetime
+import warnings
 
 import cartopy.crs as ccrs
 from cartopy.geodesic import Geodesic
 import cartopy.mpl.geoaxes
 import cftime
+import matplotlib.animation as animation
 import matplotlib.axes
 import matplotlib.collections as mpl_collections
 import matplotlib.dates as mpl_dates
@@ -1344,11 +1346,6 @@ def outline(cube, coords=None, color="k", linewidth=None, axes=None):
         axes=axes,
     )
 
-    # set the _is_stroked property to get a single color grid.
-    # See https://github.com/matplotlib/matplotlib/issues/1302
-    result._is_stroked = False
-    if hasattr(result, "_wrapped_collection_fix"):
-        result._wrapped_collection_fix._is_stroked = False
     return result
 
 
@@ -1808,3 +1805,114 @@ def citation(text, figure=None, axes=None):
         anchor.patch.set_boxstyle("round, pad=0, rounding_size=0.2")
         axes = axes if axes else figure.gca()
         axes.add_artist(anchor)
+
+
+def animate(cube_iterator, plot_func, fig=None, **kwargs):
+    """
+    Animates the given cube iterator.
+
+    Parameters
+    ----------
+    cube_iterator : iterable of :class:`iris.cube.Cube` objects
+        Each animation frame corresponds to each :class:`iris.cube.Cube`
+        object. See :meth:`iris.cube.Cube.slices`.
+    plot_func : :mod:`iris.plot` or :mod:`iris.quickplot` plotting function
+        Plotting function used to animate. Must accept the signature
+        ``plot_func(cube, vmin=vmin, vmax=vmax, coords=coords)``.
+        :func:`~iris.plot.contourf`, :func:`~iris.plot.contour`,
+        :func:`~iris.plot.pcolor` and :func:`~iris.plot.pcolormesh`
+        all conform to this signature.
+    fig : :class:`matplotlib.figure.Figure` instance, optional
+        By default, the current figure will be used or a new figure instance
+        created if no figure is available. See :func:`matplotlib.pyplot.gcf`.
+    **kwargs : dict, optional
+        Valid keyword arguments:
+
+        coords: list of :class:`~iris.coords.Coord` objects or coordinate names
+            Use the given coordinates as the axes for the plot. The order of the
+            given coordinates indicates which axis to use for each, where the first
+            element is the horizontal axis of the plot and the second element is
+            the vertical axis of the plot.
+        interval: int, float or long
+            Defines the time interval in milliseconds between successive frames.
+            A default interval of 100ms is set.
+        vmin, vmax: int, float or long
+            Color scaling values, see :class:`matplotlib.colors.Normalize` for
+            further details. Default values are determined by the min-max across
+            the data set over the entire sequence.
+
+        See :class:`matplotlib.animation.FuncAnimation` for details of other
+        valid keyword arguments.
+
+    Returns
+    -------
+    :class:`~matplotlib.animation.FuncAnimation` object suitable for
+    saving and or plotting.
+
+    Examples
+    --------
+    >>> import iris
+    >>> from iris import plot as iplt
+    >>> from iris import quickplot as qplt
+    >>> my_cube = iris.load_cube(iris.sample_data_path("A1B_north_america.nc"))
+
+    To animate along a set of :class:`~iris.cube.Cube` slices :
+
+    >>> cube_iter = my_cube.slices(("longitude", "latitude"))
+    >>> ani = iplt.animate(cube_iter, qplt.contourf)
+    >>> iplt.show()
+
+    """
+    kwargs.setdefault("interval", 100)
+    coords = kwargs.pop("coords", None)
+
+    if fig is None:
+        fig = plt.gcf()
+
+    def update_animation_iris(i, cubes, vmin, vmax, coords):
+        # Clearing the figure is currently necessary for compatibility with
+        # the iris quickploting module - due to the colorbar.
+        plt.gcf().clf()
+        plot_func(cubes[i], vmin=vmin, vmax=vmax, coords=coords)
+
+    # Turn cube iterator into a list to determine plot ranges.
+    # NOTE: we check that we are not providing a cube as this has a deprecated
+    # iter special method.
+    if hasattr(cube_iterator, "__iter__") and not isinstance(
+        cube_iterator, iris.cube.Cube
+    ):
+        cubes = iris.cube.CubeList(cube_iterator)
+    else:
+        msg = "iterable type object required for animation, {} given".format(
+            type(cube_iterator)
+        )
+        raise TypeError(msg)
+
+    supported = ["iris.plot", "iris.quickplot"]
+    if plot_func.__module__ not in supported:
+        msg = (
+            'Given plotting module "{}" may not be supported, intended '
+            "use: {}."
+        )
+        msg = msg.format(plot_func.__module__, supported)
+        warnings.warn(msg, UserWarning)
+
+    supported = ["contour", "contourf", "pcolor", "pcolormesh"]
+    if plot_func.__name__ not in supported:
+        msg = (
+            'Given plotting function "{}" may not be supported, intended '
+            "use: {}."
+        )
+        msg = msg.format(plot_func.__name__, supported)
+        warnings.warn(msg, UserWarning)
+
+    # Determine plot range.
+    vmin = kwargs.pop("vmin", min([cc.data.min() for cc in cubes]))
+    vmax = kwargs.pop("vmax", max([cc.data.max() for cc in cubes]))
+
+    update = update_animation_iris
+    frames = range(len(cubes))
+
+    return animation.FuncAnimation(
+        fig, update, frames=frames, fargs=(cubes, vmin, vmax, coords), **kwargs
+    )
diff --git a/requirements/ci/nox.lock/py310-linux-64.lock b/requirements/ci/nox.lock/py310-linux-64.lock
index d88fd19a29..6cea5880aa 100644
--- a/requirements/ci/nox.lock/py310-linux-64.lock
+++ b/requirements/ci/nox.lock/py310-linux-64.lock
@@ -1,59 +1,61 @@
 # Generated by conda-lock.
 # platform: linux-64
-# input_hash: 043088e81c1e979eac04ac622e72d5d9f2c559c9059eae30112aafa081dffa6d
+# input_hash: e755261bf8cb4508d0b291e9efd406c55fdcebf113bdeb4ea14bfccf3f623e56
 @EXPLICIT
 https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81
-https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2022.6.15-ha878542_0.tar.bz2#c320890f77fd1d617fa876e0982002c2
+https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2022.9.24-ha878542_0.tar.bz2#41e4e87062433e283696cf384f952ef6
 https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2#0c96522c6bdaed4b1566d11387caaf45
 https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6
 https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb
 https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-hab24e00_0.tar.bz2#19410c3df09dfb12d1206132a1d357c5
-https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.36.1-hea4e1c9_2.tar.bz2#bd4f2e711b39af170e7ff15163fe87ee
-https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-12.1.0-hdcd56e2_16.tar.bz2#b02605b875559ff99f04351fd5040760
-https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-12.1.0-ha89aaad_16.tar.bz2#6f5ba041a41eb102a1027d9e68731be7
+https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.39-hc81fddc_0.tar.bz2#c2719e2faa7bd7076d3a4b52271e5622
+https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-12.2.0-h337968e_19.tar.bz2#164b4b1acaedc47ee7e658ae6b308ca3
+https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-12.2.0-h46fd767_19.tar.bz2#1030b1f38c129f2634eae026f704fe60
 https://conda.anaconda.org/conda-forge/linux-64/mpi-1.0-mpich.tar.bz2#c1fcff3417b5a22bbc4cf6e8c23648cf
-https://conda.anaconda.org/conda-forge/noarch/tzdata-2022c-h191b570_0.tar.bz2#a56386ad31a7322940dd7d03fb3a9979
+https://conda.anaconda.org/conda-forge/noarch/tzdata-2022f-h191b570_0.tar.bz2#e366350e2343a798e29833286abe2560
 https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2#f766549260d6815b0c52253f1fb1bb29
-https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-12.1.0-h69a702a_16.tar.bz2#6bf15e29a20f614b18ae89368260d0a2
-https://conda.anaconda.org/conda-forge/linux-64/libgomp-12.1.0-h8d9b700_16.tar.bz2#f013cf7749536ce43d82afbffdf499ab
+https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-12.2.0-h69a702a_19.tar.bz2#cd7a806282c16e1f2d39a7e80d3a3e0d
+https://conda.anaconda.org/conda-forge/linux-64/libgomp-12.2.0-h65d4601_19.tar.bz2#cedcee7c064c01c403f962c9e8d3c373
 https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2#73aaf86a425cc6e73fcf236a5a46396d
 https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab
-https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-12.1.0-h8d9b700_16.tar.bz2#4f05bc9844f7c101e6e147dab3c88d5c
-https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.6.1-h7f98852_0.tar.bz2#0347ce6a34f8b55b544b141432c6d4c7
+https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-12.2.0-h65d4601_19.tar.bz2#e4c94f80aef025c17ab0828cd85ef535
+https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.8-h166bdaf_0.tar.bz2#be733e69048951df1e4b4b7bb8c7666f
 https://conda.anaconda.org/conda-forge/linux-64/attr-2.5.1-h166bdaf_1.tar.bz2#d9c69a24ad678ffce24c6543a0176b00
 https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h7f98852_4.tar.bz2#a1fd65c7ccbf10880423d82bca54eb54
 https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.18.1-h7f98852_0.tar.bz2#f26ef8098fab1f719c91eb760d63381a
-https://conda.anaconda.org/conda-forge/linux-64/expat-2.4.8-h27087fc_0.tar.bz2#e1b07832504eeba765d648389cc387a9
-https://conda.anaconda.org/conda-forge/linux-64/fftw-3.3.10-nompi_ha7695d1_103.tar.bz2#a56c5033619bdf56a22a1f0a0fd286aa
+https://conda.anaconda.org/conda-forge/linux-64/expat-2.5.0-h27087fc_0.tar.bz2#c4fbad8d4bddeb3c085f18cbf97fbfad
+https://conda.anaconda.org/conda-forge/linux-64/fftw-3.3.10-nompi_hf0379b8_105.tar.bz2#9d3e01547ba04a57372beee01158096f
 https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.10-h36c2ea0_0.tar.bz2#ac7bc6a654f8f41b352b38f4051135f8
-https://conda.anaconda.org/conda-forge/linux-64/geos-3.11.0-h27087fc_0.tar.bz2#a583d0bc9a85c48e8b07a588d1ac8a80
+https://conda.anaconda.org/conda-forge/linux-64/geos-3.11.1-h27087fc_0.tar.bz2#917b9a50001fffdd89b321b5dba31e55
+https://conda.anaconda.org/conda-forge/linux-64/gettext-0.21.1-h27087fc_0.tar.bz2#14947d8770185e5153fdd04d4673ed37
 https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.1-h36c2ea0_2.tar.bz2#626e68ae9cc5912d6adb79d318cf962d
 https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.13-h58526e2_1001.tar.bz2#8c54672728e8ec6aa6db90cf2806d220
 https://conda.anaconda.org/conda-forge/linux-64/icu-70.1-h27087fc_0.tar.bz2#87473a15119779e021c314249d4b4aed
 https://conda.anaconda.org/conda-forge/linux-64/jpeg-9e-h166bdaf_2.tar.bz2#ee8b844357a0946870901c7c6f418268
 https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2#30186d27e2c9fa62b45fb1476b7200e3
+https://conda.anaconda.org/conda-forge/linux-64/lame-3.100-h166bdaf_1003.tar.bz2#a8832b479f93521a9e7b5b743803be51
 https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h27087fc_0.tar.bz2#76bbff344f0134279f225174e9064c8f
-https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.0.9-h166bdaf_7.tar.bz2#f82dc1c78bcf73583f2656433ce2933c
+https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.0.9-h166bdaf_8.tar.bz2#9194c9bf9428035a05352d031462eae4
 https://conda.anaconda.org/conda-forge/linux-64/libdb-6.2.32-h9c3ff4c_0.tar.bz2#3f3258d8f841fbac63b36b75bdac1afd
-https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.13-h166bdaf_0.tar.bz2#4b5bee2e957570197327d0b20a718891
+https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.14-h166bdaf_0.tar.bz2#fc84a0446e4e4fb882e78d786cfb9734
 https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-h516909a_1.tar.bz2#6f8720dff19e17ce5d48cfe7f3d2f0a3
 https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2#d645c6d2ac96843a2bfaccd2d62b3ac3
-https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.16-h516909a_0.tar.bz2#5c0f338a513a2943c659ae619fca9211
+https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.17-h166bdaf_0.tar.bz2#b62b52da46c39ee2bc3c162ac7f1804d
 https://conda.anaconda.org/conda-forge/linux-64/libmo_unpack-3.1.2-hf484d3e_1001.tar.bz2#95f32a6a5a666d33886ca5627239f03d
 https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.0-h7f98852_0.tar.bz2#39b1328babf85c7c3a61636d9cd50206
 https://conda.anaconda.org/conda-forge/linux-64/libogg-1.3.4-h7f98852_1.tar.bz2#6e8cc2173440d77708196c5b93771680
-https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.21-pthreads_h78a6416_2.tar.bz2#839776c4e967bc881c21da197127a3ae
+https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.21-pthreads_h78a6416_3.tar.bz2#8c5963a49b6035c40646a763293fbb35
 https://conda.anaconda.org/conda-forge/linux-64/libopus-1.3.1-h7f98852_1.tar.bz2#15345e56d527b330e1cacbdf58676e8f
 https://conda.anaconda.org/conda-forge/linux-64/libtool-2.4.6-h9c3ff4c_1008.tar.bz2#16e143a1ed4b4fd169536373957f6fee
-https://conda.anaconda.org/conda-forge/linux-64/libudev1-249-h166bdaf_4.tar.bz2#dc075ff6fcb46b3d3c7652e543d5f334
+https://conda.anaconda.org/conda-forge/linux-64/libudev1-252-h166bdaf_0.tar.bz2#174243089ec111479298a5b7099b64b5
 https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.32.1-h7f98852_1000.tar.bz2#772d69f030955d9646d3d0eaf21d859d
 https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.2.4-h166bdaf_0.tar.bz2#ac2ccf7323d21f2994e4d1f5da664f37
-https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.2.12-h166bdaf_2.tar.bz2#8302381297332ea50532cf2c67961080
-https://conda.anaconda.org/conda-forge/linux-64/mpich-4.0.2-h846660c_100.tar.bz2#36a36fe04b932d4b327e7e81c5c43696
+https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.2.13-h166bdaf_4.tar.bz2#f3f9de449d32ca9b9c66a22863c96f41
+https://conda.anaconda.org/conda-forge/linux-64/mpg123-1.30.2-h27087fc_1.tar.bz2#2fe2a839394ef3a1825a5e5e296060bc
+https://conda.anaconda.org/conda-forge/linux-64/mpich-4.0.3-h846660c_100.tar.bz2#50d66bb751cfa71ee2a48b2d3eb90ac1
 https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.3-h27087fc_1.tar.bz2#4acfc691e64342b9dae57cf2adc63238
 https://conda.anaconda.org/conda-forge/linux-64/nspr-4.32-h9c3ff4c_1.tar.bz2#29ded371806431b0499aaee146abfc3e
-https://conda.anaconda.org/conda-forge/linux-64/openssl-1.1.1q-h166bdaf_0.tar.bz2#07acc367c7fc8b716770cd5b36d31717
-https://conda.anaconda.org/conda-forge/linux-64/pcre-8.45-h9c3ff4c_0.tar.bz2#c05d1820a6d34ff07aaaab7a9b7eddaa
+https://conda.anaconda.org/conda-forge/linux-64/openssl-1.1.1s-h166bdaf_0.tar.bz2#e17553617ce05787d97715177be014d1
 https://conda.anaconda.org/conda-forge/linux-64/pixman-0.40.0-h36c2ea0_0.tar.bz2#660e72c82f2e75a6b3fe6a6e75c79f19
 https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-h36c2ea0_1001.tar.bz2#22dad4df6e8630e8dff2428f6f6a7036
 https://conda.anaconda.org/conda-forge/linux-64/xorg-kbproto-1.0.7-h7f98852_1002.tar.bz2#4b230e8381279d76131116660f5a241a
@@ -66,92 +68,94 @@ https://conda.anaconda.org/conda-forge/linux-64/xorg-xproto-7.0.31-h7f98852_1007
 https://conda.anaconda.org/conda-forge/linux-64/xxhash-0.8.0-h7f98852_3.tar.bz2#52402c791f35e414e704b7a113f99605
 https://conda.anaconda.org/conda-forge/linux-64/xz-5.2.6-h166bdaf_0.tar.bz2#2161070d867d1b1204ea749c8eec4ef0
 https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2#4cb3ad778ec2d5a7acbdf254eb1c42ae
-https://conda.anaconda.org/conda-forge/linux-64/gettext-0.19.8.1-h73d1719_1008.tar.bz2#af49250eca8e139378f8ff0ae9e57251
-https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h9772cbc_4.tar.bz2#dd3e1941dd06f64cb88647d2f7ff8aaa
 https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-16_linux64_openblas.tar.bz2#d9b7a8639171f6c6fa0a983edabcfe2b
-https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.0.9-h166bdaf_7.tar.bz2#37a460703214d0d1b421e2a47eb5e6d0
-https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.0.9-h166bdaf_7.tar.bz2#785a9296ea478eb78c47593c4da6550f
-https://conda.anaconda.org/conda-forge/linux-64/libcap-2.64-ha37c62d_0.tar.bz2#5896fbd58d0376df8556a4aba1ce4f71
+https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.0.9-h166bdaf_8.tar.bz2#4ae4d7795d33e02bd20f6b23d91caf82
+https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.0.9-h166bdaf_8.tar.bz2#04bac51ba35ea023dc48af73c1c88c25
+https://conda.anaconda.org/conda-forge/linux-64/libcap-2.66-ha37c62d_0.tar.bz2#2d7665abd0997f1a6d4b7596bc27b657
 https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2#4d331e44109e3f0e19b4cb8f9b82f3e1
 https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.10-h9b69904_4.tar.bz2#390026683aef81db27ff1b8570ca1336
-https://conda.anaconda.org/conda-forge/linux-64/libllvm14-14.0.6-he0ac6c6_0.tar.bz2#f5759f0c80708fbf9c4836c0cb46d0fe
+https://conda.anaconda.org/conda-forge/linux-64/libflac-1.4.2-h27087fc_0.tar.bz2#7daf72d8e2a8e848e11d63ed6d1026e0
 https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.47.0-hdcd2b5c_1.tar.bz2#6fe9e31c2b8d0b022626ccac13e6ca3c
-https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.37-h753d276_4.tar.bz2#6b611734b73d639c084ac4be2fcd996a
-https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.39.2-h753d276_1.tar.bz2#90136dc0a305db4e1df24945d431457b
+https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.38-h753d276_0.tar.bz2#575078de1d3a3114b3ce131bd1508d0c
+https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.39.4-h753d276_0.tar.bz2#978924c298fc2215f129e8171bbea688
 https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.10.0-haa6b8db_3.tar.bz2#89acee135f0809a18a1f4537390aa2dd
 https://conda.anaconda.org/conda-forge/linux-64/libvorbis-1.3.7-h9c3ff4c_0.tar.bz2#309dec04b70a3cc0f1e84a4013683bc0
 https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.13-h7f98852_1004.tar.bz2#b3653fdc58d03face9724f602218a904
-https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.9.14-h22db469_4.tar.bz2#aced7c1f4b4dbfea08e033c6ae97c53e
+https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.10.3-h7463322_0.tar.bz2#3b933ea47ef8f330c4c068af25fcd6a8
 https://conda.anaconda.org/conda-forge/linux-64/libzip-1.9.2-hc869a4a_1.tar.bz2#7a268cf1386d271e576e35ae82149ef2
-https://conda.anaconda.org/conda-forge/linux-64/mysql-common-8.0.30-haf5c9bc_0.tar.bz2#9d3e24b1157af09abe5a2589119c7b1d
-https://conda.anaconda.org/conda-forge/linux-64/portaudio-19.6.0-h57a0ea0_5.tar.bz2#5469312a373f481c05c380897fd7c923
+https://conda.anaconda.org/conda-forge/linux-64/mysql-common-8.0.31-haf5c9bc_0.tar.bz2#0249d755f8d26cb2ac796f9f01cfb823
+https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.40-hc3806b6_0.tar.bz2#69e2c796349cd9b273890bee0febfe1b
 https://conda.anaconda.org/conda-forge/linux-64/readline-8.1.2-h0f457ee_0.tar.bz2#db2ebbe2943aae81ed051a6a9af8e0fa
 https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.12-h27826a3_0.tar.bz2#5b8c42eb62e9fc961af70bdd6a26e168
 https://conda.anaconda.org/conda-forge/linux-64/udunits2-2.2.28-hc3e0081_0.tar.bz2#d4c341e0379c31e9e781d4f204726867
 https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.3-hd9c2040_1000.tar.bz2#9e856f78d5c80d5a78f61e72d1d473a3
-https://conda.anaconda.org/conda-forge/linux-64/zlib-1.2.12-h166bdaf_2.tar.bz2#4533821485cde83ab12ff3d8bda83768
+https://conda.anaconda.org/conda-forge/linux-64/zlib-1.2.13-h166bdaf_4.tar.bz2#4b11e365c0275b808be78b30f904e295
 https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.2-h6239696_4.tar.bz2#adcf0be7897e73e312bd24353b613f74
-https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.0.9-h166bdaf_7.tar.bz2#1699c1211d56a23c66047524cd76796e
+https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.0.9-h166bdaf_8.tar.bz2#e5613f2bc717e9945840ff474419b8e4
 https://conda.anaconda.org/conda-forge/linux-64/freetype-2.12.1-hca18f0e_0.tar.bz2#4e54cbfc47b8c74c2ecc1e7730d8edce
+https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h9772cbc_5.tar.bz2#ee08782aff2ff9b3291c967fa6bc7336
 https://conda.anaconda.org/conda-forge/linux-64/krb5-1.19.3-h3790be6_0.tar.bz2#7d862b05445123144bec92cb1acc8ef8
 https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-16_linux64_openblas.tar.bz2#20bae26d0a1db73f758fc3754cab4719
-https://conda.anaconda.org/conda-forge/linux-64/libclang13-14.0.6-default_h3a83d3e_0.tar.bz2#cdbd49e0ab5c5a6c522acb8271977d4c
-https://conda.anaconda.org/conda-forge/linux-64/libflac-1.3.4-h27087fc_0.tar.bz2#620e52e160fd09eb8772dedd46bb19ef
-https://conda.anaconda.org/conda-forge/linux-64/libglib-2.72.1-h2d90d5f_0.tar.bz2#ebeadbb5fbc44052eeb6f96a2136e3c2
+https://conda.anaconda.org/conda-forge/linux-64/libglib-2.74.1-h606061b_1.tar.bz2#ed5349aa96776e00b34eccecf4a948fe
 https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-16_linux64_openblas.tar.bz2#955d993f41f9354bf753d29864ea20ad
-https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.4.0-h0e0dad5_3.tar.bz2#5627d42c13a9b117ae1701c6e195624f
+https://conda.anaconda.org/conda-forge/linux-64/libllvm15-15.0.4-h63197d8_1.tar.bz2#d1b93417274d24b751a831c21169669a
+https://conda.anaconda.org/conda-forge/linux-64/libsndfile-1.1.0-h27087fc_0.tar.bz2#02fa0b56a57c8421d1195bf0c021e682
+https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.4.0-h55922b4_4.tar.bz2#901791f0ec7cddc8714e76e273013a91
 https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.0.3-he3ba5ed_0.tar.bz2#f9dbabc7e01c459ed7a1d1d64b206e9b
-https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-8.0.30-h28c427c_0.tar.bz2#77f98ec0b224fd5ca8e7043e167efb83
+https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-8.0.31-h28c427c_0.tar.bz2#455d44a05123f30f66af2ca2a9652b5f
 https://conda.anaconda.org/conda-forge/linux-64/python-3.10.6-h582c2e5_0_cpython.tar.bz2#6f009f92084e84884d1dff862b85eb00
-https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.39.2-h4ff8645_1.tar.bz2#2676ec698ce91567fca50654ac1b18ba
+https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.39.4-h4ff8645_0.tar.bz2#643c271de2dd23ecbd107284426cebc2
 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.0-h166bdaf_0.tar.bz2#384e7fcb3cd162ba3e4aed4b687df566
 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.0-h166bdaf_0.tar.bz2#637054603bb7594302e3bf83f0a99879
 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.9-h166bdaf_0.tar.bz2#732e22f1741bccea861f5668cf7342a7
 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.1-h166bdaf_0.tar.bz2#0a8e20a8aef954390b9481a527421a8c
 https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.7.2-h7f98852_0.tar.bz2#12a61e640b8894504326aadafccbb790
 https://conda.anaconda.org/conda-forge/noarch/alabaster-0.7.12-py_0.tar.bz2#2489a97287f90176ecdc3ca982b4b0a0
-https://conda.anaconda.org/conda-forge/linux-64/atk-1.0-2.36.0-h3371d22_4.tar.bz2#661e1ed5d92552785d9f8c781ce68685
+https://conda.anaconda.org/conda-forge/linux-64/atk-1.0-2.38.0-hd4edc92_1.tar.bz2#6c72ec3e660a51736913ef6ea68c454b
 https://conda.anaconda.org/conda-forge/noarch/attrs-22.1.0-pyh71513ae_1.tar.bz2#6d3ccbc56256204925bfa8378722792f
-https://conda.anaconda.org/conda-forge/linux-64/brotli-1.0.9-h166bdaf_7.tar.bz2#3889dec08a472eb0f423e5609c76bde1
-https://conda.anaconda.org/conda-forge/noarch/certifi-2022.6.15-pyhd8ed1ab_1.tar.bz2#97349c8d67627cbf8f48d7e7e1773ea5
+https://conda.anaconda.org/conda-forge/linux-64/brotli-1.0.9-h166bdaf_8.tar.bz2#2ff08978892a3e8b954397c461f18418
+https://conda.anaconda.org/conda-forge/noarch/certifi-2022.9.24-pyhd8ed1ab_0.tar.bz2#f66309b099374af91369e67e84af397d
 https://conda.anaconda.org/conda-forge/noarch/cfgv-3.3.1-pyhd8ed1ab_0.tar.bz2#ebb5f5f7dc4f1a3780ef7ea7738db08c
 https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-2.1.1-pyhd8ed1ab_0.tar.bz2#c1d5b294fbf9a795dec349a6f4d8be8e
-https://conda.anaconda.org/conda-forge/noarch/cloudpickle-2.1.0-pyhd8ed1ab_0.tar.bz2#f7551a8a008dfad2b7ac9662dd124614
-https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.5-pyhd8ed1ab_0.tar.bz2#c267da48ce208905d7d976d49dfd9433
+https://conda.anaconda.org/conda-forge/noarch/click-8.1.3-unix_pyhd8ed1ab_2.tar.bz2#20e4087407c7cb04a40817114b333dbf
+https://conda.anaconda.org/conda-forge/noarch/cloudpickle-2.2.0-pyhd8ed1ab_0.tar.bz2#a6cf47b09786423200d7982d1faa19eb
+https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_0.tar.bz2#3faab06a954c2a04039983f2c4a50d99
 https://conda.anaconda.org/conda-forge/noarch/cycler-0.11.0-pyhd8ed1ab_0.tar.bz2#a50559fad0affdbb33729a68669ca1cb
 https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h5008d03_3.tar.bz2#ecfff944ba3960ecb334b9a2663d708d
-https://conda.anaconda.org/conda-forge/noarch/distlib-0.3.5-pyhd8ed1ab_0.tar.bz2#f15c3912378a07726093cc94d1e13251
+https://conda.anaconda.org/conda-forge/noarch/distlib-0.3.6-pyhd8ed1ab_0.tar.bz2#b65b4d50dbd2d50fa0aeac367ec9eed7
+https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.0.4-pyhd8ed1ab_0.tar.bz2#e0734d1f12de77f9daca98bda3428733
 https://conda.anaconda.org/conda-forge/noarch/execnet-1.9.0-pyhd8ed1ab_0.tar.bz2#0e521f7a5e60d508b121d38b04874fb2
 https://conda.anaconda.org/conda-forge/noarch/filelock-3.8.0-pyhd8ed1ab_0.tar.bz2#10f0218dbd493ab2e5dc6759ddea4526
-https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.14.0-h8e229c2_0.tar.bz2#f314f79031fec74adc9bff50fbaffd89
-https://conda.anaconda.org/conda-forge/noarch/fsspec-2022.7.1-pyhd8ed1ab_0.tar.bz2#984db277dfb9ea04a584aea39c6a34e4
-https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.42.8-hff1cb4f_0.tar.bz2#908fc30f89e27817d835b45f865536d7
-https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.72.1-h6239696_0.tar.bz2#a3a99cc33279091262bbc4f5ee7c4571
+https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.14.1-hc2a2eb6_0.tar.bz2#78415f0180a8d9c5bcc47889e00d5fb1
+https://conda.anaconda.org/conda-forge/noarch/fsspec-2022.11.0-pyhd8ed1ab_0.tar.bz2#eb919f2119a6db5d0192f9e9c3711572
+https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.42.8-hff1cb4f_1.tar.bz2#a61c6312192e7c9de71548a6706a21e6
+https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.74.1-h6239696_1.tar.bz2#5f442e6bc9d89ba236eb25a25c5c2815
 https://conda.anaconda.org/conda-forge/linux-64/gts-0.7.6-h64030ff_2.tar.bz2#112eb9b5b93f0c02e59aea4fd1967363
-https://conda.anaconda.org/conda-forge/noarch/idna-3.3-pyhd8ed1ab_0.tar.bz2#40b50b8b030f5f2f22085c062ed013dd
+https://conda.anaconda.org/conda-forge/noarch/idna-3.4-pyhd8ed1ab_0.tar.bz2#34272b248891bddccc64479f9a7fffed
 https://conda.anaconda.org/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2#7de5386c8fea29e76b303f37dde4c352
 https://conda.anaconda.org/conda-forge/noarch/iniconfig-1.1.1-pyh9f0ad1d_0.tar.bz2#39161f81cc5e5ca45b8226fbb06c6905
 https://conda.anaconda.org/conda-forge/noarch/iris-sample-data-2.4.0-pyhd8ed1ab_0.tar.bz2#18ee9c07cf945a33f92caf1ee3d23ad9
-https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.12-hddcbb42_0.tar.bz2#797117394a4aa588de6d741b06fad80f
-https://conda.anaconda.org/conda-forge/linux-64/libclang-14.0.6-default_h2e3cab8_0.tar.bz2#eb70548da697e50cefa7ba939d57d001
+https://conda.anaconda.org/conda-forge/linux-64/jack-1.9.21-he978b8e_1.tar.bz2#5cef21ebd70a90a0d28127543a8d3739
+https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.14-h6ed2654_0.tar.bz2#dcc588839de1445d90995a0a2c4f3a39
+https://conda.anaconda.org/conda-forge/linux-64/libclang13-15.0.4-default_h3a83d3e_0.tar.bz2#d60d2d91b5991e364fd95cba5ec8ab96
 https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h3e49a29_2.tar.bz2#3b88f1d0fe2580594d58d7e44d664617
-https://conda.anaconda.org/conda-forge/linux-64/libcurl-7.83.1-h7bff187_0.tar.bz2#d0c278476dba3b29ee13203784672ab1
-https://conda.anaconda.org/conda-forge/linux-64/libpq-14.5-hd77ab85_0.tar.bz2#d3126b425a04ed2360da1e651cef1b2d
-https://conda.anaconda.org/conda-forge/linux-64/libsndfile-1.0.31-h9c3ff4c_1.tar.bz2#fc4b6d93da04731db7601f2a1b1dc96a
+https://conda.anaconda.org/conda-forge/linux-64/libcurl-7.86.0-h7bff187_1.tar.bz2#5e5f33d81f31598d87f9b849f73c30e3
+https://conda.anaconda.org/conda-forge/linux-64/libpq-14.5-hd77ab85_1.tar.bz2#f5c8135a70758d928a8126998a6558d8
 https://conda.anaconda.org/conda-forge/linux-64/libwebp-1.2.4-h522a892_0.tar.bz2#802e43f480122a85ae6a34c1909f8f98
 https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2#91e27ef3d05cc772ce627e51cff111c4
 https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2#2ba8498c1018c1e9c61eb99b973dfe19
 https://conda.anaconda.org/conda-forge/linux-64/nss-3.78-h2350873_0.tar.bz2#ab3df39f96742e6f1a9878b09274c1dc
 https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.0-h7d73246_1.tar.bz2#a11b4df9271a8d7917686725aa04c8f2
 https://conda.anaconda.org/conda-forge/noarch/platformdirs-2.5.2-pyhd8ed1ab_1.tar.bz2#2fb3f88922e7aec26ba652fcdfe13950
+https://conda.anaconda.org/conda-forge/noarch/pluggy-1.0.0-pyhd8ed1ab_5.tar.bz2#7d301a0d25f424d96175f810935f0da9
 https://conda.anaconda.org/conda-forge/noarch/ply-3.11-py_1.tar.bz2#7205635cd71531943440fbfe3b6b5727
-https://conda.anaconda.org/conda-forge/noarch/py-1.11.0-pyh6c4a22f_0.tar.bz2#b4613d7e7a493916d867842a6a148054
 https://conda.anaconda.org/conda-forge/noarch/pycparser-2.21-pyhd8ed1ab_0.tar.bz2#076becd9e05608f8dc72757d5f3a91ff
 https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.0.9-pyhd8ed1ab_0.tar.bz2#e8fbc1b54b25f4b08281467bc13b70cc
 https://conda.anaconda.org/conda-forge/noarch/pyshp-2.3.1-pyhd8ed1ab_0.tar.bz2#92a889dc236a5197612bc85bee6d7174
+https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2#2a7de29fb590ca14b5243c4c812c8025
 https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.10-2_cp310.tar.bz2#9e7160cd0d865e98f6803f1fe15c8b61
-https://conda.anaconda.org/conda-forge/noarch/pytz-2022.2.1-pyhd8ed1ab_0.tar.bz2#974bca71d00364630f63f31fa7e059cb
-https://conda.anaconda.org/conda-forge/noarch/setuptools-65.3.0-pyhd8ed1ab_1.tar.bz2#a64c8af7be7a6348c1d9e530f88fa4da
+https://conda.anaconda.org/conda-forge/noarch/pytz-2022.6-pyhd8ed1ab_0.tar.bz2#b1f26ad83328e486910ef7f6e81dc061
+https://conda.anaconda.org/conda-forge/noarch/setuptools-65.5.1-pyhd8ed1ab_0.tar.bz2#cfb8dc4d9d285ca5fb1177b9dd450e33
 https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2#e5f25f8dbc060e9a8d912e432202afc2
 https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-2.2.0-pyhd8ed1ab_0.tar.bz2#4d22a9315e78c6827f806065957d566e
 https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.3.2.post1-pyhd8ed1ab_0.tar.bz2#146f4541d643d48fc8a75cacf69f03ae
@@ -164,93 +168,91 @@ https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-serializinghtml-1.1.
 https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_0.tar.bz2#f832c45a477c78bebd107098db465095
 https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2#5844808ffab9ebdb694585b50ba02a96
 https://conda.anaconda.org/conda-forge/noarch/toolz-0.12.0-pyhd8ed1ab_0.tar.bz2#92facfec94bc02d6ccf42e7173831a36
-https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.3.0-pyha770c72_0.tar.bz2#a9d85960bc62d53cc4ea0d1d27f73c98
-https://conda.anaconda.org/conda-forge/noarch/wheel-0.37.1-pyhd8ed1ab_0.tar.bz2#1ca02aaf78d9c70d9a81a3bed5752022
+https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.4.0-pyha770c72_0.tar.bz2#2d93b130d148d7fc77e583677792fc6a
+https://conda.anaconda.org/conda-forge/noarch/wheel-0.38.4-pyhd8ed1ab_0.tar.bz2#c829cfb8cb826acb9de0ac1a2df0a940
 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-image-0.4.0-h166bdaf_0.tar.bz2#c9b568bd804cb2903c6be6f5f68182e4
 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.4-h7f98852_1.tar.bz2#536cc5db4d0a3ba0630541aec064b5e4
 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.10-h7f98852_1003.tar.bz2#f59c1242cc1dd93e72c2ee2b360979eb
-https://conda.anaconda.org/conda-forge/noarch/zipp-3.8.1-pyhd8ed1ab_0.tar.bz2#a3508a0c850745b875de88aea4c40cc5
+https://conda.anaconda.org/conda-forge/noarch/zipp-3.10.0-pyhd8ed1ab_0.tar.bz2#cd4eb48ebde7de61f92252979aab515c
 https://conda.anaconda.org/conda-forge/linux-64/antlr-python-runtime-4.7.2-py310hff52083_1003.tar.bz2#8324f8fff866055d4b32eb25e091fe31
-https://conda.anaconda.org/conda-forge/noarch/babel-2.10.3-pyhd8ed1ab_0.tar.bz2#72f1c6d03109d7a70087bc1d029a8eda
+https://conda.anaconda.org/conda-forge/noarch/babel-2.11.0-pyhd8ed1ab_0.tar.bz2#2ea70fde8d581ba9425a761609eed6ba
 https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.11.1-pyha770c72_0.tar.bz2#eeec8814bd97b2681f708bb127478d7d
-https://conda.anaconda.org/conda-forge/linux-64/cairo-1.16.0-ha61ee94_1012.tar.bz2#9604a7c93dd37bcb6d6cc8d6b64223a4
-https://conda.anaconda.org/conda-forge/linux-64/cffi-1.15.1-py310h255011f_0.tar.bz2#3e4b55b02998782f8ca9ceaaa4f5ada9
-https://conda.anaconda.org/conda-forge/linux-64/curl-7.83.1-h7bff187_0.tar.bz2#ba33b9995f5e691e4f439422d6efafc7
-https://conda.anaconda.org/conda-forge/linux-64/docutils-0.17.1-py310hff52083_2.tar.bz2#1cdb74e021e4e0b703a8c2f7cc57d798
-https://conda.anaconda.org/conda-forge/linux-64/glib-2.72.1-h6239696_0.tar.bz2#1698b7684d3c6a4d1de2ab946f5b0fb5
+https://conda.anaconda.org/conda-forge/linux-64/cairo-1.16.0-ha61ee94_1014.tar.bz2#d1a88f3ed5b52e1024b80d4bcd26a7a0
+https://conda.anaconda.org/conda-forge/linux-64/cffi-1.15.1-py310h255011f_2.tar.bz2#6bb8063dd08f9724c18744b0e040cfe2
+https://conda.anaconda.org/conda-forge/linux-64/curl-7.86.0-h7bff187_1.tar.bz2#bc9567c50833f4b0d36b25caca7b34f8
+https://conda.anaconda.org/conda-forge/linux-64/docutils-0.17.1-py310hff52083_3.tar.bz2#785160da087cf1d70e989afbb761f01c
+https://conda.anaconda.org/conda-forge/linux-64/glib-2.74.1-h6239696_1.tar.bz2#f3220a9e9d3abcbfca43419a219df7e4
 https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.12.2-mpi_mpich_h08b82f9_0.tar.bz2#de601caacbaa828d845f758e07e3b85e
-https://conda.anaconda.org/conda-forge/linux-64/importlib-metadata-4.11.4-py310hff52083_0.tar.bz2#8ea386e64531f1ecf4a5765181579e7e
-https://conda.anaconda.org/conda-forge/linux-64/jack-1.9.18-h8c3723f_1002.tar.bz2#7b3f287fcb7683f67b3d953b79f412ea
-https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.4-py310hbf28c38_0.tar.bz2#8dc3e2dce8fa122f8df4f3739d1f771b
+https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-5.0.0-pyha770c72_1.tar.bz2#ec069c4db6a0ad84107bac5da62819d2
+https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.4-py310hbf28c38_1.tar.bz2#ad5647e517ba68e2868ef2e6e6ff7723
+https://conda.anaconda.org/conda-forge/linux-64/libclang-15.0.4-default_h2e3cab8_0.tar.bz2#248a013d3e7248c5c5f4ff69ffc307e8
 https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-h18fbbfe_3.tar.bz2#ea9758cf553476ddf75c789fdd239dc5
-https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.1-py310h5764c6d_1.tar.bz2#ec5a727504409ad1380fc2a84f83d002
-https://conda.anaconda.org/conda-forge/linux-64/mpi4py-3.1.3-py310h37cc914_2.tar.bz2#0211369f253eedce9e570b4f0e5a981a
+https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.1-py310h5764c6d_2.tar.bz2#2d7028ea2a77f909931e1a173d952261
+https://conda.anaconda.org/conda-forge/linux-64/mpi4py-3.1.4-py310h37cc914_0.tar.bz2#98d598d9178d7f3091212c61c0be693c
 https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.7.0-pyhd8ed1ab_0.tar.bz2#fbe1182f650c04513046d6894046cd6c
-https://conda.anaconda.org/conda-forge/linux-64/numpy-1.23.2-py310h53a5b5f_0.tar.bz2#8b3cfad14508018915e88612f5a963cd
+https://conda.anaconda.org/conda-forge/linux-64/numpy-1.23.4-py310h53a5b5f_1.tar.bz2#0b7d4c8253f7191030adf34e2768c412
 https://conda.anaconda.org/conda-forge/noarch/packaging-21.3-pyhd8ed1ab_0.tar.bz2#71f1ab2de48613876becddd496371c85
 https://conda.anaconda.org/conda-forge/noarch/partd-1.3.0-pyhd8ed1ab_0.tar.bz2#af8c82d121e63082926062d61d9abb54
-https://conda.anaconda.org/conda-forge/linux-64/pillow-9.2.0-py310hbd86126_2.tar.bz2#443272de4234f6df4a78f50105edc741
-https://conda.anaconda.org/conda-forge/noarch/pip-22.2.2-pyhd8ed1ab_0.tar.bz2#0b43abe4d3ee93e82742d37def53a836
-https://conda.anaconda.org/conda-forge/linux-64/pluggy-1.0.0-py310hff52083_3.tar.bz2#97f9a22577338f91a94dfac5c1a65a50
+https://conda.anaconda.org/conda-forge/linux-64/pillow-9.2.0-py310h454ad03_3.tar.bz2#eb354ff791f505b1d6f13f776359d88e
+https://conda.anaconda.org/conda-forge/noarch/pip-22.3.1-pyhd8ed1ab_0.tar.bz2#da66f2851b9836d3a7c5190082a45f7d
 https://conda.anaconda.org/conda-forge/noarch/pockets-0.9.1-py_0.tar.bz2#1b52f0c42e8077e5a33e00fe72269364
-https://conda.anaconda.org/conda-forge/linux-64/proj-9.0.1-h93bde94_1.tar.bz2#8259528ea471b0963a91ce174f002e55
-https://conda.anaconda.org/conda-forge/linux-64/psutil-5.9.1-py310h5764c6d_0.tar.bz2#eb3be71bc11a51ff49b6a0af9968f0ed
+https://conda.anaconda.org/conda-forge/linux-64/proj-9.1.0-h93bde94_0.tar.bz2#255c7204dda39747c3ba380d28b026d7
+https://conda.anaconda.org/conda-forge/linux-64/psutil-5.9.4-py310h5764c6d_0.tar.bz2#c3c55664e9becc48e6a652e2b641961f
+https://conda.anaconda.org/conda-forge/linux-64/pulseaudio-14.0-h0d2025b_11.tar.bz2#316026c5f80d8b5b9666e87b8614ac6c
 https://conda.anaconda.org/conda-forge/noarch/pygments-2.13.0-pyhd8ed1ab_0.tar.bz2#9f478e8eedd301008b5f395bad0caaed
-https://conda.anaconda.org/conda-forge/linux-64/pysocks-1.7.1-py310hff52083_5.tar.bz2#378f2260e871f3ea46c6fa58d9f05277
 https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.8.2-pyhd8ed1ab_0.tar.bz2#dd999d1cc9f79e67dbb855c8924c7984
-https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.0.0-py310h5764c6d_1.tar.bz2#b6f54b7c4177a745d5e6e4319282253a
-https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0-py310h5764c6d_4.tar.bz2#505dcf6be997e732d7a33831950dc3cf
-https://conda.anaconda.org/conda-forge/linux-64/tornado-6.2-py310h5764c6d_0.tar.bz2#c42dcb37acd84b3ca197f03f57ef927d
-https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.3.0-hd8ed1ab_0.tar.bz2#f3e98e944832fb271a0dbda7b7771dc6
-https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-14.0.0-py310h5764c6d_1.tar.bz2#791689ce9e578e2e83b635974af61743
-https://conda.anaconda.org/conda-forge/linux-64/virtualenv-20.16.3-py310hff52083_1.tar.bz2#a91c9f0499e0f0f5912098c3462014b9
-https://conda.anaconda.org/conda-forge/linux-64/brotlipy-0.7.0-py310h5764c6d_1004.tar.bz2#6499bb11b7feffb63b26847fc9181319
-https://conda.anaconda.org/conda-forge/linux-64/cftime-1.6.1-py310hde88566_0.tar.bz2#1f84cf065287d73aa0233d432d3a1ba9
-https://conda.anaconda.org/conda-forge/linux-64/cryptography-37.0.4-py310h597c629_0.tar.bz2#f285746449d16d92884f4ce0cfe26679
-https://conda.anaconda.org/conda-forge/noarch/dask-core-2022.8.1-pyhd8ed1ab_0.tar.bz2#df5026dbf551bb992cdf247b08e11078
-https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.37.1-py310h5764c6d_0.tar.bz2#3dda361cb1fa5da73a75c4089d2ed338
-https://conda.anaconda.org/conda-forge/linux-64/gstreamer-1.20.3-hd4edc92_0.tar.bz2#94cb81ffdce328f80c87ac9b01244632
-https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-5.1.0-hf9f4e7c_0.tar.bz2#7c1f73a8f7864a202b126d82e88ddffc
+https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.0.0-py310h5764c6d_2.tar.bz2#cce72b32ccc346ed166fc85071854a86
+https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0-py310h5764c6d_5.tar.bz2#9e68d2ff6d98737c855b65f48dd3c597
+https://conda.anaconda.org/conda-forge/linux-64/tornado-6.2-py310h5764c6d_1.tar.bz2#be4a201ac582c11d89ed7d15b3157cc3
+https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.4.0-hd8ed1ab_0.tar.bz2#be969210b61b897775a0de63cd9e9026
+https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-15.0.0-py310h5764c6d_0.tar.bz2#e972c5a1f472561cf4a91962cb01f4b4
+https://conda.anaconda.org/conda-forge/linux-64/virtualenv-20.16.7-py310hff52083_0.tar.bz2#02600c102a32274e20fc0604ef35af3c
+https://conda.anaconda.org/conda-forge/linux-64/brotlipy-0.7.0-py310h5764c6d_1005.tar.bz2#87669c3468dff637bbd0363bc0f895cf
+https://conda.anaconda.org/conda-forge/linux-64/cftime-1.6.2-py310hde88566_1.tar.bz2#94ce7a76b0c912279f6958e0b6b21d2b
+https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.0.6-py310hbf28c38_0.tar.bz2#c5b1699e390d30b680dd93a2b251062b
+https://conda.anaconda.org/conda-forge/linux-64/cryptography-38.0.3-py310h597c629_0.tar.bz2#aa5aad596f9d5ef091364c4dad789094
+https://conda.anaconda.org/conda-forge/noarch/dask-core-2022.11.0-pyhd8ed1ab_0.tar.bz2#1ab924fb3c44ed49796cc85eb1315cdc
+https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.38.0-py310h5764c6d_1.tar.bz2#12ebe92a8a578bc903bd844744f4d040
+https://conda.anaconda.org/conda-forge/linux-64/gstreamer-1.21.1-hd4edc92_1.tar.bz2#e604f83df3497fcdb6991ae58b73238f
+https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-5.3.0-h418a68e_0.tar.bz2#888056bd4b12e110b10d4d1f29161c5e
 https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.2-pyhd8ed1ab_1.tar.bz2#c8490ed5c70966d232fdd389d0dbed37
-https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.8.1-mpi_mpich_h06c54e2_4.tar.bz2#491803a7356c6a668a84d71f491c4014
-https://conda.anaconda.org/conda-forge/linux-64/mo_pack-0.2.0-py310hde88566_1007.tar.bz2#c2ec7c118184ddfd855fc3698d1c8e63
-https://conda.anaconda.org/conda-forge/linux-64/pandas-1.4.3-py310h769672d_0.tar.bz2#e48c810453df0f03bb8fcdff5e1d9e9d
-https://conda.anaconda.org/conda-forge/linux-64/pulseaudio-14.0-h7f54b18_8.tar.bz2#f9dbcfbb942ec9a3c0249cb71da5c7d1
-https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.3.1-py310hf94497c_1.tar.bz2#aaa559c22c09139a504796bd453fd535
-https://conda.anaconda.org/conda-forge/linux-64/pytest-7.1.2-py310hff52083_0.tar.bz2#5d44c6ab93d445b6c433914753390e86
-https://conda.anaconda.org/conda-forge/linux-64/python-stratify-0.2.post0-py310hde88566_2.tar.bz2#a282f30e2e1efa1f210817597e144762
-https://conda.anaconda.org/conda-forge/linux-64/pywavelets-1.3.0-py310hde88566_1.tar.bz2#cbfce984f85c64401e3d4fedf4bc4247
-https://conda.anaconda.org/conda-forge/linux-64/scipy-1.9.0-py310hdfbd76f_0.tar.bz2#e5d21b0cb4161a40221786f2f05b3903
-https://conda.anaconda.org/conda-forge/noarch/setuptools-scm-7.0.5-pyhd8ed1ab_0.tar.bz2#743074b7a216807886f7e8f6d497cceb
-https://conda.anaconda.org/conda-forge/linux-64/shapely-1.8.4-py310h5e49deb_0.tar.bz2#2f2c225d04e99ff99d6d3a86692ce968
-https://conda.anaconda.org/conda-forge/linux-64/sip-6.6.2-py310hd8f1fbe_0.tar.bz2#3d311837eadeb8137fca02bdb5a9751f
+https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.8.1-mpi_mpich_hcd871d9_6.tar.bz2#6cdc429ed22edb566ac4308f3da6916d
+https://conda.anaconda.org/conda-forge/linux-64/mo_pack-0.2.0-py310hde88566_1008.tar.bz2#f9dd8a7a2fcc23eb2cd95cd817c949e7
+https://conda.anaconda.org/conda-forge/linux-64/pandas-1.5.1-py310h769672d_1.tar.bz2#4dd589c55d445e52ef0a7102158254df
+https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.4.0-py310hb1338dc_2.tar.bz2#e1648c222911ad7559d62831e4bc447c
+https://conda.anaconda.org/conda-forge/noarch/pytest-7.2.0-pyhd8ed1ab_2.tar.bz2#ac82c7aebc282e6ac0450fca012ca78c
+https://conda.anaconda.org/conda-forge/linux-64/python-stratify-0.2.post0-py310hde88566_3.tar.bz2#0b686f306a76fba9a61e7019f854321f
+https://conda.anaconda.org/conda-forge/linux-64/pywavelets-1.3.0-py310hde88566_2.tar.bz2#61e2f2f7befaf45f47d1da449a9a0aca
+https://conda.anaconda.org/conda-forge/linux-64/scipy-1.9.3-py310hdfbd76f_2.tar.bz2#0582a434d03f6b06d5defbb142c96f4f
+https://conda.anaconda.org/conda-forge/noarch/setuptools-scm-7.0.5-pyhd8ed1ab_1.tar.bz2#07037fe2931871ed69b2b3d2acd5fdc6
+https://conda.anaconda.org/conda-forge/linux-64/shapely-1.8.5-py310h5b266fc_2.tar.bz2#c4a3707d6a630facb6cf7ed8e0d37326
+https://conda.anaconda.org/conda-forge/linux-64/sip-6.7.4-py310hd8f1fbe_0.tar.bz2#5c54faf5327d5d7ef993dd0f6f25e123
 https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-napoleon-0.7-py_0.tar.bz2#0bc25ff6f2e34af63ded59692df5f749
-https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.0.1-py310hbf28c38_2.tar.bz2#46784478afa27e33b9d5f017c4deb49d
-https://conda.anaconda.org/conda-forge/linux-64/cf-units-3.1.1-py310hde88566_0.tar.bz2#49790458218da5f86068f32e3938d334
-https://conda.anaconda.org/conda-forge/linux-64/gst-plugins-base-1.20.3-hf6a322e_0.tar.bz2#6ea2ce6265c3207876ef2369b7479f08
-https://conda.anaconda.org/conda-forge/noarch/identify-2.5.3-pyhd8ed1ab_0.tar.bz2#682f05a8e4b047ce4bdcec9d69c12551
-https://conda.anaconda.org/conda-forge/noarch/imagehash-4.2.1-pyhd8ed1ab_0.tar.bz2#01cc8698b6e1a124dc4f585516c27643
-https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.5.3-py310h8d5ebf3_2.tar.bz2#760bc53cc184c9d6eeca9a38099e5fa8
-https://conda.anaconda.org/conda-forge/linux-64/netcdf-fortran-4.6.0-mpi_mpich_hd09bd1e_0.tar.bz2#247c70ce54beeb3e60def44061576821
-https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.6.0-nompi_py310h9fd08d4_101.tar.bz2#0c7d82a8e4a32c1231036eb8530f31b2
-https://conda.anaconda.org/conda-forge/linux-64/pango-1.50.9-hc4f8a73_0.tar.bz2#b8e090dce29a036357552a009c770187
-https://conda.anaconda.org/conda-forge/noarch/pyopenssl-22.0.0-pyhd8ed1ab_0.tar.bz2#1d7e241dfaf5475e893d4b824bb71b44
-https://conda.anaconda.org/conda-forge/linux-64/pyqt5-sip-12.11.0-py310hd8f1fbe_0.tar.bz2#9e3db99607d6f9285b7348c2af28a095
-https://conda.anaconda.org/conda-forge/noarch/pytest-forked-1.4.0-pyhd8ed1ab_0.tar.bz2#95286e05a617de9ebfe3246cecbfb72f
-https://conda.anaconda.org/conda-forge/linux-64/cartopy-0.20.3-py310he7eef42_2.tar.bz2#9212ffec588998a9b3ac573bba2e597e
+https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.0.1-py310hbf28c38_3.tar.bz2#703ff1ac7d1b27fb5944b8052b5d1edb
+https://conda.anaconda.org/conda-forge/linux-64/cf-units-3.1.1-py310hde88566_2.tar.bz2#7433944046deda7775c5b1f7e0b6fe18
+https://conda.anaconda.org/conda-forge/linux-64/gst-plugins-base-1.21.1-h3e40eee_1.tar.bz2#c03f4fca88373cf6c26d932c26e4ee20
+https://conda.anaconda.org/conda-forge/noarch/identify-2.5.8-pyhd8ed1ab_0.tar.bz2#8001c46448f385fa43bc4221893704d2
+https://conda.anaconda.org/conda-forge/noarch/imagehash-4.3.1-pyhd8ed1ab_0.tar.bz2#132ad832787a2156be1f1b309835001a
+https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.6.2-py310h8d5ebf3_0.tar.bz2#da51ddb20c0f99d672eb756c3abf27e7
+https://conda.anaconda.org/conda-forge/linux-64/netcdf-fortran-4.6.0-mpi_mpich_hd09bd1e_1.tar.bz2#0b69750bb937cab0db14f6bcef6fd787
+https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.6.0-nompi_py310h55e1e36_102.tar.bz2#588d5bd8f16287b766c509ef173b892d
+https://conda.anaconda.org/conda-forge/linux-64/pango-1.50.11-h382ae3d_0.tar.bz2#509e3f89508398070d3bf7769d9e8b03
+https://conda.anaconda.org/conda-forge/noarch/pyopenssl-22.1.0-pyhd8ed1ab_0.tar.bz2#fbfa0a180d48c800f922a10a114a8632
+https://conda.anaconda.org/conda-forge/linux-64/pyqt5-sip-12.11.0-py310hd8f1fbe_2.tar.bz2#0d815f1b2258d3d4c17cc80fd01e0f36
+https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.0.2-pyhd8ed1ab_0.tar.bz2#18bdfe034d1187a34d860ed8e6fec788
+https://conda.anaconda.org/conda-forge/linux-64/cartopy-0.21.0-py310h83f2385_3.tar.bz2#4ec35f7eebe4221c1c00fdd6540db4dc
 https://conda.anaconda.org/conda-forge/linux-64/esmf-8.2.0-mpi_mpich_h5a1934d_102.tar.bz2#bb8bdfa5e3e9e3f6ec861f05cd2ad441
 https://conda.anaconda.org/conda-forge/linux-64/gtk2-2.24.33-h90689f9_2.tar.bz2#957a0255ab58aaf394a91725d73ab422
 https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.54.4-h7abd40a_0.tar.bz2#921e53675ed5ea352f022b79abab076a
 https://conda.anaconda.org/conda-forge/noarch/nc-time-axis-1.4.1-pyhd8ed1ab_0.tar.bz2#281b58948bf60a2582de9e548bcc5369
-https://conda.anaconda.org/conda-forge/linux-64/pre-commit-2.20.0-py310hff52083_0.tar.bz2#5af49a9342d50006017b897698921f43
-https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-2.5.0-pyhd8ed1ab_0.tar.bz2#1fdd1f3baccf0deb647385c677a1a48e
-https://conda.anaconda.org/conda-forge/linux-64/qt-main-5.15.4-ha5833f6_2.tar.bz2#dd3aa6715b9e9efaf842febf18ce4261
+https://conda.anaconda.org/conda-forge/linux-64/pre-commit-2.20.0-py310hff52083_1.tar.bz2#8c151d720f9fe3b9962efe71fc10b07b
+https://conda.anaconda.org/conda-forge/linux-64/qt-main-5.15.6-hd477bba_1.tar.bz2#738d009d60cd682df336b6d52c766190
 https://conda.anaconda.org/conda-forge/noarch/urllib3-1.26.11-pyhd8ed1ab_0.tar.bz2#0738978569b10669bdef41c671252dd1
 https://conda.anaconda.org/conda-forge/linux-64/esmpy-8.2.0-mpi_mpich_py310hd9c82d4_101.tar.bz2#0333d51ee594be40f50b157ac6f27b5a
-https://conda.anaconda.org/conda-forge/linux-64/graphviz-5.0.1-h5abf519_0.tar.bz2#03f22ca50fcff4bbee39da0943ab8475
-https://conda.anaconda.org/conda-forge/linux-64/pyqt-5.15.7-py310h29803b5_0.tar.bz2#b5fb5328cae86d0b1591fc4894e68238
-https://conda.anaconda.org/conda-forge/noarch/requests-2.28.1-pyhd8ed1ab_0.tar.bz2#70d6e72856de9551f83ae0f2de689a7a
-https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.5.3-py310hff52083_2.tar.bz2#46fb1538bf92de6d807feb81b462aa0f
+https://conda.anaconda.org/conda-forge/linux-64/graphviz-6.0.1-h5abf519_0.tar.bz2#123c55da3e9ea8664f73c70e13ef08c2
+https://conda.anaconda.org/conda-forge/linux-64/pyqt-5.15.7-py310h29803b5_2.tar.bz2#1e2c49215b17e6cf06edf100c9869ebe
+https://conda.anaconda.org/conda-forge/noarch/requests-2.28.1-pyhd8ed1ab_1.tar.bz2#089382ee0e2dc2eae33a04cc3c2bddb0
+https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.6.2-py310hff52083_0.tar.bz2#aa78d12708912cd34135e6694a046ba0
 https://conda.anaconda.org/conda-forge/noarch/sphinx-4.5.0-pyh6c4a22f_0.tar.bz2#46b38d88c4270ff9ba78a89c83c66345
 https://conda.anaconda.org/conda-forge/noarch/pydata-sphinx-theme-0.8.1-pyhd8ed1ab_0.tar.bz2#7d8390ec71225ea9841b276552fdffba
 https://conda.anaconda.org/conda-forge/noarch/sphinx-copybutton-0.5.0-pyhd8ed1ab_0.tar.bz2#4c969cdd5191306c269490f7ff236d9c
diff --git a/requirements/ci/nox.lock/py38-linux-64.lock b/requirements/ci/nox.lock/py38-linux-64.lock
index af62d7e5b1..1cb20c2cce 100644
--- a/requirements/ci/nox.lock/py38-linux-64.lock
+++ b/requirements/ci/nox.lock/py38-linux-64.lock
@@ -1,58 +1,60 @@
 # Generated by conda-lock.
 # platform: linux-64
-# input_hash: 40cbe959a02aa488bdf70e6b6968135b05b560f9b9ed8768ccf1780314c0e219
+# input_hash: 0adff1c1aa6a49a3d784fd9be06fb43f1a450cf0f1871db2d38d071ffa6efbcc
 @EXPLICIT
 https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81
-https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2022.6.15-ha878542_0.tar.bz2#c320890f77fd1d617fa876e0982002c2
+https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2022.9.24-ha878542_0.tar.bz2#41e4e87062433e283696cf384f952ef6
 https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2#0c96522c6bdaed4b1566d11387caaf45
 https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6
 https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb
 https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-hab24e00_0.tar.bz2#19410c3df09dfb12d1206132a1d357c5
-https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.36.1-hea4e1c9_2.tar.bz2#bd4f2e711b39af170e7ff15163fe87ee
-https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-12.1.0-hdcd56e2_16.tar.bz2#b02605b875559ff99f04351fd5040760
-https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-12.1.0-ha89aaad_16.tar.bz2#6f5ba041a41eb102a1027d9e68731be7
+https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.39-hc81fddc_0.tar.bz2#c2719e2faa7bd7076d3a4b52271e5622
+https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-12.2.0-h337968e_19.tar.bz2#164b4b1acaedc47ee7e658ae6b308ca3
+https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-12.2.0-h46fd767_19.tar.bz2#1030b1f38c129f2634eae026f704fe60
 https://conda.anaconda.org/conda-forge/linux-64/mpi-1.0-mpich.tar.bz2#c1fcff3417b5a22bbc4cf6e8c23648cf
 https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2#f766549260d6815b0c52253f1fb1bb29
-https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-12.1.0-h69a702a_16.tar.bz2#6bf15e29a20f614b18ae89368260d0a2
-https://conda.anaconda.org/conda-forge/linux-64/libgomp-12.1.0-h8d9b700_16.tar.bz2#f013cf7749536ce43d82afbffdf499ab
+https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-12.2.0-h69a702a_19.tar.bz2#cd7a806282c16e1f2d39a7e80d3a3e0d
+https://conda.anaconda.org/conda-forge/linux-64/libgomp-12.2.0-h65d4601_19.tar.bz2#cedcee7c064c01c403f962c9e8d3c373
 https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2#73aaf86a425cc6e73fcf236a5a46396d
 https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab
-https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-12.1.0-h8d9b700_16.tar.bz2#4f05bc9844f7c101e6e147dab3c88d5c
-https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.6.1-h7f98852_0.tar.bz2#0347ce6a34f8b55b544b141432c6d4c7
+https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-12.2.0-h65d4601_19.tar.bz2#e4c94f80aef025c17ab0828cd85ef535
+https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.8-h166bdaf_0.tar.bz2#be733e69048951df1e4b4b7bb8c7666f
 https://conda.anaconda.org/conda-forge/linux-64/attr-2.5.1-h166bdaf_1.tar.bz2#d9c69a24ad678ffce24c6543a0176b00
 https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h7f98852_4.tar.bz2#a1fd65c7ccbf10880423d82bca54eb54
 https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.18.1-h7f98852_0.tar.bz2#f26ef8098fab1f719c91eb760d63381a
-https://conda.anaconda.org/conda-forge/linux-64/expat-2.4.8-h27087fc_0.tar.bz2#e1b07832504eeba765d648389cc387a9
-https://conda.anaconda.org/conda-forge/linux-64/fftw-3.3.10-nompi_ha7695d1_103.tar.bz2#a56c5033619bdf56a22a1f0a0fd286aa
+https://conda.anaconda.org/conda-forge/linux-64/expat-2.5.0-h27087fc_0.tar.bz2#c4fbad8d4bddeb3c085f18cbf97fbfad
+https://conda.anaconda.org/conda-forge/linux-64/fftw-3.3.10-nompi_hf0379b8_105.tar.bz2#9d3e01547ba04a57372beee01158096f
 https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.10-h36c2ea0_0.tar.bz2#ac7bc6a654f8f41b352b38f4051135f8
-https://conda.anaconda.org/conda-forge/linux-64/geos-3.11.0-h27087fc_0.tar.bz2#a583d0bc9a85c48e8b07a588d1ac8a80
+https://conda.anaconda.org/conda-forge/linux-64/geos-3.11.1-h27087fc_0.tar.bz2#917b9a50001fffdd89b321b5dba31e55
+https://conda.anaconda.org/conda-forge/linux-64/gettext-0.21.1-h27087fc_0.tar.bz2#14947d8770185e5153fdd04d4673ed37
 https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.1-h36c2ea0_2.tar.bz2#626e68ae9cc5912d6adb79d318cf962d
 https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.13-h58526e2_1001.tar.bz2#8c54672728e8ec6aa6db90cf2806d220
 https://conda.anaconda.org/conda-forge/linux-64/icu-70.1-h27087fc_0.tar.bz2#87473a15119779e021c314249d4b4aed
 https://conda.anaconda.org/conda-forge/linux-64/jpeg-9e-h166bdaf_2.tar.bz2#ee8b844357a0946870901c7c6f418268
 https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2#30186d27e2c9fa62b45fb1476b7200e3
+https://conda.anaconda.org/conda-forge/linux-64/lame-3.100-h166bdaf_1003.tar.bz2#a8832b479f93521a9e7b5b743803be51
 https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h27087fc_0.tar.bz2#76bbff344f0134279f225174e9064c8f
-https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.0.9-h166bdaf_7.tar.bz2#f82dc1c78bcf73583f2656433ce2933c
+https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.0.9-h166bdaf_8.tar.bz2#9194c9bf9428035a05352d031462eae4
 https://conda.anaconda.org/conda-forge/linux-64/libdb-6.2.32-h9c3ff4c_0.tar.bz2#3f3258d8f841fbac63b36b75bdac1afd
-https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.13-h166bdaf_0.tar.bz2#4b5bee2e957570197327d0b20a718891
+https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.14-h166bdaf_0.tar.bz2#fc84a0446e4e4fb882e78d786cfb9734
 https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-h516909a_1.tar.bz2#6f8720dff19e17ce5d48cfe7f3d2f0a3
 https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2#d645c6d2ac96843a2bfaccd2d62b3ac3
-https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.16-h516909a_0.tar.bz2#5c0f338a513a2943c659ae619fca9211
+https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.17-h166bdaf_0.tar.bz2#b62b52da46c39ee2bc3c162ac7f1804d
 https://conda.anaconda.org/conda-forge/linux-64/libmo_unpack-3.1.2-hf484d3e_1001.tar.bz2#95f32a6a5a666d33886ca5627239f03d
 https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.0-h7f98852_0.tar.bz2#39b1328babf85c7c3a61636d9cd50206
 https://conda.anaconda.org/conda-forge/linux-64/libogg-1.3.4-h7f98852_1.tar.bz2#6e8cc2173440d77708196c5b93771680
-https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.21-pthreads_h78a6416_2.tar.bz2#839776c4e967bc881c21da197127a3ae
+https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.21-pthreads_h78a6416_3.tar.bz2#8c5963a49b6035c40646a763293fbb35
 https://conda.anaconda.org/conda-forge/linux-64/libopus-1.3.1-h7f98852_1.tar.bz2#15345e56d527b330e1cacbdf58676e8f
 https://conda.anaconda.org/conda-forge/linux-64/libtool-2.4.6-h9c3ff4c_1008.tar.bz2#16e143a1ed4b4fd169536373957f6fee
-https://conda.anaconda.org/conda-forge/linux-64/libudev1-249-h166bdaf_4.tar.bz2#dc075ff6fcb46b3d3c7652e543d5f334
+https://conda.anaconda.org/conda-forge/linux-64/libudev1-252-h166bdaf_0.tar.bz2#174243089ec111479298a5b7099b64b5
 https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.32.1-h7f98852_1000.tar.bz2#772d69f030955d9646d3d0eaf21d859d
 https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.2.4-h166bdaf_0.tar.bz2#ac2ccf7323d21f2994e4d1f5da664f37
-https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.2.12-h166bdaf_2.tar.bz2#8302381297332ea50532cf2c67961080
-https://conda.anaconda.org/conda-forge/linux-64/mpich-4.0.2-h846660c_100.tar.bz2#36a36fe04b932d4b327e7e81c5c43696
+https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.2.13-h166bdaf_4.tar.bz2#f3f9de449d32ca9b9c66a22863c96f41
+https://conda.anaconda.org/conda-forge/linux-64/mpg123-1.30.2-h27087fc_1.tar.bz2#2fe2a839394ef3a1825a5e5e296060bc
+https://conda.anaconda.org/conda-forge/linux-64/mpich-4.0.3-h846660c_100.tar.bz2#50d66bb751cfa71ee2a48b2d3eb90ac1
 https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.3-h27087fc_1.tar.bz2#4acfc691e64342b9dae57cf2adc63238
 https://conda.anaconda.org/conda-forge/linux-64/nspr-4.32-h9c3ff4c_1.tar.bz2#29ded371806431b0499aaee146abfc3e
-https://conda.anaconda.org/conda-forge/linux-64/openssl-1.1.1q-h166bdaf_0.tar.bz2#07acc367c7fc8b716770cd5b36d31717
-https://conda.anaconda.org/conda-forge/linux-64/pcre-8.45-h9c3ff4c_0.tar.bz2#c05d1820a6d34ff07aaaab7a9b7eddaa
+https://conda.anaconda.org/conda-forge/linux-64/openssl-1.1.1s-h166bdaf_0.tar.bz2#e17553617ce05787d97715177be014d1
 https://conda.anaconda.org/conda-forge/linux-64/pixman-0.40.0-h36c2ea0_0.tar.bz2#660e72c82f2e75a6b3fe6a6e75c79f19
 https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-h36c2ea0_1001.tar.bz2#22dad4df6e8630e8dff2428f6f6a7036
 https://conda.anaconda.org/conda-forge/linux-64/xorg-kbproto-1.0.7-h7f98852_1002.tar.bz2#4b230e8381279d76131116660f5a241a
@@ -65,61 +67,60 @@ https://conda.anaconda.org/conda-forge/linux-64/xorg-xproto-7.0.31-h7f98852_1007
 https://conda.anaconda.org/conda-forge/linux-64/xxhash-0.8.0-h7f98852_3.tar.bz2#52402c791f35e414e704b7a113f99605
 https://conda.anaconda.org/conda-forge/linux-64/xz-5.2.6-h166bdaf_0.tar.bz2#2161070d867d1b1204ea749c8eec4ef0
 https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2#4cb3ad778ec2d5a7acbdf254eb1c42ae
-https://conda.anaconda.org/conda-forge/linux-64/gettext-0.19.8.1-h73d1719_1008.tar.bz2#af49250eca8e139378f8ff0ae9e57251
-https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h9772cbc_4.tar.bz2#dd3e1941dd06f64cb88647d2f7ff8aaa
 https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-16_linux64_openblas.tar.bz2#d9b7a8639171f6c6fa0a983edabcfe2b
-https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.0.9-h166bdaf_7.tar.bz2#37a460703214d0d1b421e2a47eb5e6d0
-https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.0.9-h166bdaf_7.tar.bz2#785a9296ea478eb78c47593c4da6550f
-https://conda.anaconda.org/conda-forge/linux-64/libcap-2.64-ha37c62d_0.tar.bz2#5896fbd58d0376df8556a4aba1ce4f71
+https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.0.9-h166bdaf_8.tar.bz2#4ae4d7795d33e02bd20f6b23d91caf82
+https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.0.9-h166bdaf_8.tar.bz2#04bac51ba35ea023dc48af73c1c88c25
+https://conda.anaconda.org/conda-forge/linux-64/libcap-2.66-ha37c62d_0.tar.bz2#2d7665abd0997f1a6d4b7596bc27b657
 https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2#4d331e44109e3f0e19b4cb8f9b82f3e1
 https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.10-h9b69904_4.tar.bz2#390026683aef81db27ff1b8570ca1336
-https://conda.anaconda.org/conda-forge/linux-64/libllvm14-14.0.6-he0ac6c6_0.tar.bz2#f5759f0c80708fbf9c4836c0cb46d0fe
+https://conda.anaconda.org/conda-forge/linux-64/libflac-1.4.2-h27087fc_0.tar.bz2#7daf72d8e2a8e848e11d63ed6d1026e0
 https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.47.0-hdcd2b5c_1.tar.bz2#6fe9e31c2b8d0b022626ccac13e6ca3c
-https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.37-h753d276_4.tar.bz2#6b611734b73d639c084ac4be2fcd996a
-https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.39.2-h753d276_1.tar.bz2#90136dc0a305db4e1df24945d431457b
+https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.38-h753d276_0.tar.bz2#575078de1d3a3114b3ce131bd1508d0c
+https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.39.4-h753d276_0.tar.bz2#978924c298fc2215f129e8171bbea688
 https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.10.0-haa6b8db_3.tar.bz2#89acee135f0809a18a1f4537390aa2dd
 https://conda.anaconda.org/conda-forge/linux-64/libvorbis-1.3.7-h9c3ff4c_0.tar.bz2#309dec04b70a3cc0f1e84a4013683bc0
 https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.13-h7f98852_1004.tar.bz2#b3653fdc58d03face9724f602218a904
-https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.9.14-h22db469_4.tar.bz2#aced7c1f4b4dbfea08e033c6ae97c53e
+https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.10.3-h7463322_0.tar.bz2#3b933ea47ef8f330c4c068af25fcd6a8
 https://conda.anaconda.org/conda-forge/linux-64/libzip-1.9.2-hc869a4a_1.tar.bz2#7a268cf1386d271e576e35ae82149ef2
-https://conda.anaconda.org/conda-forge/linux-64/mysql-common-8.0.30-haf5c9bc_0.tar.bz2#9d3e24b1157af09abe5a2589119c7b1d
-https://conda.anaconda.org/conda-forge/linux-64/portaudio-19.6.0-h57a0ea0_5.tar.bz2#5469312a373f481c05c380897fd7c923
+https://conda.anaconda.org/conda-forge/linux-64/mysql-common-8.0.31-haf5c9bc_0.tar.bz2#0249d755f8d26cb2ac796f9f01cfb823
+https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.40-hc3806b6_0.tar.bz2#69e2c796349cd9b273890bee0febfe1b
 https://conda.anaconda.org/conda-forge/linux-64/readline-8.1.2-h0f457ee_0.tar.bz2#db2ebbe2943aae81ed051a6a9af8e0fa
 https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.12-h27826a3_0.tar.bz2#5b8c42eb62e9fc961af70bdd6a26e168
 https://conda.anaconda.org/conda-forge/linux-64/udunits2-2.2.28-hc3e0081_0.tar.bz2#d4c341e0379c31e9e781d4f204726867
 https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.3-hd9c2040_1000.tar.bz2#9e856f78d5c80d5a78f61e72d1d473a3
-https://conda.anaconda.org/conda-forge/linux-64/zlib-1.2.12-h166bdaf_2.tar.bz2#4533821485cde83ab12ff3d8bda83768
+https://conda.anaconda.org/conda-forge/linux-64/zlib-1.2.13-h166bdaf_4.tar.bz2#4b11e365c0275b808be78b30f904e295
 https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.2-h6239696_4.tar.bz2#adcf0be7897e73e312bd24353b613f74
-https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.0.9-h166bdaf_7.tar.bz2#1699c1211d56a23c66047524cd76796e
+https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.0.9-h166bdaf_8.tar.bz2#e5613f2bc717e9945840ff474419b8e4
 https://conda.anaconda.org/conda-forge/linux-64/freetype-2.12.1-hca18f0e_0.tar.bz2#4e54cbfc47b8c74c2ecc1e7730d8edce
+https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h9772cbc_5.tar.bz2#ee08782aff2ff9b3291c967fa6bc7336
 https://conda.anaconda.org/conda-forge/linux-64/krb5-1.19.3-h3790be6_0.tar.bz2#7d862b05445123144bec92cb1acc8ef8
 https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-16_linux64_openblas.tar.bz2#20bae26d0a1db73f758fc3754cab4719
-https://conda.anaconda.org/conda-forge/linux-64/libclang13-14.0.6-default_h3a83d3e_0.tar.bz2#cdbd49e0ab5c5a6c522acb8271977d4c
-https://conda.anaconda.org/conda-forge/linux-64/libflac-1.3.4-h27087fc_0.tar.bz2#620e52e160fd09eb8772dedd46bb19ef
-https://conda.anaconda.org/conda-forge/linux-64/libglib-2.72.1-h2d90d5f_0.tar.bz2#ebeadbb5fbc44052eeb6f96a2136e3c2
+https://conda.anaconda.org/conda-forge/linux-64/libglib-2.74.1-h606061b_1.tar.bz2#ed5349aa96776e00b34eccecf4a948fe
 https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-16_linux64_openblas.tar.bz2#955d993f41f9354bf753d29864ea20ad
-https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.4.0-h0e0dad5_3.tar.bz2#5627d42c13a9b117ae1701c6e195624f
+https://conda.anaconda.org/conda-forge/linux-64/libllvm15-15.0.4-h63197d8_1.tar.bz2#d1b93417274d24b751a831c21169669a
+https://conda.anaconda.org/conda-forge/linux-64/libsndfile-1.1.0-h27087fc_0.tar.bz2#02fa0b56a57c8421d1195bf0c021e682
+https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.4.0-h55922b4_4.tar.bz2#901791f0ec7cddc8714e76e273013a91
 https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.0.3-he3ba5ed_0.tar.bz2#f9dbabc7e01c459ed7a1d1d64b206e9b
-https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-8.0.30-h28c427c_0.tar.bz2#77f98ec0b224fd5ca8e7043e167efb83
-https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.39.2-h4ff8645_1.tar.bz2#2676ec698ce91567fca50654ac1b18ba
+https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-8.0.31-h28c427c_0.tar.bz2#455d44a05123f30f66af2ca2a9652b5f
+https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.39.4-h4ff8645_0.tar.bz2#643c271de2dd23ecbd107284426cebc2
 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.0-h166bdaf_0.tar.bz2#384e7fcb3cd162ba3e4aed4b687df566
 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.0-h166bdaf_0.tar.bz2#637054603bb7594302e3bf83f0a99879
 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.9-h166bdaf_0.tar.bz2#732e22f1741bccea861f5668cf7342a7
 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.1-h166bdaf_0.tar.bz2#0a8e20a8aef954390b9481a527421a8c
 https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.7.2-h7f98852_0.tar.bz2#12a61e640b8894504326aadafccbb790
-https://conda.anaconda.org/conda-forge/linux-64/atk-1.0-2.36.0-h3371d22_4.tar.bz2#661e1ed5d92552785d9f8c781ce68685
-https://conda.anaconda.org/conda-forge/linux-64/brotli-1.0.9-h166bdaf_7.tar.bz2#3889dec08a472eb0f423e5609c76bde1
+https://conda.anaconda.org/conda-forge/linux-64/atk-1.0-2.38.0-hd4edc92_1.tar.bz2#6c72ec3e660a51736913ef6ea68c454b
+https://conda.anaconda.org/conda-forge/linux-64/brotli-1.0.9-h166bdaf_8.tar.bz2#2ff08978892a3e8b954397c461f18418
 https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h5008d03_3.tar.bz2#ecfff944ba3960ecb334b9a2663d708d
-https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.14.0-h8e229c2_0.tar.bz2#f314f79031fec74adc9bff50fbaffd89
-https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.42.8-hff1cb4f_0.tar.bz2#908fc30f89e27817d835b45f865536d7
-https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.72.1-h6239696_0.tar.bz2#a3a99cc33279091262bbc4f5ee7c4571
+https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.14.1-hc2a2eb6_0.tar.bz2#78415f0180a8d9c5bcc47889e00d5fb1
+https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.42.8-hff1cb4f_1.tar.bz2#a61c6312192e7c9de71548a6706a21e6
+https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.74.1-h6239696_1.tar.bz2#5f442e6bc9d89ba236eb25a25c5c2815
 https://conda.anaconda.org/conda-forge/linux-64/gts-0.7.6-h64030ff_2.tar.bz2#112eb9b5b93f0c02e59aea4fd1967363
-https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.12-hddcbb42_0.tar.bz2#797117394a4aa588de6d741b06fad80f
-https://conda.anaconda.org/conda-forge/linux-64/libclang-14.0.6-default_h2e3cab8_0.tar.bz2#eb70548da697e50cefa7ba939d57d001
+https://conda.anaconda.org/conda-forge/linux-64/jack-1.9.21-he978b8e_1.tar.bz2#5cef21ebd70a90a0d28127543a8d3739
+https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.14-h6ed2654_0.tar.bz2#dcc588839de1445d90995a0a2c4f3a39
+https://conda.anaconda.org/conda-forge/linux-64/libclang13-15.0.4-default_h3a83d3e_0.tar.bz2#d60d2d91b5991e364fd95cba5ec8ab96
 https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h3e49a29_2.tar.bz2#3b88f1d0fe2580594d58d7e44d664617
-https://conda.anaconda.org/conda-forge/linux-64/libcurl-7.83.1-h7bff187_0.tar.bz2#d0c278476dba3b29ee13203784672ab1
-https://conda.anaconda.org/conda-forge/linux-64/libpq-14.5-hd77ab85_0.tar.bz2#d3126b425a04ed2360da1e651cef1b2d
-https://conda.anaconda.org/conda-forge/linux-64/libsndfile-1.0.31-h9c3ff4c_1.tar.bz2#fc4b6d93da04731db7601f2a1b1dc96a
+https://conda.anaconda.org/conda-forge/linux-64/libcurl-7.86.0-h7bff187_1.tar.bz2#5e5f33d81f31598d87f9b849f73c30e3
+https://conda.anaconda.org/conda-forge/linux-64/libpq-14.5-hd77ab85_1.tar.bz2#f5c8135a70758d928a8126998a6558d8
 https://conda.anaconda.org/conda-forge/linux-64/libwebp-1.2.4-h522a892_0.tar.bz2#802e43f480122a85ae6a34c1909f8f98
 https://conda.anaconda.org/conda-forge/linux-64/nss-3.78-h2350873_0.tar.bz2#ab3df39f96742e6f1a9878b09274c1dc
 https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.0-h7d73246_1.tar.bz2#a11b4df9271a8d7917686725aa04c8f2
@@ -129,38 +130,42 @@ https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.4-h7f98852_1.ta
 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.10-h7f98852_1003.tar.bz2#f59c1242cc1dd93e72c2ee2b360979eb
 https://conda.anaconda.org/conda-forge/noarch/alabaster-0.7.12-py_0.tar.bz2#2489a97287f90176ecdc3ca982b4b0a0
 https://conda.anaconda.org/conda-forge/noarch/attrs-22.1.0-pyh71513ae_1.tar.bz2#6d3ccbc56256204925bfa8378722792f
-https://conda.anaconda.org/conda-forge/linux-64/cairo-1.16.0-ha61ee94_1012.tar.bz2#9604a7c93dd37bcb6d6cc8d6b64223a4
-https://conda.anaconda.org/conda-forge/noarch/certifi-2022.6.15-pyhd8ed1ab_1.tar.bz2#97349c8d67627cbf8f48d7e7e1773ea5
+https://conda.anaconda.org/conda-forge/linux-64/cairo-1.16.0-ha61ee94_1014.tar.bz2#d1a88f3ed5b52e1024b80d4bcd26a7a0
+https://conda.anaconda.org/conda-forge/noarch/certifi-2022.9.24-pyhd8ed1ab_0.tar.bz2#f66309b099374af91369e67e84af397d
 https://conda.anaconda.org/conda-forge/noarch/cfgv-3.3.1-pyhd8ed1ab_0.tar.bz2#ebb5f5f7dc4f1a3780ef7ea7738db08c
 https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-2.1.1-pyhd8ed1ab_0.tar.bz2#c1d5b294fbf9a795dec349a6f4d8be8e
-https://conda.anaconda.org/conda-forge/noarch/cloudpickle-2.1.0-pyhd8ed1ab_0.tar.bz2#f7551a8a008dfad2b7ac9662dd124614
-https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.5-pyhd8ed1ab_0.tar.bz2#c267da48ce208905d7d976d49dfd9433
-https://conda.anaconda.org/conda-forge/linux-64/curl-7.83.1-h7bff187_0.tar.bz2#ba33b9995f5e691e4f439422d6efafc7
+https://conda.anaconda.org/conda-forge/noarch/click-8.1.3-unix_pyhd8ed1ab_2.tar.bz2#20e4087407c7cb04a40817114b333dbf
+https://conda.anaconda.org/conda-forge/noarch/cloudpickle-2.2.0-pyhd8ed1ab_0.tar.bz2#a6cf47b09786423200d7982d1faa19eb
+https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_0.tar.bz2#3faab06a954c2a04039983f2c4a50d99
+https://conda.anaconda.org/conda-forge/linux-64/curl-7.86.0-h7bff187_1.tar.bz2#bc9567c50833f4b0d36b25caca7b34f8
 https://conda.anaconda.org/conda-forge/noarch/cycler-0.11.0-pyhd8ed1ab_0.tar.bz2#a50559fad0affdbb33729a68669ca1cb
-https://conda.anaconda.org/conda-forge/noarch/distlib-0.3.5-pyhd8ed1ab_0.tar.bz2#f15c3912378a07726093cc94d1e13251
+https://conda.anaconda.org/conda-forge/noarch/distlib-0.3.6-pyhd8ed1ab_0.tar.bz2#b65b4d50dbd2d50fa0aeac367ec9eed7
+https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.0.4-pyhd8ed1ab_0.tar.bz2#e0734d1f12de77f9daca98bda3428733
 https://conda.anaconda.org/conda-forge/noarch/execnet-1.9.0-pyhd8ed1ab_0.tar.bz2#0e521f7a5e60d508b121d38b04874fb2
 https://conda.anaconda.org/conda-forge/noarch/filelock-3.8.0-pyhd8ed1ab_0.tar.bz2#10f0218dbd493ab2e5dc6759ddea4526
-https://conda.anaconda.org/conda-forge/noarch/fsspec-2022.7.1-pyhd8ed1ab_0.tar.bz2#984db277dfb9ea04a584aea39c6a34e4
-https://conda.anaconda.org/conda-forge/linux-64/glib-2.72.1-h6239696_0.tar.bz2#1698b7684d3c6a4d1de2ab946f5b0fb5
+https://conda.anaconda.org/conda-forge/noarch/fsspec-2022.11.0-pyhd8ed1ab_0.tar.bz2#eb919f2119a6db5d0192f9e9c3711572
+https://conda.anaconda.org/conda-forge/linux-64/glib-2.74.1-h6239696_1.tar.bz2#f3220a9e9d3abcbfca43419a219df7e4
 https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.12.2-mpi_mpich_h08b82f9_0.tar.bz2#de601caacbaa828d845f758e07e3b85e
-https://conda.anaconda.org/conda-forge/noarch/idna-3.3-pyhd8ed1ab_0.tar.bz2#40b50b8b030f5f2f22085c062ed013dd
+https://conda.anaconda.org/conda-forge/noarch/idna-3.4-pyhd8ed1ab_0.tar.bz2#34272b248891bddccc64479f9a7fffed
 https://conda.anaconda.org/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2#7de5386c8fea29e76b303f37dde4c352
 https://conda.anaconda.org/conda-forge/noarch/iniconfig-1.1.1-pyh9f0ad1d_0.tar.bz2#39161f81cc5e5ca45b8226fbb06c6905
 https://conda.anaconda.org/conda-forge/noarch/iris-sample-data-2.4.0-pyhd8ed1ab_0.tar.bz2#18ee9c07cf945a33f92caf1ee3d23ad9
-https://conda.anaconda.org/conda-forge/linux-64/jack-1.9.18-h8c3723f_1002.tar.bz2#7b3f287fcb7683f67b3d953b79f412ea
+https://conda.anaconda.org/conda-forge/linux-64/libclang-15.0.4-default_h2e3cab8_0.tar.bz2#248a013d3e7248c5c5f4ff69ffc307e8
 https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-h18fbbfe_3.tar.bz2#ea9758cf553476ddf75c789fdd239dc5
 https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2#91e27ef3d05cc772ce627e51cff111c4
 https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2#2ba8498c1018c1e9c61eb99b973dfe19
 https://conda.anaconda.org/conda-forge/noarch/platformdirs-2.5.2-pyhd8ed1ab_1.tar.bz2#2fb3f88922e7aec26ba652fcdfe13950
+https://conda.anaconda.org/conda-forge/noarch/pluggy-1.0.0-pyhd8ed1ab_5.tar.bz2#7d301a0d25f424d96175f810935f0da9
 https://conda.anaconda.org/conda-forge/noarch/ply-3.11-py_1.tar.bz2#7205635cd71531943440fbfe3b6b5727
-https://conda.anaconda.org/conda-forge/linux-64/proj-9.0.1-h93bde94_1.tar.bz2#8259528ea471b0963a91ce174f002e55
-https://conda.anaconda.org/conda-forge/noarch/py-1.11.0-pyh6c4a22f_0.tar.bz2#b4613d7e7a493916d867842a6a148054
+https://conda.anaconda.org/conda-forge/linux-64/proj-9.1.0-h93bde94_0.tar.bz2#255c7204dda39747c3ba380d28b026d7
+https://conda.anaconda.org/conda-forge/linux-64/pulseaudio-14.0-h0d2025b_11.tar.bz2#316026c5f80d8b5b9666e87b8614ac6c
 https://conda.anaconda.org/conda-forge/noarch/pycparser-2.21-pyhd8ed1ab_0.tar.bz2#076becd9e05608f8dc72757d5f3a91ff
 https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.0.9-pyhd8ed1ab_0.tar.bz2#e8fbc1b54b25f4b08281467bc13b70cc
 https://conda.anaconda.org/conda-forge/noarch/pyshp-2.3.1-pyhd8ed1ab_0.tar.bz2#92a889dc236a5197612bc85bee6d7174
+https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2#2a7de29fb590ca14b5243c4c812c8025
 https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.8-2_cp38.tar.bz2#bfbb29d517281e78ac53e48d21e6e860
-https://conda.anaconda.org/conda-forge/noarch/pytz-2022.2.1-pyhd8ed1ab_0.tar.bz2#974bca71d00364630f63f31fa7e059cb
-https://conda.anaconda.org/conda-forge/noarch/setuptools-65.3.0-pyhd8ed1ab_1.tar.bz2#a64c8af7be7a6348c1d9e530f88fa4da
+https://conda.anaconda.org/conda-forge/noarch/pytz-2022.6-pyhd8ed1ab_0.tar.bz2#b1f26ad83328e486910ef7f6e81dc061
+https://conda.anaconda.org/conda-forge/noarch/setuptools-65.5.1-pyhd8ed1ab_0.tar.bz2#cfb8dc4d9d285ca5fb1177b9dd450e33
 https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2#e5f25f8dbc060e9a8d912e432202afc2
 https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-2.2.0-pyhd8ed1ab_0.tar.bz2#4d22a9315e78c6827f806065957d566e
 https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.3.2.post1-pyhd8ed1ab_0.tar.bz2#146f4541d643d48fc8a75cacf69f03ae
@@ -173,83 +178,80 @@ https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-serializinghtml-1.1.
 https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_0.tar.bz2#f832c45a477c78bebd107098db465095
 https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2#5844808ffab9ebdb694585b50ba02a96
 https://conda.anaconda.org/conda-forge/noarch/toolz-0.12.0-pyhd8ed1ab_0.tar.bz2#92facfec94bc02d6ccf42e7173831a36
-https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.3.0-pyha770c72_0.tar.bz2#a9d85960bc62d53cc4ea0d1d27f73c98
-https://conda.anaconda.org/conda-forge/noarch/wheel-0.37.1-pyhd8ed1ab_0.tar.bz2#1ca02aaf78d9c70d9a81a3bed5752022
-https://conda.anaconda.org/conda-forge/noarch/zipp-3.8.1-pyhd8ed1ab_0.tar.bz2#a3508a0c850745b875de88aea4c40cc5
+https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.4.0-pyha770c72_0.tar.bz2#2d93b130d148d7fc77e583677792fc6a
+https://conda.anaconda.org/conda-forge/noarch/wheel-0.38.4-pyhd8ed1ab_0.tar.bz2#c829cfb8cb826acb9de0ac1a2df0a940
+https://conda.anaconda.org/conda-forge/noarch/zipp-3.10.0-pyhd8ed1ab_0.tar.bz2#cd4eb48ebde7de61f92252979aab515c
 https://conda.anaconda.org/conda-forge/linux-64/antlr-python-runtime-4.7.2-py38h578d9bd_1003.tar.bz2#db8b471d9a764f561a129f94ea215c0a
-https://conda.anaconda.org/conda-forge/noarch/babel-2.10.3-pyhd8ed1ab_0.tar.bz2#72f1c6d03109d7a70087bc1d029a8eda
+https://conda.anaconda.org/conda-forge/noarch/babel-2.11.0-pyhd8ed1ab_0.tar.bz2#2ea70fde8d581ba9425a761609eed6ba
 https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.11.1-pyha770c72_0.tar.bz2#eeec8814bd97b2681f708bb127478d7d
-https://conda.anaconda.org/conda-forge/linux-64/cffi-1.15.1-py38h4a40e3a_0.tar.bz2#a970d201055ec06a75db83bf25447eb2
-https://conda.anaconda.org/conda-forge/linux-64/docutils-0.16-py38h578d9bd_3.tar.bz2#a7866449fb9e5e4008a02df276549d34
-https://conda.anaconda.org/conda-forge/linux-64/gstreamer-1.20.3-hd4edc92_0.tar.bz2#94cb81ffdce328f80c87ac9b01244632
-https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-5.1.0-hf9f4e7c_0.tar.bz2#7c1f73a8f7864a202b126d82e88ddffc
-https://conda.anaconda.org/conda-forge/linux-64/importlib-metadata-4.11.4-py38h578d9bd_0.tar.bz2#037225c33a50e99c5d4f86fac90f6de8
-https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.4-py38h43d8883_0.tar.bz2#ae54c61918e1cbd280b8587ed6219258
-https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.8.1-mpi_mpich_h06c54e2_4.tar.bz2#491803a7356c6a668a84d71f491c4014
-https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.1-py38h0a891b7_1.tar.bz2#20d003ad5f584e212c299f64cac46c05
-https://conda.anaconda.org/conda-forge/linux-64/mpi4py-3.1.3-py38h97ac3a3_2.tar.bz2#fccce86e5fc8183bf2658ac9bfc535b4
+https://conda.anaconda.org/conda-forge/linux-64/cffi-1.15.1-py38h4a40e3a_2.tar.bz2#2276b1f4d1ede3f5f14cc7e4ae6f9a33
+https://conda.anaconda.org/conda-forge/linux-64/docutils-0.17.1-py38h578d9bd_3.tar.bz2#34e1f12e3ed15aff218644e9d865b722
+https://conda.anaconda.org/conda-forge/linux-64/gstreamer-1.21.1-hd4edc92_1.tar.bz2#e604f83df3497fcdb6991ae58b73238f
+https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-5.3.0-h418a68e_0.tar.bz2#888056bd4b12e110b10d4d1f29161c5e
+https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-5.0.0-pyha770c72_1.tar.bz2#ec069c4db6a0ad84107bac5da62819d2
+https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.4-py38h43d8883_1.tar.bz2#41ca56d5cac7bfc7eb4fcdbee878eb84
+https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.8.1-mpi_mpich_hcd871d9_6.tar.bz2#6cdc429ed22edb566ac4308f3da6916d
+https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.1-py38h0a891b7_2.tar.bz2#c342a370480791db83d5dd20f2d8899f
+https://conda.anaconda.org/conda-forge/linux-64/mpi4py-3.1.4-py38h97ac3a3_0.tar.bz2#0c469687a517052c0d581fc6e1a4189d
 https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.7.0-pyhd8ed1ab_0.tar.bz2#fbe1182f650c04513046d6894046cd6c
-https://conda.anaconda.org/conda-forge/linux-64/numpy-1.23.2-py38h3a7f9d9_0.tar.bz2#a7579626c41b3975da213c0b53aefa29
+https://conda.anaconda.org/conda-forge/linux-64/numpy-1.23.4-py38h7042d01_1.tar.bz2#7fa0e9ed4e8a096e2f00b2426ebba0de
 https://conda.anaconda.org/conda-forge/noarch/packaging-21.3-pyhd8ed1ab_0.tar.bz2#71f1ab2de48613876becddd496371c85
 https://conda.anaconda.org/conda-forge/noarch/partd-1.3.0-pyhd8ed1ab_0.tar.bz2#af8c82d121e63082926062d61d9abb54
-https://conda.anaconda.org/conda-forge/linux-64/pillow-9.2.0-py38ha3b2c9c_2.tar.bz2#a077cc2bb9d854074b1cf4607252da7a
-https://conda.anaconda.org/conda-forge/noarch/pip-22.2.2-pyhd8ed1ab_0.tar.bz2#0b43abe4d3ee93e82742d37def53a836
-https://conda.anaconda.org/conda-forge/linux-64/pluggy-1.0.0-py38h578d9bd_3.tar.bz2#6ce4ce3d4490a56eb33b52c179609193
+https://conda.anaconda.org/conda-forge/linux-64/pillow-9.2.0-py38h9eb91d8_3.tar.bz2#61dc7b3140b7b79b1985b53d52726d74
+https://conda.anaconda.org/conda-forge/noarch/pip-22.3.1-pyhd8ed1ab_0.tar.bz2#da66f2851b9836d3a7c5190082a45f7d
 https://conda.anaconda.org/conda-forge/noarch/pockets-0.9.1-py_0.tar.bz2#1b52f0c42e8077e5a33e00fe72269364
-https://conda.anaconda.org/conda-forge/linux-64/psutil-5.9.1-py38h0a891b7_0.tar.bz2#e3908bd184030e7f4a3d837959ebf6d7
-https://conda.anaconda.org/conda-forge/linux-64/pulseaudio-14.0-h7f54b18_8.tar.bz2#f9dbcfbb942ec9a3c0249cb71da5c7d1
+https://conda.anaconda.org/conda-forge/linux-64/psutil-5.9.4-py38h0a891b7_0.tar.bz2#fe2ef279417faa1af0adf178de2032f7
 https://conda.anaconda.org/conda-forge/noarch/pygments-2.13.0-pyhd8ed1ab_0.tar.bz2#9f478e8eedd301008b5f395bad0caaed
-https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.3.1-py38he1635e7_1.tar.bz2#3907607e23c3e18202960fc4217baa0a
-https://conda.anaconda.org/conda-forge/linux-64/pysocks-1.7.1-py38h578d9bd_5.tar.bz2#11113c7e50bb81f30762fe8325f305e1
+https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.4.0-py38hce0a2d1_2.tar.bz2#be61a535f279bffdf7f449a654eaa19d
 https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.8.2-pyhd8ed1ab_0.tar.bz2#dd999d1cc9f79e67dbb855c8924c7984
-https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.0.0-py38h0a891b7_1.tar.bz2#69fc64e4f4c13abe0b8df699ddaa1051
-https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0-py38h0a891b7_4.tar.bz2#ba24ff01bb38c5cd5be54b45ef685db3
-https://conda.anaconda.org/conda-forge/linux-64/tornado-6.2-py38h0a891b7_0.tar.bz2#acd276486a0067bee3098590f0952a0f
-https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.3.0-hd8ed1ab_0.tar.bz2#f3e98e944832fb271a0dbda7b7771dc6
-https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-14.0.0-py38h0a891b7_1.tar.bz2#83df0e9e3faffc295f12607438691465
-https://conda.anaconda.org/conda-forge/linux-64/virtualenv-20.16.3-py38h578d9bd_1.tar.bz2#30765568a158c9457d577cc83f0e8307
-https://conda.anaconda.org/conda-forge/linux-64/brotlipy-0.7.0-py38h0a891b7_1004.tar.bz2#9fcaaca218dcfeb8da806d4fd4824aa0
-https://conda.anaconda.org/conda-forge/linux-64/cftime-1.6.1-py38h71d37f0_0.tar.bz2#acf7ef1f057459e9e707142a4b92e481
-https://conda.anaconda.org/conda-forge/linux-64/cryptography-37.0.4-py38h2b5fc30_0.tar.bz2#28e9acd6f13ed29f27d5550a1cf0554b
-https://conda.anaconda.org/conda-forge/noarch/dask-core-2022.8.1-pyhd8ed1ab_0.tar.bz2#df5026dbf551bb992cdf247b08e11078
-https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.37.1-py38h0a891b7_0.tar.bz2#369c805e42d0244be7c097b39c38ebb4
-https://conda.anaconda.org/conda-forge/linux-64/gst-plugins-base-1.20.3-hf6a322e_0.tar.bz2#6ea2ce6265c3207876ef2369b7479f08
+https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.0.0-py38h0a891b7_2.tar.bz2#9b13816a39904084556126a6ce7fd0d0
+https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0-py38h0a891b7_5.tar.bz2#0856c59f9ddb710c640dc0428d66b1b7
+https://conda.anaconda.org/conda-forge/linux-64/tornado-6.2-py38h0a891b7_1.tar.bz2#358beb228a53b5e1031862de3525d1d3
+https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.4.0-hd8ed1ab_0.tar.bz2#be969210b61b897775a0de63cd9e9026
+https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-15.0.0-py38h0a891b7_0.tar.bz2#44421904760e9f5ae2035193e04360f0
+https://conda.anaconda.org/conda-forge/linux-64/virtualenv-20.16.7-py38h578d9bd_0.tar.bz2#fc6d74114bb0006224f252393bdacee6
+https://conda.anaconda.org/conda-forge/linux-64/brotlipy-0.7.0-py38h0a891b7_1005.tar.bz2#e99e08812dfff30fdd17b3f8838e2759
+https://conda.anaconda.org/conda-forge/linux-64/cftime-1.6.2-py38h26c90d9_1.tar.bz2#dcc025a7bb54374979c500c2e161fac9
+https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.0.6-py38h43d8883_0.tar.bz2#1107ee053d55172b26c4fc905dd0238e
+https://conda.anaconda.org/conda-forge/linux-64/cryptography-38.0.3-py38h2b5fc30_0.tar.bz2#218274e4a04630a977b4da2b45eff593
+https://conda.anaconda.org/conda-forge/noarch/dask-core-2022.11.0-pyhd8ed1ab_0.tar.bz2#1ab924fb3c44ed49796cc85eb1315cdc
+https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.38.0-py38h0a891b7_1.tar.bz2#62c89ddefed9c5835e228a32b357a28d
+https://conda.anaconda.org/conda-forge/linux-64/gst-plugins-base-1.21.1-h3e40eee_1.tar.bz2#c03f4fca88373cf6c26d932c26e4ee20
 https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.2-pyhd8ed1ab_1.tar.bz2#c8490ed5c70966d232fdd389d0dbed37
-https://conda.anaconda.org/conda-forge/linux-64/mo_pack-0.2.0-py38h71d37f0_1007.tar.bz2#c8d3d8f137f8af7b1daca318131223b1
-https://conda.anaconda.org/conda-forge/linux-64/netcdf-fortran-4.6.0-mpi_mpich_hd09bd1e_0.tar.bz2#247c70ce54beeb3e60def44061576821
-https://conda.anaconda.org/conda-forge/linux-64/pandas-1.4.3-py38h47df419_0.tar.bz2#91c5ac3f8f0e55a946be7b9ce489abfe
-https://conda.anaconda.org/conda-forge/linux-64/pango-1.50.9-hc4f8a73_0.tar.bz2#b8e090dce29a036357552a009c770187
-https://conda.anaconda.org/conda-forge/linux-64/pytest-7.1.2-py38h578d9bd_0.tar.bz2#626d2b8f96c8c3d20198e6bd84d1cfb7
-https://conda.anaconda.org/conda-forge/linux-64/python-stratify-0.2.post0-py38h71d37f0_2.tar.bz2#cdef2f7b0e263e338016da4b77ae4c0b
-https://conda.anaconda.org/conda-forge/linux-64/pywavelets-1.3.0-py38h71d37f0_1.tar.bz2#704f1776af689de568514b0ff9dd0fbe
-https://conda.anaconda.org/conda-forge/linux-64/scipy-1.9.0-py38hea3f02b_0.tar.bz2#d19e23bb56b31d2504a0ff4d46b7aabc
-https://conda.anaconda.org/conda-forge/noarch/setuptools-scm-7.0.5-pyhd8ed1ab_0.tar.bz2#743074b7a216807886f7e8f6d497cceb
-https://conda.anaconda.org/conda-forge/linux-64/shapely-1.8.4-py38h3b45516_0.tar.bz2#d8621497bcc7b369ef9cce25d5a58aeb
-https://conda.anaconda.org/conda-forge/linux-64/sip-6.6.2-py38hfa26641_0.tar.bz2#b869c6b54a02c92fac8b10c0d9b32e43
+https://conda.anaconda.org/conda-forge/linux-64/mo_pack-0.2.0-py38h26c90d9_1008.tar.bz2#6bc8cd29312f4fc77156b78124e165cd
+https://conda.anaconda.org/conda-forge/linux-64/netcdf-fortran-4.6.0-mpi_mpich_hd09bd1e_1.tar.bz2#0b69750bb937cab0db14f6bcef6fd787
+https://conda.anaconda.org/conda-forge/linux-64/pandas-1.5.1-py38h8f669ce_1.tar.bz2#75f37fc81d6513ba5db1e05301671a2e
+https://conda.anaconda.org/conda-forge/linux-64/pango-1.50.11-h382ae3d_0.tar.bz2#509e3f89508398070d3bf7769d9e8b03
+https://conda.anaconda.org/conda-forge/noarch/pytest-7.2.0-pyhd8ed1ab_2.tar.bz2#ac82c7aebc282e6ac0450fca012ca78c
+https://conda.anaconda.org/conda-forge/linux-64/python-stratify-0.2.post0-py38h26c90d9_3.tar.bz2#6e7902b0e96f42fa1b73daa5f65dd669
+https://conda.anaconda.org/conda-forge/linux-64/pywavelets-1.3.0-py38h26c90d9_2.tar.bz2#d30399a3c636c75cfd3460c92effa960
+https://conda.anaconda.org/conda-forge/linux-64/scipy-1.9.3-py38h8ce737c_2.tar.bz2#dfd81898f0c6e9ee0c22305da6aa443e
+https://conda.anaconda.org/conda-forge/noarch/setuptools-scm-7.0.5-pyhd8ed1ab_1.tar.bz2#07037fe2931871ed69b2b3d2acd5fdc6
+https://conda.anaconda.org/conda-forge/linux-64/shapely-1.8.5-py38hafd38ec_2.tar.bz2#8df75c6a8c1deac4e99583ec624ff327
+https://conda.anaconda.org/conda-forge/linux-64/sip-6.7.4-py38hfa26641_0.tar.bz2#a39a1d696fbe108d705c0ac584b937e3
 https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-napoleon-0.7-py_0.tar.bz2#0bc25ff6f2e34af63ded59692df5f749
-https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.0.1-py38h43d8883_2.tar.bz2#3f6ce81c7d28563fe2af763d9ff43e62
-https://conda.anaconda.org/conda-forge/linux-64/cf-units-3.1.1-py38h71d37f0_0.tar.bz2#b9e7f6f7509496a4a62906d02dfe3128
+https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.0.1-py38h43d8883_3.tar.bz2#82b3797d08a43a101b645becbb938e65
+https://conda.anaconda.org/conda-forge/linux-64/cf-units-3.1.1-py38h26c90d9_2.tar.bz2#0ea017e84efe45badce6c32f274dbf8e
 https://conda.anaconda.org/conda-forge/linux-64/esmf-8.2.0-mpi_mpich_h5a1934d_102.tar.bz2#bb8bdfa5e3e9e3f6ec861f05cd2ad441
 https://conda.anaconda.org/conda-forge/linux-64/gtk2-2.24.33-h90689f9_2.tar.bz2#957a0255ab58aaf394a91725d73ab422
-https://conda.anaconda.org/conda-forge/noarch/identify-2.5.3-pyhd8ed1ab_0.tar.bz2#682f05a8e4b047ce4bdcec9d69c12551
-https://conda.anaconda.org/conda-forge/noarch/imagehash-4.2.1-pyhd8ed1ab_0.tar.bz2#01cc8698b6e1a124dc4f585516c27643
+https://conda.anaconda.org/conda-forge/noarch/identify-2.5.8-pyhd8ed1ab_0.tar.bz2#8001c46448f385fa43bc4221893704d2
+https://conda.anaconda.org/conda-forge/noarch/imagehash-4.3.1-pyhd8ed1ab_0.tar.bz2#132ad832787a2156be1f1b309835001a
 https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.54.4-h7abd40a_0.tar.bz2#921e53675ed5ea352f022b79abab076a
-https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.5.3-py38h38b5ce0_2.tar.bz2#0db5b110946be87a04643c1ba95c6ef9
-https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.6.0-nompi_py38h32db9c8_101.tar.bz2#d1451d40c8204594cdcf156363128000
-https://conda.anaconda.org/conda-forge/noarch/pyopenssl-22.0.0-pyhd8ed1ab_0.tar.bz2#1d7e241dfaf5475e893d4b824bb71b44
-https://conda.anaconda.org/conda-forge/linux-64/pyqt5-sip-12.11.0-py38hfa26641_0.tar.bz2#6ddbd9abb62e70243702c006b81c63e4
-https://conda.anaconda.org/conda-forge/noarch/pytest-forked-1.4.0-pyhd8ed1ab_0.tar.bz2#95286e05a617de9ebfe3246cecbfb72f
-https://conda.anaconda.org/conda-forge/linux-64/qt-main-5.15.4-ha5833f6_2.tar.bz2#dd3aa6715b9e9efaf842febf18ce4261
-https://conda.anaconda.org/conda-forge/linux-64/cartopy-0.20.3-py38h1816dc1_2.tar.bz2#0beb44c3333518cdbb4ccbf7913ff38a
+https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.6.2-py38hb021067_0.tar.bz2#72422499195d8aded0dfd461c6e3e86f
+https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.6.0-nompi_py38h2a9f00d_102.tar.bz2#533ae5db3e2367d71a7890efb0aa3cdc
+https://conda.anaconda.org/conda-forge/noarch/pyopenssl-22.1.0-pyhd8ed1ab_0.tar.bz2#fbfa0a180d48c800f922a10a114a8632
+https://conda.anaconda.org/conda-forge/linux-64/pyqt5-sip-12.11.0-py38hfa26641_2.tar.bz2#ad6437509a14f1e8e5b8a354f93f340c
+https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.0.2-pyhd8ed1ab_0.tar.bz2#18bdfe034d1187a34d860ed8e6fec788
+https://conda.anaconda.org/conda-forge/linux-64/qt-main-5.15.6-hd477bba_1.tar.bz2#738d009d60cd682df336b6d52c766190
+https://conda.anaconda.org/conda-forge/linux-64/cartopy-0.21.0-py38hf6c3373_3.tar.bz2#1dc477fef9b0b1080af3e7c7ecb4aff7
 https://conda.anaconda.org/conda-forge/linux-64/esmpy-8.2.0-mpi_mpich_py38h9147699_101.tar.bz2#5a9de1dec507b6614150a77d1aabf257
-https://conda.anaconda.org/conda-forge/linux-64/graphviz-5.0.1-h5abf519_0.tar.bz2#03f22ca50fcff4bbee39da0943ab8475
+https://conda.anaconda.org/conda-forge/linux-64/graphviz-6.0.1-h5abf519_0.tar.bz2#123c55da3e9ea8664f73c70e13ef08c2
 https://conda.anaconda.org/conda-forge/noarch/nc-time-axis-1.4.1-pyhd8ed1ab_0.tar.bz2#281b58948bf60a2582de9e548bcc5369
-https://conda.anaconda.org/conda-forge/linux-64/pre-commit-2.20.0-py38h578d9bd_0.tar.bz2#ac8aa845f1177901eecf1518997ea0a1
-https://conda.anaconda.org/conda-forge/linux-64/pyqt-5.15.7-py38h7492b6b_0.tar.bz2#59ece9f652baf50ee6b842db833896ae
-https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-2.5.0-pyhd8ed1ab_0.tar.bz2#1fdd1f3baccf0deb647385c677a1a48e
+https://conda.anaconda.org/conda-forge/linux-64/pre-commit-2.20.0-py38h578d9bd_1.tar.bz2#38d9029214399e4bfc378b62b0171bf0
+https://conda.anaconda.org/conda-forge/linux-64/pyqt-5.15.7-py38h7492b6b_2.tar.bz2#cfa725eff634872f90dcd5ebf8e8dc1a
 https://conda.anaconda.org/conda-forge/noarch/urllib3-1.26.11-pyhd8ed1ab_0.tar.bz2#0738978569b10669bdef41c671252dd1
-https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.5.3-py38h578d9bd_2.tar.bz2#3b6f187bade8a47d05c8a74c6385a900
-https://conda.anaconda.org/conda-forge/noarch/requests-2.28.1-pyhd8ed1ab_0.tar.bz2#70d6e72856de9551f83ae0f2de689a7a
+https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.6.2-py38h578d9bd_0.tar.bz2#e1a19f0d4686a701d4a4acce2b625acb
+https://conda.anaconda.org/conda-forge/noarch/requests-2.28.1-pyhd8ed1ab_1.tar.bz2#089382ee0e2dc2eae33a04cc3c2bddb0
 https://conda.anaconda.org/conda-forge/noarch/sphinx-4.5.0-pyh6c4a22f_0.tar.bz2#46b38d88c4270ff9ba78a89c83c66345
 https://conda.anaconda.org/conda-forge/noarch/pydata-sphinx-theme-0.8.1-pyhd8ed1ab_0.tar.bz2#7d8390ec71225ea9841b276552fdffba
 https://conda.anaconda.org/conda-forge/noarch/sphinx-copybutton-0.5.0-pyhd8ed1ab_0.tar.bz2#4c969cdd5191306c269490f7ff236d9c
diff --git a/requirements/ci/nox.lock/py39-linux-64.lock b/requirements/ci/nox.lock/py39-linux-64.lock
index 3cfe2b9a29..bec758d380 100644
--- a/requirements/ci/nox.lock/py39-linux-64.lock
+++ b/requirements/ci/nox.lock/py39-linux-64.lock
@@ -1,59 +1,61 @@
 # Generated by conda-lock.
 # platform: linux-64
-# input_hash: 87d5bb40e4218219f5c768306688f703396cce4593f26c2ff46d8adb198c9ae9
+# input_hash: 8a7c28a9309987aef78235650068290b0ecd5ac9e6ab522f4fad962cc9b1fc6c
 @EXPLICIT
 https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81
-https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2022.6.15-ha878542_0.tar.bz2#c320890f77fd1d617fa876e0982002c2
+https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2022.9.24-ha878542_0.tar.bz2#41e4e87062433e283696cf384f952ef6
 https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2#0c96522c6bdaed4b1566d11387caaf45
 https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6
 https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb
 https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-hab24e00_0.tar.bz2#19410c3df09dfb12d1206132a1d357c5
-https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.36.1-hea4e1c9_2.tar.bz2#bd4f2e711b39af170e7ff15163fe87ee
-https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-12.1.0-hdcd56e2_16.tar.bz2#b02605b875559ff99f04351fd5040760
-https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-12.1.0-ha89aaad_16.tar.bz2#6f5ba041a41eb102a1027d9e68731be7
+https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.39-hc81fddc_0.tar.bz2#c2719e2faa7bd7076d3a4b52271e5622
+https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-12.2.0-h337968e_19.tar.bz2#164b4b1acaedc47ee7e658ae6b308ca3
+https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-12.2.0-h46fd767_19.tar.bz2#1030b1f38c129f2634eae026f704fe60
 https://conda.anaconda.org/conda-forge/linux-64/mpi-1.0-mpich.tar.bz2#c1fcff3417b5a22bbc4cf6e8c23648cf
-https://conda.anaconda.org/conda-forge/noarch/tzdata-2022c-h191b570_0.tar.bz2#a56386ad31a7322940dd7d03fb3a9979
+https://conda.anaconda.org/conda-forge/noarch/tzdata-2022f-h191b570_0.tar.bz2#e366350e2343a798e29833286abe2560
 https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2#f766549260d6815b0c52253f1fb1bb29
-https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-12.1.0-h69a702a_16.tar.bz2#6bf15e29a20f614b18ae89368260d0a2
-https://conda.anaconda.org/conda-forge/linux-64/libgomp-12.1.0-h8d9b700_16.tar.bz2#f013cf7749536ce43d82afbffdf499ab
+https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-12.2.0-h69a702a_19.tar.bz2#cd7a806282c16e1f2d39a7e80d3a3e0d
+https://conda.anaconda.org/conda-forge/linux-64/libgomp-12.2.0-h65d4601_19.tar.bz2#cedcee7c064c01c403f962c9e8d3c373
 https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2#73aaf86a425cc6e73fcf236a5a46396d
 https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab
-https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-12.1.0-h8d9b700_16.tar.bz2#4f05bc9844f7c101e6e147dab3c88d5c
-https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.6.1-h7f98852_0.tar.bz2#0347ce6a34f8b55b544b141432c6d4c7
+https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-12.2.0-h65d4601_19.tar.bz2#e4c94f80aef025c17ab0828cd85ef535
+https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.8-h166bdaf_0.tar.bz2#be733e69048951df1e4b4b7bb8c7666f
 https://conda.anaconda.org/conda-forge/linux-64/attr-2.5.1-h166bdaf_1.tar.bz2#d9c69a24ad678ffce24c6543a0176b00
 https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-h7f98852_4.tar.bz2#a1fd65c7ccbf10880423d82bca54eb54
 https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.18.1-h7f98852_0.tar.bz2#f26ef8098fab1f719c91eb760d63381a
-https://conda.anaconda.org/conda-forge/linux-64/expat-2.4.8-h27087fc_0.tar.bz2#e1b07832504eeba765d648389cc387a9
-https://conda.anaconda.org/conda-forge/linux-64/fftw-3.3.10-nompi_ha7695d1_103.tar.bz2#a56c5033619bdf56a22a1f0a0fd286aa
+https://conda.anaconda.org/conda-forge/linux-64/expat-2.5.0-h27087fc_0.tar.bz2#c4fbad8d4bddeb3c085f18cbf97fbfad
+https://conda.anaconda.org/conda-forge/linux-64/fftw-3.3.10-nompi_hf0379b8_105.tar.bz2#9d3e01547ba04a57372beee01158096f
 https://conda.anaconda.org/conda-forge/linux-64/fribidi-1.0.10-h36c2ea0_0.tar.bz2#ac7bc6a654f8f41b352b38f4051135f8
-https://conda.anaconda.org/conda-forge/linux-64/geos-3.11.0-h27087fc_0.tar.bz2#a583d0bc9a85c48e8b07a588d1ac8a80
+https://conda.anaconda.org/conda-forge/linux-64/geos-3.11.1-h27087fc_0.tar.bz2#917b9a50001fffdd89b321b5dba31e55
+https://conda.anaconda.org/conda-forge/linux-64/gettext-0.21.1-h27087fc_0.tar.bz2#14947d8770185e5153fdd04d4673ed37
 https://conda.anaconda.org/conda-forge/linux-64/giflib-5.2.1-h36c2ea0_2.tar.bz2#626e68ae9cc5912d6adb79d318cf962d
 https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.13-h58526e2_1001.tar.bz2#8c54672728e8ec6aa6db90cf2806d220
 https://conda.anaconda.org/conda-forge/linux-64/icu-70.1-h27087fc_0.tar.bz2#87473a15119779e021c314249d4b4aed
 https://conda.anaconda.org/conda-forge/linux-64/jpeg-9e-h166bdaf_2.tar.bz2#ee8b844357a0946870901c7c6f418268
 https://conda.anaconda.org/conda-forge/linux-64/keyutils-1.6.1-h166bdaf_0.tar.bz2#30186d27e2c9fa62b45fb1476b7200e3
+https://conda.anaconda.org/conda-forge/linux-64/lame-3.100-h166bdaf_1003.tar.bz2#a8832b479f93521a9e7b5b743803be51
 https://conda.anaconda.org/conda-forge/linux-64/lerc-4.0.0-h27087fc_0.tar.bz2#76bbff344f0134279f225174e9064c8f
-https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.0.9-h166bdaf_7.tar.bz2#f82dc1c78bcf73583f2656433ce2933c
+https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.0.9-h166bdaf_8.tar.bz2#9194c9bf9428035a05352d031462eae4
 https://conda.anaconda.org/conda-forge/linux-64/libdb-6.2.32-h9c3ff4c_0.tar.bz2#3f3258d8f841fbac63b36b75bdac1afd
-https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.13-h166bdaf_0.tar.bz2#4b5bee2e957570197327d0b20a718891
+https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.14-h166bdaf_0.tar.bz2#fc84a0446e4e4fb882e78d786cfb9734
 https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-h516909a_1.tar.bz2#6f8720dff19e17ce5d48cfe7f3d2f0a3
 https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2#d645c6d2ac96843a2bfaccd2d62b3ac3
-https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.16-h516909a_0.tar.bz2#5c0f338a513a2943c659ae619fca9211
+https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.17-h166bdaf_0.tar.bz2#b62b52da46c39ee2bc3c162ac7f1804d
 https://conda.anaconda.org/conda-forge/linux-64/libmo_unpack-3.1.2-hf484d3e_1001.tar.bz2#95f32a6a5a666d33886ca5627239f03d
 https://conda.anaconda.org/conda-forge/linux-64/libnsl-2.0.0-h7f98852_0.tar.bz2#39b1328babf85c7c3a61636d9cd50206
 https://conda.anaconda.org/conda-forge/linux-64/libogg-1.3.4-h7f98852_1.tar.bz2#6e8cc2173440d77708196c5b93771680
-https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.21-pthreads_h78a6416_2.tar.bz2#839776c4e967bc881c21da197127a3ae
+https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.21-pthreads_h78a6416_3.tar.bz2#8c5963a49b6035c40646a763293fbb35
 https://conda.anaconda.org/conda-forge/linux-64/libopus-1.3.1-h7f98852_1.tar.bz2#15345e56d527b330e1cacbdf58676e8f
 https://conda.anaconda.org/conda-forge/linux-64/libtool-2.4.6-h9c3ff4c_1008.tar.bz2#16e143a1ed4b4fd169536373957f6fee
-https://conda.anaconda.org/conda-forge/linux-64/libudev1-249-h166bdaf_4.tar.bz2#dc075ff6fcb46b3d3c7652e543d5f334
+https://conda.anaconda.org/conda-forge/linux-64/libudev1-252-h166bdaf_0.tar.bz2#174243089ec111479298a5b7099b64b5
 https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.32.1-h7f98852_1000.tar.bz2#772d69f030955d9646d3d0eaf21d859d
 https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.2.4-h166bdaf_0.tar.bz2#ac2ccf7323d21f2994e4d1f5da664f37
-https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.2.12-h166bdaf_2.tar.bz2#8302381297332ea50532cf2c67961080
-https://conda.anaconda.org/conda-forge/linux-64/mpich-4.0.2-h846660c_100.tar.bz2#36a36fe04b932d4b327e7e81c5c43696
+https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.2.13-h166bdaf_4.tar.bz2#f3f9de449d32ca9b9c66a22863c96f41
+https://conda.anaconda.org/conda-forge/linux-64/mpg123-1.30.2-h27087fc_1.tar.bz2#2fe2a839394ef3a1825a5e5e296060bc
+https://conda.anaconda.org/conda-forge/linux-64/mpich-4.0.3-h846660c_100.tar.bz2#50d66bb751cfa71ee2a48b2d3eb90ac1
 https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.3-h27087fc_1.tar.bz2#4acfc691e64342b9dae57cf2adc63238
 https://conda.anaconda.org/conda-forge/linux-64/nspr-4.32-h9c3ff4c_1.tar.bz2#29ded371806431b0499aaee146abfc3e
-https://conda.anaconda.org/conda-forge/linux-64/openssl-1.1.1q-h166bdaf_0.tar.bz2#07acc367c7fc8b716770cd5b36d31717
-https://conda.anaconda.org/conda-forge/linux-64/pcre-8.45-h9c3ff4c_0.tar.bz2#c05d1820a6d34ff07aaaab7a9b7eddaa
+https://conda.anaconda.org/conda-forge/linux-64/openssl-1.1.1s-h166bdaf_0.tar.bz2#e17553617ce05787d97715177be014d1
 https://conda.anaconda.org/conda-forge/linux-64/pixman-0.40.0-h36c2ea0_0.tar.bz2#660e72c82f2e75a6b3fe6a6e75c79f19
 https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-h36c2ea0_1001.tar.bz2#22dad4df6e8630e8dff2428f6f6a7036
 https://conda.anaconda.org/conda-forge/linux-64/xorg-kbproto-1.0.7-h7f98852_1002.tar.bz2#4b230e8381279d76131116660f5a241a
@@ -66,61 +68,60 @@ https://conda.anaconda.org/conda-forge/linux-64/xorg-xproto-7.0.31-h7f98852_1007
 https://conda.anaconda.org/conda-forge/linux-64/xxhash-0.8.0-h7f98852_3.tar.bz2#52402c791f35e414e704b7a113f99605
 https://conda.anaconda.org/conda-forge/linux-64/xz-5.2.6-h166bdaf_0.tar.bz2#2161070d867d1b1204ea749c8eec4ef0
 https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2#4cb3ad778ec2d5a7acbdf254eb1c42ae
-https://conda.anaconda.org/conda-forge/linux-64/gettext-0.19.8.1-h73d1719_1008.tar.bz2#af49250eca8e139378f8ff0ae9e57251
-https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h9772cbc_4.tar.bz2#dd3e1941dd06f64cb88647d2f7ff8aaa
 https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-16_linux64_openblas.tar.bz2#d9b7a8639171f6c6fa0a983edabcfe2b
-https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.0.9-h166bdaf_7.tar.bz2#37a460703214d0d1b421e2a47eb5e6d0
-https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.0.9-h166bdaf_7.tar.bz2#785a9296ea478eb78c47593c4da6550f
-https://conda.anaconda.org/conda-forge/linux-64/libcap-2.64-ha37c62d_0.tar.bz2#5896fbd58d0376df8556a4aba1ce4f71
+https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.0.9-h166bdaf_8.tar.bz2#4ae4d7795d33e02bd20f6b23d91caf82
+https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.0.9-h166bdaf_8.tar.bz2#04bac51ba35ea023dc48af73c1c88c25
+https://conda.anaconda.org/conda-forge/linux-64/libcap-2.66-ha37c62d_0.tar.bz2#2d7665abd0997f1a6d4b7596bc27b657
 https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2#4d331e44109e3f0e19b4cb8f9b82f3e1
 https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.10-h9b69904_4.tar.bz2#390026683aef81db27ff1b8570ca1336
-https://conda.anaconda.org/conda-forge/linux-64/libllvm14-14.0.6-he0ac6c6_0.tar.bz2#f5759f0c80708fbf9c4836c0cb46d0fe
+https://conda.anaconda.org/conda-forge/linux-64/libflac-1.4.2-h27087fc_0.tar.bz2#7daf72d8e2a8e848e11d63ed6d1026e0
 https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.47.0-hdcd2b5c_1.tar.bz2#6fe9e31c2b8d0b022626ccac13e6ca3c
-https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.37-h753d276_4.tar.bz2#6b611734b73d639c084ac4be2fcd996a
-https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.39.2-h753d276_1.tar.bz2#90136dc0a305db4e1df24945d431457b
+https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.38-h753d276_0.tar.bz2#575078de1d3a3114b3ce131bd1508d0c
+https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.39.4-h753d276_0.tar.bz2#978924c298fc2215f129e8171bbea688
 https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.10.0-haa6b8db_3.tar.bz2#89acee135f0809a18a1f4537390aa2dd
 https://conda.anaconda.org/conda-forge/linux-64/libvorbis-1.3.7-h9c3ff4c_0.tar.bz2#309dec04b70a3cc0f1e84a4013683bc0
 https://conda.anaconda.org/conda-forge/linux-64/libxcb-1.13-h7f98852_1004.tar.bz2#b3653fdc58d03face9724f602218a904
-https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.9.14-h22db469_4.tar.bz2#aced7c1f4b4dbfea08e033c6ae97c53e
+https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.10.3-h7463322_0.tar.bz2#3b933ea47ef8f330c4c068af25fcd6a8
 https://conda.anaconda.org/conda-forge/linux-64/libzip-1.9.2-hc869a4a_1.tar.bz2#7a268cf1386d271e576e35ae82149ef2
-https://conda.anaconda.org/conda-forge/linux-64/mysql-common-8.0.30-haf5c9bc_0.tar.bz2#9d3e24b1157af09abe5a2589119c7b1d
-https://conda.anaconda.org/conda-forge/linux-64/portaudio-19.6.0-h57a0ea0_5.tar.bz2#5469312a373f481c05c380897fd7c923
+https://conda.anaconda.org/conda-forge/linux-64/mysql-common-8.0.31-haf5c9bc_0.tar.bz2#0249d755f8d26cb2ac796f9f01cfb823
+https://conda.anaconda.org/conda-forge/linux-64/pcre2-10.40-hc3806b6_0.tar.bz2#69e2c796349cd9b273890bee0febfe1b
 https://conda.anaconda.org/conda-forge/linux-64/readline-8.1.2-h0f457ee_0.tar.bz2#db2ebbe2943aae81ed051a6a9af8e0fa
 https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.12-h27826a3_0.tar.bz2#5b8c42eb62e9fc961af70bdd6a26e168
 https://conda.anaconda.org/conda-forge/linux-64/udunits2-2.2.28-hc3e0081_0.tar.bz2#d4c341e0379c31e9e781d4f204726867
 https://conda.anaconda.org/conda-forge/linux-64/xorg-libsm-1.2.3-hd9c2040_1000.tar.bz2#9e856f78d5c80d5a78f61e72d1d473a3
-https://conda.anaconda.org/conda-forge/linux-64/zlib-1.2.12-h166bdaf_2.tar.bz2#4533821485cde83ab12ff3d8bda83768
+https://conda.anaconda.org/conda-forge/linux-64/zlib-1.2.13-h166bdaf_4.tar.bz2#4b11e365c0275b808be78b30f904e295
 https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.2-h6239696_4.tar.bz2#adcf0be7897e73e312bd24353b613f74
-https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.0.9-h166bdaf_7.tar.bz2#1699c1211d56a23c66047524cd76796e
+https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.0.9-h166bdaf_8.tar.bz2#e5613f2bc717e9945840ff474419b8e4
 https://conda.anaconda.org/conda-forge/linux-64/freetype-2.12.1-hca18f0e_0.tar.bz2#4e54cbfc47b8c74c2ecc1e7730d8edce
+https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h9772cbc_5.tar.bz2#ee08782aff2ff9b3291c967fa6bc7336
 https://conda.anaconda.org/conda-forge/linux-64/krb5-1.19.3-h3790be6_0.tar.bz2#7d862b05445123144bec92cb1acc8ef8
 https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-16_linux64_openblas.tar.bz2#20bae26d0a1db73f758fc3754cab4719
-https://conda.anaconda.org/conda-forge/linux-64/libclang13-14.0.6-default_h3a83d3e_0.tar.bz2#cdbd49e0ab5c5a6c522acb8271977d4c
-https://conda.anaconda.org/conda-forge/linux-64/libflac-1.3.4-h27087fc_0.tar.bz2#620e52e160fd09eb8772dedd46bb19ef
-https://conda.anaconda.org/conda-forge/linux-64/libglib-2.72.1-h2d90d5f_0.tar.bz2#ebeadbb5fbc44052eeb6f96a2136e3c2
+https://conda.anaconda.org/conda-forge/linux-64/libglib-2.74.1-h606061b_1.tar.bz2#ed5349aa96776e00b34eccecf4a948fe
 https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-16_linux64_openblas.tar.bz2#955d993f41f9354bf753d29864ea20ad
-https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.4.0-h0e0dad5_3.tar.bz2#5627d42c13a9b117ae1701c6e195624f
+https://conda.anaconda.org/conda-forge/linux-64/libllvm15-15.0.4-h63197d8_1.tar.bz2#d1b93417274d24b751a831c21169669a
+https://conda.anaconda.org/conda-forge/linux-64/libsndfile-1.1.0-h27087fc_0.tar.bz2#02fa0b56a57c8421d1195bf0c021e682
+https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.4.0-h55922b4_4.tar.bz2#901791f0ec7cddc8714e76e273013a91
 https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.0.3-he3ba5ed_0.tar.bz2#f9dbabc7e01c459ed7a1d1d64b206e9b
-https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-8.0.30-h28c427c_0.tar.bz2#77f98ec0b224fd5ca8e7043e167efb83
-https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.39.2-h4ff8645_1.tar.bz2#2676ec698ce91567fca50654ac1b18ba
+https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-8.0.31-h28c427c_0.tar.bz2#455d44a05123f30f66af2ca2a9652b5f
+https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.39.4-h4ff8645_0.tar.bz2#643c271de2dd23ecbd107284426cebc2
 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-0.4.0-h166bdaf_0.tar.bz2#384e7fcb3cd162ba3e4aed4b687df566
 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-keysyms-0.4.0-h166bdaf_0.tar.bz2#637054603bb7594302e3bf83f0a99879
 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-renderutil-0.3.9-h166bdaf_0.tar.bz2#732e22f1741bccea861f5668cf7342a7
 https://conda.anaconda.org/conda-forge/linux-64/xcb-util-wm-0.4.1-h166bdaf_0.tar.bz2#0a8e20a8aef954390b9481a527421a8c
 https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.7.2-h7f98852_0.tar.bz2#12a61e640b8894504326aadafccbb790
-https://conda.anaconda.org/conda-forge/linux-64/atk-1.0-2.36.0-h3371d22_4.tar.bz2#661e1ed5d92552785d9f8c781ce68685
-https://conda.anaconda.org/conda-forge/linux-64/brotli-1.0.9-h166bdaf_7.tar.bz2#3889dec08a472eb0f423e5609c76bde1
+https://conda.anaconda.org/conda-forge/linux-64/atk-1.0-2.38.0-hd4edc92_1.tar.bz2#6c72ec3e660a51736913ef6ea68c454b
+https://conda.anaconda.org/conda-forge/linux-64/brotli-1.0.9-h166bdaf_8.tar.bz2#2ff08978892a3e8b954397c461f18418
 https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h5008d03_3.tar.bz2#ecfff944ba3960ecb334b9a2663d708d
-https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.14.0-h8e229c2_0.tar.bz2#f314f79031fec74adc9bff50fbaffd89
-https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.42.8-hff1cb4f_0.tar.bz2#908fc30f89e27817d835b45f865536d7
-https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.72.1-h6239696_0.tar.bz2#a3a99cc33279091262bbc4f5ee7c4571
+https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.14.1-hc2a2eb6_0.tar.bz2#78415f0180a8d9c5bcc47889e00d5fb1
+https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.42.8-hff1cb4f_1.tar.bz2#a61c6312192e7c9de71548a6706a21e6
+https://conda.anaconda.org/conda-forge/linux-64/glib-tools-2.74.1-h6239696_1.tar.bz2#5f442e6bc9d89ba236eb25a25c5c2815
 https://conda.anaconda.org/conda-forge/linux-64/gts-0.7.6-h64030ff_2.tar.bz2#112eb9b5b93f0c02e59aea4fd1967363
-https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.12-hddcbb42_0.tar.bz2#797117394a4aa588de6d741b06fad80f
-https://conda.anaconda.org/conda-forge/linux-64/libclang-14.0.6-default_h2e3cab8_0.tar.bz2#eb70548da697e50cefa7ba939d57d001
+https://conda.anaconda.org/conda-forge/linux-64/jack-1.9.21-he978b8e_1.tar.bz2#5cef21ebd70a90a0d28127543a8d3739
+https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.14-h6ed2654_0.tar.bz2#dcc588839de1445d90995a0a2c4f3a39
+https://conda.anaconda.org/conda-forge/linux-64/libclang13-15.0.4-default_h3a83d3e_0.tar.bz2#d60d2d91b5991e364fd95cba5ec8ab96
 https://conda.anaconda.org/conda-forge/linux-64/libcups-2.3.3-h3e49a29_2.tar.bz2#3b88f1d0fe2580594d58d7e44d664617
-https://conda.anaconda.org/conda-forge/linux-64/libcurl-7.83.1-h7bff187_0.tar.bz2#d0c278476dba3b29ee13203784672ab1
-https://conda.anaconda.org/conda-forge/linux-64/libpq-14.5-hd77ab85_0.tar.bz2#d3126b425a04ed2360da1e651cef1b2d
-https://conda.anaconda.org/conda-forge/linux-64/libsndfile-1.0.31-h9c3ff4c_1.tar.bz2#fc4b6d93da04731db7601f2a1b1dc96a
+https://conda.anaconda.org/conda-forge/linux-64/libcurl-7.86.0-h7bff187_1.tar.bz2#5e5f33d81f31598d87f9b849f73c30e3
+https://conda.anaconda.org/conda-forge/linux-64/libpq-14.5-hd77ab85_1.tar.bz2#f5c8135a70758d928a8126998a6558d8
 https://conda.anaconda.org/conda-forge/linux-64/libwebp-1.2.4-h522a892_0.tar.bz2#802e43f480122a85ae6a34c1909f8f98
 https://conda.anaconda.org/conda-forge/linux-64/nss-3.78-h2350873_0.tar.bz2#ab3df39f96742e6f1a9878b09274c1dc
 https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.5.0-h7d73246_1.tar.bz2#a11b4df9271a8d7917686725aa04c8f2
@@ -130,38 +131,42 @@ https://conda.anaconda.org/conda-forge/linux-64/xorg-libxext-1.3.4-h7f98852_1.ta
 https://conda.anaconda.org/conda-forge/linux-64/xorg-libxrender-0.9.10-h7f98852_1003.tar.bz2#f59c1242cc1dd93e72c2ee2b360979eb
 https://conda.anaconda.org/conda-forge/noarch/alabaster-0.7.12-py_0.tar.bz2#2489a97287f90176ecdc3ca982b4b0a0
 https://conda.anaconda.org/conda-forge/noarch/attrs-22.1.0-pyh71513ae_1.tar.bz2#6d3ccbc56256204925bfa8378722792f
-https://conda.anaconda.org/conda-forge/linux-64/cairo-1.16.0-ha61ee94_1012.tar.bz2#9604a7c93dd37bcb6d6cc8d6b64223a4
-https://conda.anaconda.org/conda-forge/noarch/certifi-2022.6.15-pyhd8ed1ab_1.tar.bz2#97349c8d67627cbf8f48d7e7e1773ea5
+https://conda.anaconda.org/conda-forge/linux-64/cairo-1.16.0-ha61ee94_1014.tar.bz2#d1a88f3ed5b52e1024b80d4bcd26a7a0
+https://conda.anaconda.org/conda-forge/noarch/certifi-2022.9.24-pyhd8ed1ab_0.tar.bz2#f66309b099374af91369e67e84af397d
 https://conda.anaconda.org/conda-forge/noarch/cfgv-3.3.1-pyhd8ed1ab_0.tar.bz2#ebb5f5f7dc4f1a3780ef7ea7738db08c
 https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-2.1.1-pyhd8ed1ab_0.tar.bz2#c1d5b294fbf9a795dec349a6f4d8be8e
-https://conda.anaconda.org/conda-forge/noarch/cloudpickle-2.1.0-pyhd8ed1ab_0.tar.bz2#f7551a8a008dfad2b7ac9662dd124614
-https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.5-pyhd8ed1ab_0.tar.bz2#c267da48ce208905d7d976d49dfd9433
-https://conda.anaconda.org/conda-forge/linux-64/curl-7.83.1-h7bff187_0.tar.bz2#ba33b9995f5e691e4f439422d6efafc7
+https://conda.anaconda.org/conda-forge/noarch/click-8.1.3-unix_pyhd8ed1ab_2.tar.bz2#20e4087407c7cb04a40817114b333dbf
+https://conda.anaconda.org/conda-forge/noarch/cloudpickle-2.2.0-pyhd8ed1ab_0.tar.bz2#a6cf47b09786423200d7982d1faa19eb
+https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_0.tar.bz2#3faab06a954c2a04039983f2c4a50d99
+https://conda.anaconda.org/conda-forge/linux-64/curl-7.86.0-h7bff187_1.tar.bz2#bc9567c50833f4b0d36b25caca7b34f8
 https://conda.anaconda.org/conda-forge/noarch/cycler-0.11.0-pyhd8ed1ab_0.tar.bz2#a50559fad0affdbb33729a68669ca1cb
-https://conda.anaconda.org/conda-forge/noarch/distlib-0.3.5-pyhd8ed1ab_0.tar.bz2#f15c3912378a07726093cc94d1e13251
+https://conda.anaconda.org/conda-forge/noarch/distlib-0.3.6-pyhd8ed1ab_0.tar.bz2#b65b4d50dbd2d50fa0aeac367ec9eed7
+https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.0.4-pyhd8ed1ab_0.tar.bz2#e0734d1f12de77f9daca98bda3428733
 https://conda.anaconda.org/conda-forge/noarch/execnet-1.9.0-pyhd8ed1ab_0.tar.bz2#0e521f7a5e60d508b121d38b04874fb2
 https://conda.anaconda.org/conda-forge/noarch/filelock-3.8.0-pyhd8ed1ab_0.tar.bz2#10f0218dbd493ab2e5dc6759ddea4526
-https://conda.anaconda.org/conda-forge/noarch/fsspec-2022.7.1-pyhd8ed1ab_0.tar.bz2#984db277dfb9ea04a584aea39c6a34e4
-https://conda.anaconda.org/conda-forge/linux-64/glib-2.72.1-h6239696_0.tar.bz2#1698b7684d3c6a4d1de2ab946f5b0fb5
+https://conda.anaconda.org/conda-forge/noarch/fsspec-2022.11.0-pyhd8ed1ab_0.tar.bz2#eb919f2119a6db5d0192f9e9c3711572
+https://conda.anaconda.org/conda-forge/linux-64/glib-2.74.1-h6239696_1.tar.bz2#f3220a9e9d3abcbfca43419a219df7e4
 https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.12.2-mpi_mpich_h08b82f9_0.tar.bz2#de601caacbaa828d845f758e07e3b85e
-https://conda.anaconda.org/conda-forge/noarch/idna-3.3-pyhd8ed1ab_0.tar.bz2#40b50b8b030f5f2f22085c062ed013dd
+https://conda.anaconda.org/conda-forge/noarch/idna-3.4-pyhd8ed1ab_0.tar.bz2#34272b248891bddccc64479f9a7fffed
 https://conda.anaconda.org/conda-forge/noarch/imagesize-1.4.1-pyhd8ed1ab_0.tar.bz2#7de5386c8fea29e76b303f37dde4c352
 https://conda.anaconda.org/conda-forge/noarch/iniconfig-1.1.1-pyh9f0ad1d_0.tar.bz2#39161f81cc5e5ca45b8226fbb06c6905
 https://conda.anaconda.org/conda-forge/noarch/iris-sample-data-2.4.0-pyhd8ed1ab_0.tar.bz2#18ee9c07cf945a33f92caf1ee3d23ad9
-https://conda.anaconda.org/conda-forge/linux-64/jack-1.9.18-h8c3723f_1002.tar.bz2#7b3f287fcb7683f67b3d953b79f412ea
+https://conda.anaconda.org/conda-forge/linux-64/libclang-15.0.4-default_h2e3cab8_0.tar.bz2#248a013d3e7248c5c5f4ff69ffc307e8
 https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-h18fbbfe_3.tar.bz2#ea9758cf553476ddf75c789fdd239dc5
 https://conda.anaconda.org/conda-forge/noarch/locket-1.0.0-pyhd8ed1ab_0.tar.bz2#91e27ef3d05cc772ce627e51cff111c4
 https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2#2ba8498c1018c1e9c61eb99b973dfe19
 https://conda.anaconda.org/conda-forge/noarch/platformdirs-2.5.2-pyhd8ed1ab_1.tar.bz2#2fb3f88922e7aec26ba652fcdfe13950
+https://conda.anaconda.org/conda-forge/noarch/pluggy-1.0.0-pyhd8ed1ab_5.tar.bz2#7d301a0d25f424d96175f810935f0da9
 https://conda.anaconda.org/conda-forge/noarch/ply-3.11-py_1.tar.bz2#7205635cd71531943440fbfe3b6b5727
-https://conda.anaconda.org/conda-forge/linux-64/proj-9.0.1-h93bde94_1.tar.bz2#8259528ea471b0963a91ce174f002e55
-https://conda.anaconda.org/conda-forge/noarch/py-1.11.0-pyh6c4a22f_0.tar.bz2#b4613d7e7a493916d867842a6a148054
+https://conda.anaconda.org/conda-forge/linux-64/proj-9.1.0-h93bde94_0.tar.bz2#255c7204dda39747c3ba380d28b026d7
+https://conda.anaconda.org/conda-forge/linux-64/pulseaudio-14.0-h0d2025b_11.tar.bz2#316026c5f80d8b5b9666e87b8614ac6c
 https://conda.anaconda.org/conda-forge/noarch/pycparser-2.21-pyhd8ed1ab_0.tar.bz2#076becd9e05608f8dc72757d5f3a91ff
 https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.0.9-pyhd8ed1ab_0.tar.bz2#e8fbc1b54b25f4b08281467bc13b70cc
 https://conda.anaconda.org/conda-forge/noarch/pyshp-2.3.1-pyhd8ed1ab_0.tar.bz2#92a889dc236a5197612bc85bee6d7174
+https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha2e5f31_6.tar.bz2#2a7de29fb590ca14b5243c4c812c8025
 https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.9-2_cp39.tar.bz2#39adde4247484de2bb4000122fdcf665
-https://conda.anaconda.org/conda-forge/noarch/pytz-2022.2.1-pyhd8ed1ab_0.tar.bz2#974bca71d00364630f63f31fa7e059cb
-https://conda.anaconda.org/conda-forge/noarch/setuptools-65.3.0-pyhd8ed1ab_1.tar.bz2#a64c8af7be7a6348c1d9e530f88fa4da
+https://conda.anaconda.org/conda-forge/noarch/pytz-2022.6-pyhd8ed1ab_0.tar.bz2#b1f26ad83328e486910ef7f6e81dc061
+https://conda.anaconda.org/conda-forge/noarch/setuptools-65.5.1-pyhd8ed1ab_0.tar.bz2#cfb8dc4d9d285ca5fb1177b9dd450e33
 https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2#e5f25f8dbc060e9a8d912e432202afc2
 https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-2.2.0-pyhd8ed1ab_0.tar.bz2#4d22a9315e78c6827f806065957d566e
 https://conda.anaconda.org/conda-forge/noarch/soupsieve-2.3.2.post1-pyhd8ed1ab_0.tar.bz2#146f4541d643d48fc8a75cacf69f03ae
@@ -174,83 +179,80 @@ https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-serializinghtml-1.1.
 https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_0.tar.bz2#f832c45a477c78bebd107098db465095
 https://conda.anaconda.org/conda-forge/noarch/tomli-2.0.1-pyhd8ed1ab_0.tar.bz2#5844808ffab9ebdb694585b50ba02a96
 https://conda.anaconda.org/conda-forge/noarch/toolz-0.12.0-pyhd8ed1ab_0.tar.bz2#92facfec94bc02d6ccf42e7173831a36
-https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.3.0-pyha770c72_0.tar.bz2#a9d85960bc62d53cc4ea0d1d27f73c98
-https://conda.anaconda.org/conda-forge/noarch/wheel-0.37.1-pyhd8ed1ab_0.tar.bz2#1ca02aaf78d9c70d9a81a3bed5752022
-https://conda.anaconda.org/conda-forge/noarch/zipp-3.8.1-pyhd8ed1ab_0.tar.bz2#a3508a0c850745b875de88aea4c40cc5
+https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.4.0-pyha770c72_0.tar.bz2#2d93b130d148d7fc77e583677792fc6a
+https://conda.anaconda.org/conda-forge/noarch/wheel-0.38.4-pyhd8ed1ab_0.tar.bz2#c829cfb8cb826acb9de0ac1a2df0a940
+https://conda.anaconda.org/conda-forge/noarch/zipp-3.10.0-pyhd8ed1ab_0.tar.bz2#cd4eb48ebde7de61f92252979aab515c
 https://conda.anaconda.org/conda-forge/linux-64/antlr-python-runtime-4.7.2-py39hf3d152e_1003.tar.bz2#5e8330e806e50bd6137ebd125f4bc1bb
-https://conda.anaconda.org/conda-forge/noarch/babel-2.10.3-pyhd8ed1ab_0.tar.bz2#72f1c6d03109d7a70087bc1d029a8eda
+https://conda.anaconda.org/conda-forge/noarch/babel-2.11.0-pyhd8ed1ab_0.tar.bz2#2ea70fde8d581ba9425a761609eed6ba
 https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.11.1-pyha770c72_0.tar.bz2#eeec8814bd97b2681f708bb127478d7d
-https://conda.anaconda.org/conda-forge/linux-64/cffi-1.15.1-py39he91dace_0.tar.bz2#61e961a94c8fd535e4496b17e7452dfe
-https://conda.anaconda.org/conda-forge/linux-64/docutils-0.16-py39hf3d152e_3.tar.bz2#4f0fa7459a1f40a969aaad418b1c428c
-https://conda.anaconda.org/conda-forge/linux-64/gstreamer-1.20.3-hd4edc92_0.tar.bz2#94cb81ffdce328f80c87ac9b01244632
-https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-5.1.0-hf9f4e7c_0.tar.bz2#7c1f73a8f7864a202b126d82e88ddffc
-https://conda.anaconda.org/conda-forge/linux-64/importlib-metadata-4.11.4-py39hf3d152e_0.tar.bz2#4c2a0eabf0b8980b2c755646a6f750eb
-https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.4-py39hf939315_0.tar.bz2#e8d1310648c189d6d11a2e13f73da1fe
-https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.8.1-mpi_mpich_h06c54e2_4.tar.bz2#491803a7356c6a668a84d71f491c4014
-https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.1-py39hb9d737c_1.tar.bz2#7cda413e43b252044a270c2477031c5c
-https://conda.anaconda.org/conda-forge/linux-64/mpi4py-3.1.3-py39h32b9844_2.tar.bz2#b809706525f081610469169b671b2600
+https://conda.anaconda.org/conda-forge/linux-64/cffi-1.15.1-py39he91dace_2.tar.bz2#fc70a133e8162f51e363cff3b6dc741c
+https://conda.anaconda.org/conda-forge/linux-64/docutils-0.17.1-py39hf3d152e_3.tar.bz2#3caf51fb6a259d377f05d6913193b11c
+https://conda.anaconda.org/conda-forge/linux-64/gstreamer-1.21.1-hd4edc92_1.tar.bz2#e604f83df3497fcdb6991ae58b73238f
+https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-5.3.0-h418a68e_0.tar.bz2#888056bd4b12e110b10d4d1f29161c5e
+https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-5.0.0-pyha770c72_1.tar.bz2#ec069c4db6a0ad84107bac5da62819d2
+https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.4-py39hf939315_1.tar.bz2#41679a052a8ce841c74df1ebc802e411
+https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.8.1-mpi_mpich_hcd871d9_6.tar.bz2#6cdc429ed22edb566ac4308f3da6916d
+https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.1-py39hb9d737c_2.tar.bz2#c678e07e7862b3157fb9f6d908233ffa
+https://conda.anaconda.org/conda-forge/linux-64/mpi4py-3.1.4-py39h32b9844_0.tar.bz2#b035b507f55bb6a967d86d4b7e059437
 https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.7.0-pyhd8ed1ab_0.tar.bz2#fbe1182f650c04513046d6894046cd6c
-https://conda.anaconda.org/conda-forge/linux-64/numpy-1.23.2-py39hba7629e_0.tar.bz2#25285f960f9c7f4e8ef56171af5e2a22
+https://conda.anaconda.org/conda-forge/linux-64/numpy-1.23.4-py39h3d75532_1.tar.bz2#ba4f1c0466d4ba7f53090c150fc88434
 https://conda.anaconda.org/conda-forge/noarch/packaging-21.3-pyhd8ed1ab_0.tar.bz2#71f1ab2de48613876becddd496371c85
 https://conda.anaconda.org/conda-forge/noarch/partd-1.3.0-pyhd8ed1ab_0.tar.bz2#af8c82d121e63082926062d61d9abb54
-https://conda.anaconda.org/conda-forge/linux-64/pillow-9.2.0-py39hd5dbb17_2.tar.bz2#3b74a959f6a8008f5901de60b3572c09
-https://conda.anaconda.org/conda-forge/noarch/pip-22.2.2-pyhd8ed1ab_0.tar.bz2#0b43abe4d3ee93e82742d37def53a836
-https://conda.anaconda.org/conda-forge/linux-64/pluggy-1.0.0-py39hf3d152e_3.tar.bz2#c375c89340e563053f3656c7f134d265
+https://conda.anaconda.org/conda-forge/linux-64/pillow-9.2.0-py39hf3a2cdf_3.tar.bz2#2bd111c38da69056e5fe25a51b832eba
+https://conda.anaconda.org/conda-forge/noarch/pip-22.3.1-pyhd8ed1ab_0.tar.bz2#da66f2851b9836d3a7c5190082a45f7d
 https://conda.anaconda.org/conda-forge/noarch/pockets-0.9.1-py_0.tar.bz2#1b52f0c42e8077e5a33e00fe72269364
-https://conda.anaconda.org/conda-forge/linux-64/psutil-5.9.1-py39hb9d737c_0.tar.bz2#5852c69cad74811dc3c95f9ab6a184ef
-https://conda.anaconda.org/conda-forge/linux-64/pulseaudio-14.0-h7f54b18_8.tar.bz2#f9dbcfbb942ec9a3c0249cb71da5c7d1
+https://conda.anaconda.org/conda-forge/linux-64/psutil-5.9.4-py39hb9d737c_0.tar.bz2#12184951da572828fb986b06ffb63eed
 https://conda.anaconda.org/conda-forge/noarch/pygments-2.13.0-pyhd8ed1ab_0.tar.bz2#9f478e8eedd301008b5f395bad0caaed
-https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.3.1-py39hdcf6798_1.tar.bz2#4edc329e5d60c4a1c1299cea60608d00
-https://conda.anaconda.org/conda-forge/linux-64/pysocks-1.7.1-py39hf3d152e_5.tar.bz2#d34b97a2386932b97c7cb80916a673e7
+https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.4.0-py39h14a8356_2.tar.bz2#5d93c781338ff274a0b3dc3d901e19a6
 https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.8.2-pyhd8ed1ab_0.tar.bz2#dd999d1cc9f79e67dbb855c8924c7984
-https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.0.0-py39hb9d737c_1.tar.bz2#9f71f72dad4fd7b9da7bcc2ba64505bc
-https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0-py39hb9d737c_4.tar.bz2#dcc47a3b751508507183d17e569805e5
-https://conda.anaconda.org/conda-forge/linux-64/tornado-6.2-py39hb9d737c_0.tar.bz2#a3c57360af28c0d9956622af99a521cd
-https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.3.0-hd8ed1ab_0.tar.bz2#f3e98e944832fb271a0dbda7b7771dc6
-https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-14.0.0-py39hb9d737c_1.tar.bz2#ef84376736d1e8a814ccb06d1d814e6f
-https://conda.anaconda.org/conda-forge/linux-64/virtualenv-20.16.3-py39hf3d152e_1.tar.bz2#baa79a28aa08de404d9deae634b91e03
-https://conda.anaconda.org/conda-forge/linux-64/brotlipy-0.7.0-py39hb9d737c_1004.tar.bz2#05a99367d885ec9990f25e74128a8a08
-https://conda.anaconda.org/conda-forge/linux-64/cftime-1.6.1-py39hd257fcd_0.tar.bz2#0911339f31c5fa644c312e4b3af95ea5
-https://conda.anaconda.org/conda-forge/linux-64/cryptography-37.0.4-py39hd97740a_0.tar.bz2#edc3668e7b71657237f94cf25e286478
-https://conda.anaconda.org/conda-forge/noarch/dask-core-2022.8.1-pyhd8ed1ab_0.tar.bz2#df5026dbf551bb992cdf247b08e11078
-https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.37.1-py39hb9d737c_0.tar.bz2#b006086e249cf6d88758bff9b462f971
-https://conda.anaconda.org/conda-forge/linux-64/gst-plugins-base-1.20.3-hf6a322e_0.tar.bz2#6ea2ce6265c3207876ef2369b7479f08
+https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.0.0-py39hb9d737c_2.tar.bz2#b643f1e19306b75a6013d77228156076
+https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0-py39hb9d737c_5.tar.bz2#ef9db3c38ae7275f6b14491cfe61a248
+https://conda.anaconda.org/conda-forge/linux-64/tornado-6.2-py39hb9d737c_1.tar.bz2#8a7d309b08cff6386fe384aa10dd3748
+https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.4.0-hd8ed1ab_0.tar.bz2#be969210b61b897775a0de63cd9e9026
+https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-15.0.0-py39hb9d737c_0.tar.bz2#230d65004135bf312504a1bbcb0c7a08
+https://conda.anaconda.org/conda-forge/linux-64/virtualenv-20.16.7-py39hf3d152e_0.tar.bz2#242b09f574d87f15a1d1479970037594
+https://conda.anaconda.org/conda-forge/linux-64/brotlipy-0.7.0-py39hb9d737c_1005.tar.bz2#a639fdd9428d8b25f8326a3838d54045
+https://conda.anaconda.org/conda-forge/linux-64/cftime-1.6.2-py39h2ae25f5_1.tar.bz2#c943fb9a2818ecc5be1e0ecc8b7738f1
+https://conda.anaconda.org/conda-forge/linux-64/contourpy-1.0.6-py39hf939315_0.tar.bz2#fb3f77fe25042c20c51974fcfe72f797
+https://conda.anaconda.org/conda-forge/linux-64/cryptography-38.0.3-py39hd97740a_0.tar.bz2#be40f2e5698bd0497ddee8a63f8fb4a6
+https://conda.anaconda.org/conda-forge/noarch/dask-core-2022.11.0-pyhd8ed1ab_0.tar.bz2#1ab924fb3c44ed49796cc85eb1315cdc
+https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.38.0-py39hb9d737c_1.tar.bz2#3f2d104f2fefdd5e8a205dd3aacbf1d7
+https://conda.anaconda.org/conda-forge/linux-64/gst-plugins-base-1.21.1-h3e40eee_1.tar.bz2#c03f4fca88373cf6c26d932c26e4ee20
 https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.2-pyhd8ed1ab_1.tar.bz2#c8490ed5c70966d232fdd389d0dbed37
-https://conda.anaconda.org/conda-forge/linux-64/mo_pack-0.2.0-py39hd257fcd_1007.tar.bz2#e7527bcf8da0dad996aaefd046c17480
-https://conda.anaconda.org/conda-forge/linux-64/netcdf-fortran-4.6.0-mpi_mpich_hd09bd1e_0.tar.bz2#247c70ce54beeb3e60def44061576821
-https://conda.anaconda.org/conda-forge/linux-64/pandas-1.4.3-py39h1832856_0.tar.bz2#74e00961703972cf33b44a6fca7c3d51
-https://conda.anaconda.org/conda-forge/linux-64/pango-1.50.9-hc4f8a73_0.tar.bz2#b8e090dce29a036357552a009c770187
-https://conda.anaconda.org/conda-forge/linux-64/pytest-7.1.2-py39hf3d152e_0.tar.bz2#a6bcf633d12aabdfc4cb32a09ebc0f31
-https://conda.anaconda.org/conda-forge/linux-64/python-stratify-0.2.post0-py39hd257fcd_2.tar.bz2#644be766007a1dc7590c3277647f81a1
-https://conda.anaconda.org/conda-forge/linux-64/pywavelets-1.3.0-py39hd257fcd_1.tar.bz2#c4b698994b2d8d2e659ae02202e6abe4
-https://conda.anaconda.org/conda-forge/linux-64/scipy-1.9.0-py39h8ba3f38_0.tar.bz2#b098a256777cb9e2605451f183c78768
-https://conda.anaconda.org/conda-forge/noarch/setuptools-scm-7.0.5-pyhd8ed1ab_0.tar.bz2#743074b7a216807886f7e8f6d497cceb
-https://conda.anaconda.org/conda-forge/linux-64/shapely-1.8.4-py39h68ae834_0.tar.bz2#e871ee7de5bfa95095256e95e30be2a6
-https://conda.anaconda.org/conda-forge/linux-64/sip-6.6.2-py39h5a03fae_0.tar.bz2#e37704c6be07b8b14ffc1ce912802ce0
+https://conda.anaconda.org/conda-forge/linux-64/mo_pack-0.2.0-py39h2ae25f5_1008.tar.bz2#d90acb3804f16c63eb6726652e4e25b3
+https://conda.anaconda.org/conda-forge/linux-64/netcdf-fortran-4.6.0-mpi_mpich_hd09bd1e_1.tar.bz2#0b69750bb937cab0db14f6bcef6fd787
+https://conda.anaconda.org/conda-forge/linux-64/pandas-1.5.1-py39h4661b88_1.tar.bz2#d541bbe75ce0f2679344ead981b2f858
+https://conda.anaconda.org/conda-forge/linux-64/pango-1.50.11-h382ae3d_0.tar.bz2#509e3f89508398070d3bf7769d9e8b03
+https://conda.anaconda.org/conda-forge/noarch/pytest-7.2.0-pyhd8ed1ab_2.tar.bz2#ac82c7aebc282e6ac0450fca012ca78c
+https://conda.anaconda.org/conda-forge/linux-64/python-stratify-0.2.post0-py39h2ae25f5_3.tar.bz2#bcc7de3bb458a198b598ac1f75bf37e3
+https://conda.anaconda.org/conda-forge/linux-64/pywavelets-1.3.0-py39h2ae25f5_2.tar.bz2#234ad9828eca1caf0f2fdcb4a24ad816
+https://conda.anaconda.org/conda-forge/linux-64/scipy-1.9.3-py39hddc5342_2.tar.bz2#0615ac8191c6ccf7d40860aff645f774
+https://conda.anaconda.org/conda-forge/noarch/setuptools-scm-7.0.5-pyhd8ed1ab_1.tar.bz2#07037fe2931871ed69b2b3d2acd5fdc6
+https://conda.anaconda.org/conda-forge/linux-64/shapely-1.8.5-py39h76a96b7_2.tar.bz2#10bea68a9dd064b703743d210e679408
+https://conda.anaconda.org/conda-forge/linux-64/sip-6.7.4-py39h5a03fae_0.tar.bz2#1d06fe7a83e1ac8340c7b483f6ee00e5
 https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-napoleon-0.7-py_0.tar.bz2#0bc25ff6f2e34af63ded59692df5f749
-https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.0.1-py39hf939315_2.tar.bz2#5a3bb9dc2fe08a4a6f2b61548a1431d6
-https://conda.anaconda.org/conda-forge/linux-64/cf-units-3.1.1-py39hd257fcd_0.tar.bz2#e0f1f1d3013be31359d3ac635b288469
+https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.0.1-py39hf939315_3.tar.bz2#0f11bcdf9669a5ae0f39efd8c830209a
+https://conda.anaconda.org/conda-forge/linux-64/cf-units-3.1.1-py39h2ae25f5_2.tar.bz2#b3b4aab96d1c4ed394d6f4b9146699d4
 https://conda.anaconda.org/conda-forge/linux-64/esmf-8.2.0-mpi_mpich_h5a1934d_102.tar.bz2#bb8bdfa5e3e9e3f6ec861f05cd2ad441
 https://conda.anaconda.org/conda-forge/linux-64/gtk2-2.24.33-h90689f9_2.tar.bz2#957a0255ab58aaf394a91725d73ab422
-https://conda.anaconda.org/conda-forge/noarch/identify-2.5.3-pyhd8ed1ab_0.tar.bz2#682f05a8e4b047ce4bdcec9d69c12551
-https://conda.anaconda.org/conda-forge/noarch/imagehash-4.2.1-pyhd8ed1ab_0.tar.bz2#01cc8698b6e1a124dc4f585516c27643
+https://conda.anaconda.org/conda-forge/noarch/identify-2.5.8-pyhd8ed1ab_0.tar.bz2#8001c46448f385fa43bc4221893704d2
+https://conda.anaconda.org/conda-forge/noarch/imagehash-4.3.1-pyhd8ed1ab_0.tar.bz2#132ad832787a2156be1f1b309835001a
 https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.54.4-h7abd40a_0.tar.bz2#921e53675ed5ea352f022b79abab076a
-https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.5.3-py39h19d6b11_2.tar.bz2#dc400bb297d8425b8b05367a21854b0b
-https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.6.0-nompi_py39h71b8e10_101.tar.bz2#91e01aa93a2bcca96c9d64d2ce4f65f0
-https://conda.anaconda.org/conda-forge/noarch/pyopenssl-22.0.0-pyhd8ed1ab_0.tar.bz2#1d7e241dfaf5475e893d4b824bb71b44
-https://conda.anaconda.org/conda-forge/linux-64/pyqt5-sip-12.11.0-py39h5a03fae_0.tar.bz2#1fd9112714d50ee5be3dbf4fd23964dc
-https://conda.anaconda.org/conda-forge/noarch/pytest-forked-1.4.0-pyhd8ed1ab_0.tar.bz2#95286e05a617de9ebfe3246cecbfb72f
-https://conda.anaconda.org/conda-forge/linux-64/qt-main-5.15.4-ha5833f6_2.tar.bz2#dd3aa6715b9e9efaf842febf18ce4261
-https://conda.anaconda.org/conda-forge/linux-64/cartopy-0.20.3-py39hed214b2_2.tar.bz2#12964abb0bdcb4abb3c680b359560c1b
+https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.6.2-py39hf9fd14e_0.tar.bz2#78ce32061e0be12deb8e0f11ffb76906
+https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.6.0-nompi_py39h6ced12a_102.tar.bz2#b92600d0fef7f12f426935d87d6413e6
+https://conda.anaconda.org/conda-forge/noarch/pyopenssl-22.1.0-pyhd8ed1ab_0.tar.bz2#fbfa0a180d48c800f922a10a114a8632
+https://conda.anaconda.org/conda-forge/linux-64/pyqt5-sip-12.11.0-py39h5a03fae_2.tar.bz2#306f1a018668f06a0bd89350a3f62c07
+https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-3.0.2-pyhd8ed1ab_0.tar.bz2#18bdfe034d1187a34d860ed8e6fec788
+https://conda.anaconda.org/conda-forge/linux-64/qt-main-5.15.6-hd477bba_1.tar.bz2#738d009d60cd682df336b6d52c766190
+https://conda.anaconda.org/conda-forge/linux-64/cartopy-0.21.0-py39h91bfd65_3.tar.bz2#7d10a2e14c08f383baae00e77bf890e5
 https://conda.anaconda.org/conda-forge/linux-64/esmpy-8.2.0-mpi_mpich_py39h8bb458d_101.tar.bz2#347f324dd99dfb0b1479a466213b55bf
-https://conda.anaconda.org/conda-forge/linux-64/graphviz-5.0.1-h5abf519_0.tar.bz2#03f22ca50fcff4bbee39da0943ab8475
+https://conda.anaconda.org/conda-forge/linux-64/graphviz-6.0.1-h5abf519_0.tar.bz2#123c55da3e9ea8664f73c70e13ef08c2
 https://conda.anaconda.org/conda-forge/noarch/nc-time-axis-1.4.1-pyhd8ed1ab_0.tar.bz2#281b58948bf60a2582de9e548bcc5369
-https://conda.anaconda.org/conda-forge/linux-64/pre-commit-2.20.0-py39hf3d152e_0.tar.bz2#314c8cb1538706f62ec36cf64370f2b2
-https://conda.anaconda.org/conda-forge/linux-64/pyqt-5.15.7-py39h18e9c17_0.tar.bz2#5ed8f83afff3b64fa91f7a6af8d7ff04
-https://conda.anaconda.org/conda-forge/noarch/pytest-xdist-2.5.0-pyhd8ed1ab_0.tar.bz2#1fdd1f3baccf0deb647385c677a1a48e
+https://conda.anaconda.org/conda-forge/linux-64/pre-commit-2.20.0-py39hf3d152e_1.tar.bz2#921f8a7c2a16d18d7168fdac88b2adfe
+https://conda.anaconda.org/conda-forge/linux-64/pyqt-5.15.7-py39h18e9c17_2.tar.bz2#384809c51fb2adc04773f6fa097cd051
 https://conda.anaconda.org/conda-forge/noarch/urllib3-1.26.11-pyhd8ed1ab_0.tar.bz2#0738978569b10669bdef41c671252dd1
-https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.5.3-py39hf3d152e_2.tar.bz2#98bf9bdfbac2ac73bbd1dc12a61519eb
-https://conda.anaconda.org/conda-forge/noarch/requests-2.28.1-pyhd8ed1ab_0.tar.bz2#70d6e72856de9551f83ae0f2de689a7a
+https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.6.2-py39hf3d152e_0.tar.bz2#03225b4745d1dee7bb19d81e41c773a0
+https://conda.anaconda.org/conda-forge/noarch/requests-2.28.1-pyhd8ed1ab_1.tar.bz2#089382ee0e2dc2eae33a04cc3c2bddb0
 https://conda.anaconda.org/conda-forge/noarch/sphinx-4.5.0-pyh6c4a22f_0.tar.bz2#46b38d88c4270ff9ba78a89c83c66345
 https://conda.anaconda.org/conda-forge/noarch/pydata-sphinx-theme-0.8.1-pyhd8ed1ab_0.tar.bz2#7d8390ec71225ea9841b276552fdffba
 https://conda.anaconda.org/conda-forge/noarch/sphinx-copybutton-0.5.0-pyhd8ed1ab_0.tar.bz2#4c969cdd5191306c269490f7ff236d9c
diff --git a/requirements/ci/py310.yml b/requirements/ci/py310.yml
index 8f730729b7..87a16ba18a 100644
--- a/requirements/ci/py310.yml
+++ b/requirements/ci/py310.yml
@@ -11,12 +11,12 @@ dependencies:
   - setuptools-scm >=7
 
 # Core dependencies.
-  - cartopy >=0.20
+  - cartopy >=0.21
   - cf-units >=3.1
   - cftime >=1.5
   - dask-core >=2.26
-  - matplotlib
-  - netcdf4
+  - matplotlib >=3.5
+  - netcdf4 !=1.6.1
   - numpy >=1.19
   - python-xxhash
   - pyproj
@@ -49,3 +49,7 @@ dependencies:
   - sphinx-gallery >=0.11.0
   - sphinx-panels
   - pydata-sphinx-theme = 0.8.1
+
+# Temporary minimum pins.
+# See https://github.com/SciTools/iris/pull/5051
+  - graphviz >=6.0.0
diff --git a/requirements/ci/py38.yml b/requirements/ci/py38.yml
index d92a68076c..e2d696d30c 100644
--- a/requirements/ci/py38.yml
+++ b/requirements/ci/py38.yml
@@ -11,12 +11,12 @@ dependencies:
   - setuptools-scm >=7
 
 # Core dependencies.
-  - cartopy >=0.20
+  - cartopy >=0.21
   - cf-units >=3.1
   - cftime >=1.5
   - dask-core >=2.26
-  - matplotlib
-  - netcdf4
+  - matplotlib >=3.5
+  - netcdf4 !=1.6.1
   - numpy >=1.19
   - python-xxhash
   - pyproj
@@ -49,3 +49,7 @@ dependencies:
   - sphinx-gallery >=0.11.0
   - sphinx-panels
   - pydata-sphinx-theme = 0.8.1
+
+# Temporary minimum pins.
+# See https://github.com/SciTools/iris/pull/5051
+  - graphviz >=6.0.0
diff --git a/requirements/ci/py39.yml b/requirements/ci/py39.yml
index 001d3565d5..500ec2f80f 100644
--- a/requirements/ci/py39.yml
+++ b/requirements/ci/py39.yml
@@ -11,12 +11,12 @@ dependencies:
   - setuptools-scm >=7
 
 # Core dependencies.
-  - cartopy >=0.20
+  - cartopy >=0.21
   - cf-units >=3.1
   - cftime >=1.5
   - dask-core >=2.26
-  - matplotlib
-  - netcdf4
+  - matplotlib >=3.5
+  - netcdf4 !=1.6.1
   - numpy >=1.19
   - python-xxhash
   - pyproj
@@ -49,3 +49,7 @@ dependencies:
   - sphinx-gallery >=0.11.0
   - sphinx-panels
   - pydata-sphinx-theme = 0.8.1
+
+# Temporary minimum pins.
+# See https://github.com/SciTools/iris/pull/5051
+  - graphviz >=6.0.0
diff --git a/setup.cfg b/setup.cfg
index 8166f88f59..ca35a8eb4e 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
 [metadata]
 author = SciTools Developers
-author_email = scitools-iris-dev@googlegroups.com
+author_email = scitools.pub@gmail.com
 classifiers =
     Development Status :: 5 - Production/Stable
     Intended Audience :: Science/Research
@@ -12,6 +12,8 @@ classifiers =
     Programming Language :: Python
     Programming Language :: Python :: 3 :: Only
     Programming Language :: Python :: 3.8
+    Programming Language :: Python :: 3.9
+    Programming Language :: Python :: 3.10
     Programming Language :: Python :: Implementation :: CPython
     Topic :: Scientific/Engineering
     Topic :: Scientific/Engineering :: Atmospheric Science
@@ -45,12 +47,12 @@ version = attr: iris.__version__
 [options]
 include_package_data = True
 install_requires =
-    cartopy>=0.20
+    cartopy>=0.21
     cf-units>=3.1
     cftime>=1.5.0
     dask[array]>=2.26
-    matplotlib
-    netcdf4
+    matplotlib>=3.5
+    netcdf4!=1.6.1
     numpy>=1.19
     scipy
     shapely!=1.8.3
diff --git a/setup.py b/setup.py
index c7438ee698..061b35c262 100644
--- a/setup.py
+++ b/setup.py
@@ -1,42 +1,16 @@
-from contextlib import contextmanager
 import os
-from shutil import copyfile
 import sys
 
 from setuptools import Command, setup
 from setuptools.command.build_py import build_py
-from setuptools.command.develop import develop as develop_cmd
-
-
-@contextmanager
-def temporary_path(directory):
-    """
-    Context manager that adds and subsequently removes the given directory
-    to sys.path
-
-    """
-    sys.path.insert(0, directory)
-    try:
-        yield
-    finally:
-        del sys.path[0]
-
-
-# Add full path so Python doesn't load any __init__.py in the intervening
-# directories, thereby saving setup.py from additional dependencies.
-with temporary_path("lib/iris/tests/runner"):
-    from _runner import TestRunner  # noqa:
-
-
-class SetupTestRunner(TestRunner, Command):
-    pass
+from setuptools.command.develop import develop
 
 
 class BaseCommand(Command):
-    """A valid no-op command for setuptools & distutils."""
+    """A minimal no-op setuptools command."""
 
-    description = "A no-op command."
-    user_options = []
+    description: str = "A no-op command."
+    user_options: list = []
 
     def initialize_options(self):
         pass
@@ -48,75 +22,65 @@ def run(self):
         pass
 
 
-class CleanSource(BaseCommand):
-    description = "clean orphaned pyc/pyo files from the source directory"
-
-    def run(self):
-        for root_path, dir_names, file_names in os.walk("lib"):
-            for file_name in file_names:
-                if file_name.endswith("pyc") or file_name.endswith("pyo"):
-                    compiled_path = os.path.join(root_path, file_name)
-                    source_path = compiled_path[:-1]
-                    if not os.path.exists(source_path):
-                        print("Cleaning", compiled_path)
-                        os.remove(compiled_path)
-
-
-def copy_copyright(cmd, directory):
-    # Copy the COPYRIGHT information into the package root
-    iris_build_dir = os.path.join(directory, "iris")
-    for fname in ["COPYING", "COPYING.LESSER"]:
-        copyfile(fname, os.path.join(iris_build_dir, fname))
-
+def custom_command(cmd, help=""):
+    """
+    Factory function to generate a custom command that adds additional
+    behaviour to build the CF standard names module.
 
-def build_std_names(cmd, directory):
-    # Call out to tools/generate_std_names.py to build std_names module.
+    """
 
-    script_path = os.path.join("tools", "generate_std_names.py")
-    xml_path = os.path.join("etc", "cf-standard-name-table.xml")
-    module_path = os.path.join(directory, "iris", "std_names.py")
-    args = (sys.executable, script_path, xml_path, module_path)
-    cmd.spawn(args)
+    class CustomCommand(cmd):
+        description = help or cmd.description
 
+        def _build_std_names(self, directory):
+            # Call out to tools/generate_std_names.py to build std_names module.
 
-def custom_cmd(command_to_override, functions, help_doc=""):
-    """
-    Allows command specialisation to include calls to the given functions.
+            script_path = os.path.join("tools", "generate_std_names.py")
+            xml_path = os.path.join("etc", "cf-standard-name-table.xml")
+            module_path = os.path.join(directory, "iris", "std_names.py")
+            args = (sys.executable, script_path, xml_path, module_path)
+            self.spawn(args)
 
-    """
+        def finalize_options(self):
+            # Execute the parent "cmd" class method.
+            cmd.finalize_options(self)
 
-    class ExtendedCommand(command_to_override):
-        description = help_doc or command_to_override.description
+            if (
+                not hasattr(self, "editable_mode")
+                or self.editable_mode is None
+            ):
+                # Default to editable i.e., applicable to "std_names" and
+                # and "develop" commands.
+                self.editable_mode = True
 
         def run(self):
-            # Run the original command first to make sure all the target
-            # directories are in place.
-            command_to_override.run(self)
+            # Execute the parent "cmd" class method.
+            cmd.run(self)
 
+            # Determine the target root directory
             if self.editable_mode:
-                print(" [Running in-place]")
                 # Pick the source dir instead (currently in the sub-dir "lib").
-                dest = "lib"
+                target = "lib"
+                msg = "in-place"
             else:
                 # Not editable - must be building.
-                dest = self.build_lib
+                target = self.build_lib
+                msg = "as-build"
+
+            print(f"\n[Running {msg}]")
 
-            for func in functions:
-                func(self, dest)
+            # Build the CF standard names.
+            self._build_std_names(target)
 
-    return ExtendedCommand
+    return CustomCommand
 
 
 custom_commands = {
-    "test": SetupTestRunner,
-    "develop": custom_cmd(develop_cmd, [build_std_names]),
-    "build_py": custom_cmd(build_py, [build_std_names, copy_copyright]),
-    "std_names": custom_cmd(
-        BaseCommand,
-        [build_std_names],
-        help_doc="generate CF standard name module",
+    "develop": custom_command(develop),
+    "build_py": custom_command(build_py),
+    "std_names": custom_command(
+        BaseCommand, help="generate CF standard names"
     ),
-    "clean_source": CleanSource,
 }
 
 
diff --git a/tools/update_lockfiles.py b/tools/update_lockfiles.py
index f05210be87..dc898784ae 100755
--- a/tools/update_lockfiles.py
+++ b/tools/update_lockfiles.py
@@ -17,6 +17,15 @@
 from pathlib import Path
 import subprocess
 import sys
+from warnings import warn
+
+
+message = (
+    "Iris' large requirements may require Mamba to successfully solve. If you "
+    "don't want to install Mamba, consider using the workflow_dispatch on "
+    "Iris' GitHub action."
+)
+warn(message)
 
 
 try:
@@ -29,9 +38,9 @@
     "Iris Lockfile Generator",
 )
 
-parser.add_argument('files', nargs='+', 
+parser.add_argument('files', nargs='+',
     help="List of environment.yml files to lock")
-parser.add_argument('--output-dir', '-o', default='.', 
+parser.add_argument('--output-dir', '-o', default='.',
     help="Directory to save output lock files")
 
 args = parser.parse_args()
@@ -43,7 +52,7 @@
     ftype = fname.split('.')[-1]
     if ftype.lower() in ('yaml', 'yml'):
         fname = '.'.join(fname.split('.')[:-1])
-    
+
     # conda-lock --filename-template expects a string with a "...{platform}..."
     # placeholder in it, so we have to build the .lock filname without
     # using .format


Test patch:
diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml
index aae1ce2970..cee98dc33d 100644
--- a/.github/workflows/ci-tests.yml
+++ b/.github/workflows/ci-tests.yml
@@ -46,7 +46,7 @@ jobs:
             session: "tests"
 
     env:
-      IRIS_TEST_DATA_VERSION: "2.15"
+      IRIS_TEST_DATA_VERSION: "2.18"
       ENV_NAME: "ci-tests"
 
     steps:
diff --git a/docs/src/whatsnew/latest.rst b/docs/src/whatsnew/latest.rst
index 8ea27d7667..bfce8bf04c 100644
--- a/docs/src/whatsnew/latest.rst
+++ b/docs/src/whatsnew/latest.rst
@@ -25,7 +25,16 @@ This document explains the changes made to Iris for this release
 📢 Announcements
 ================
 
-#. Welcome to `@ESadek-MO`_ and `@hsteptoe`_ who made their first contribution to Iris 🎉
+#. Welcome to `@ESadek-MO`_, `@TTV-Intrepid`_ and @`hsteptoe`_,  who made their
+   first contributions to Iris 🎉
+
+   .. _try_experimental_stratify:
+
+#. Do you enjoy `python-stratify`_? Did you know that Iris includes a
+   convenience for using `python-stratify`_ with :class:`~iris.cube.Cube`\s?
+   It has been 'experimental' for several years now, without receiving much
+   feedback, so it's **use it or lose it** time: please try out
+   :mod:`iris.experimental.stratify` and let us know what you think!
 
 
 ✨ Features
@@ -35,6 +44,36 @@ This document explains the changes made to Iris for this release
    non-existing paths, and added expansion functionality to :func:`~iris.io.save`.
    (:issue:`4772`, :pull:`4913`)
 
+#. `@trexfeathers`_ and `Julian Heming`_ added new mappings between CF
+   standard names and UK Met Office LBFC codes. (:pull:`4859`)
+
+#. `@pp-mo`_ changed the metadata of a face/edge-type
+   :class:`~iris.experimental.ugrid.mesh.MeshCoord`, to be same as the face/edge
+   coordinate in the mesh from which it takes its ``.points``.  Previously, all MeshCoords
+   took their metadata from the node coord, but only a node-type MeshCoord now does
+   that.  Also, the MeshCoord ``.var_name`` is now that of the underlying coord, whereas
+   previously this was always None.  These changes make MeshCoord more like an ordinary
+   :class:`~iris.coords.AuxCoord`, which avoids some specific known usage problems.
+   (:issue:`4860`, :pull:`5020`)
+
+#. `@Esadek-MO`_ and `@trexfeathers`_ added dim coord
+   prioritisation to ``_get_lon_lat_coords()`` in :mod:`iris.analysis.cartography`.
+   This allows :func:`iris.analysis.cartography.area_weights` and
+   :func:`~iris.analysis.cartography.project` to handle cubes which contain
+   both dim and aux coords of the same type e.g. ``longitude`` and ``grid_longitude``.
+   (:issue:`3916`, :pull:`5029`).
+
+#. `@stephenworsley`_ added the ability to regrid derived coordinates with the
+   :obj:`~iris.analysis.PointInCell` regridding scheme. (:pull:`4807`)
+
+#. `@trexfeathers`_ made NetCDF loading more tolerant by enabling skipping of
+   :class:`~iris.coords.DimCoord`\s, :class:`~iris.coords.AuxCoord`\s,
+   :class:`~iris.coords.CellMeasure`\s and
+   :class:`~iris.coords.AncillaryVariable`\s if they cannot be added to a
+   :class:`~iris.cube.Cube` (e.g. due to CF non-compliance). This is done via
+   a new error class: :class:`~iris.exceptions.CannotAddError` (subclass of
+   :class:`ValueError`). (:pull:`5054`)
+
 #. `@hsteptoe`_ and `@trexfeathers`_ improved
    :func:`iris.pandas.as_data_frame`\'s conversion of :class:`~iris.cube.Cube`\s to
    :class:`~pandas.DataFrame`\s. This includes better handling of multiple
@@ -55,11 +94,58 @@ This document explains the changes made to Iris for this release
    differs from 0 or 2. This enables the use of this method on mesh
    coordinates. (:issue:`4672`, :pull:`4870`)
 
+#. `@bjlittle`_ and `@lbdreyer`_ (reviewer) fixed the building of the CF
+   Standard Names module ``iris.std_names`` for the ``setup.py`` commands
+   ``develop`` and ``std_names``. (:issue:`4951`, :pull:`4952`)
+
+#. `@lbdreyer`_ and `@pp-mo`_ (reviewer) fixed the cube print out such that
+   scalar ancillary variables are displayed in a dedicated section rather than
+   being added to the vector ancillary variables section. Further, ancillary
+   variables and cell measures that map to a cube dimension of length 1 are now
+   included in the respective vector sections. (:pull:`4945`)
+
+#. `@rcomer`_ removed some old redundant code that prevented determining the
+   order of time cells. (:issue:`4697`, :pull:`4729`)
+
+#. `@stephenworsley`_ improved the accuracy of the error messages for
+   :meth:`~iris.cube.Cube.coord` when failing to find coordinates in the case where
+   a coordinate is given as the argument. Similarly, improved the error messages for
+   :meth:`~iris.cube.Cube.cell_measure` and :meth:`~iris.cube.Cube.ancillary_variable`.
+   (:issue:`4898`, :pull:`4928`)
+
+#. `@stephenworsley`_ fixed a bug which caused derived coordinates to be realised
+   after calling :meth:`iris.cube.Cube.aggregated_by`. (:issue:`3637`, :pull:`4947`)
+
+#. `@rcomer`_ corrected the ``standard_name`` mapping from UM stash code ``m01s30i311``
+   to indicate that this is the upward, rather than northward part of the flow.
+   (:pull:`5060`)
+
+#. `@bjlittle`_ and `@trexfeathers`_ (reviewer) fixed an issue which prevented
+   uncompressed PP fields with additional trailing padded words in the field
+   data to be loaded and saved. (:pull:`5058`)
+
+#. `@lbdreyer`_ and `@trexfeathers`_ (reviewer) fixed the handling of data when
+   regridding with :class:`~iris.analysis.UnstructuredNearest` or calling
+   :func:`~iris.analysis.trajectory.interpolate` such that the data type and mask is
+   preserved. (:issue:`4463`, :pull:`5062`)
+
 
 💣 Incompatible Changes
 =======================
 
-#. N/A
+#. `@trexfeathers`_ altered testing to accept new Dask copying behaviour from
+   `dask/dask#9555`_ - copies of a Dask array created using ``da.from_array()``
+   will all ``compute()`` to a shared identical array. So creating a
+   :class:`~iris.cube.Cube` using ``Cube(data=da.from_array(...``, then
+   using :class:`~iris.cube.Cube` :meth:`~iris.cube.Cube.copy`,
+   will produce two :class:`~iris.cube.Cube`\s that both return an identical
+   array when requesting :class:`~iris.cube.Cube` :attr:`~iris.cube.Cube.data`.
+   We do not expect this to affect typical user workflows but please get in
+   touch if you need help. (:pull:`5041`)
+
+#. `@trexfeathers`_ moved ``iris.experimental.animate.animate()`` to
+   :func:`iris.plot.animate`, in recognition of its successful use over several
+   years since introduction. (:pull:`5056`)
 
 
 🚀 Performance Enhancements
@@ -69,6 +155,18 @@ This document explains the changes made to Iris for this release
    :obj:`~iris.analysis.SUM`, :obj:`~iris.analysis.COUNT` and
    :obj:`~iris.analysis.PROPORTION` on real data. (:pull:`4905`)
 
+#. `@bouweandela`_ made :meth:`iris.coords.Coord.cells` faster for time
+   coordinates. This also affects :meth:`iris.cube.Cube.extract`,
+   :meth:`iris.cube.Cube.subset`, and :meth:`iris.coords.Coord.intersect`.
+   (:pull:`4969`)
+
+#. `@bouweandela`_ improved the speed of :meth:`iris.cube.Cube.subset` /
+   :meth:`iris.coords.Coord.intersect`.
+   (:pull:`4955`)
+
+#. `@stephenworsley`_ improved the speed of the :obj:`~iris.analysis.PointInCell`
+   regridding scheme. (:pull:`4807`)
+
 
 🔥 Deprecations
 ===============
@@ -85,32 +183,85 @@ This document explains the changes made to Iris for this release
 #. `@rcomer`_ introduced the ``dask >=2.26`` minimum pin, so that Iris can benefit
    from Dask's support for `NEP13`_ and `NEP18`_. (:pull:`4905`)
 
+#. `@trexfeathers`_ advanced the Cartopy pin to ``>=0.21``, as Cartopy's
+   change to default Transverse Mercator projection affects an Iris test.
+   See `SciTools/cartopy@fcb784d`_ and `SciTools/cartopy@8860a81`_ for more
+   details.
+   (:pull:`4968`)
+
+#. `@trexfeathers`_ introduced the ``netcdf4!=1.6.1`` pin to avoid a problem
+   with segfaults. (:pull:`4968`)
+
+#. `@trexfeathers`_ updated the Matplotlib colormap registration in
+   :mod:`iris.palette` in response to a deprecation warning. Using the new
+   Matplotlib API also means a ``matplotlib>=3.5`` pin. (:pull:`4998`)
+
+#. See `💣 Incompatible Changes`_ for notes about `dask/dask#9555`_.
+
 
 📚 Documentation
 ================
 
-#. N/A
+#. `@ESadek-MO`_, `@TTV-Intrepid`_ and `@trexfeathers`_ added a gallery example for zonal
+   means plotted parallel to a cartographic plot. (:pull:`4871`)
+
+#. `@Esadek-MO`_ added a key-terms :ref:`glossary` page into the user guide. (:pull:`4902`)
+
+#. `@pp-mo`_ added a :ref:`code example <ORCA_example>`
+   for converting ORCA-gridded data to an unstructured cube. (:pull:`5013`)
+#. `@Esadek-MO`_ added links to relevant Gallery examples within the User Guide
+   to improve understanding. (:pull:`5009`)
+
+#. `@trexfeathers`_ changed the warning header for the **latest** documentation
+   to reference Read the Docs' built-in version switcher, instead of generating
+   its own independent links. (:pull:`5055`)
 
 #. `@tkknight`_ updated the links for the Iris documentation to v2.4 and
    earlier to point to the archive of zip files instead. (:pull:`5064`)
 
+#. `@Esadek-MO`_ added notes at the bottom of functions to
+   to clarify if the function preserves laziness or not. (:pull:`5066`)
+
 💼 Internal
 ===========
 
 #. `@rcomer`_ removed the obsolete ``setUpClass`` method from Iris testing.
    (:pull:`4927`)
 
+#. `@bjlittle`_ and `@lbdreyer`_ (reviewer) removed support for
+   ``python setup.py test``, which is a deprecated approach to executing
+   package tests, see `pypa/setuptools#1684`_.  Also performed assorted
+   ``setup.py`` script hygiene. (:pull:`4948`, :pull:`4949`, :pull:`4950`)
+
+#. `@pp-mo`_ split the module :mod:`iris.fileformats.netcdf` into separate
+   :mod:`~iris.fileformats.netcdf.loader` and :mod:`~iris.fileformats.netcdf.saver`
+   submodules, just to make the code easier to handle.
+
+#. `@trexfeathers`_ adapted the benchmark for importing :mod:`iris.palette` to
+   cope with new colormap behaviour in Matplotlib `v3.6`. (:pull:`4998`)
+
+#. `@rcomer`_ removed a now redundant workaround for an old matplotlib bug,
+   highlighted by :issue:`4090`.  (:pull:`4999`)
+
+#. `@rcomer`_ added the ``show`` option to the documentation Makefiles, as a
+   convenient way for contributors to view their built documentation.
+   (:pull:`5000`)
 
 .. comment
     Whatsnew author names (@github name) in alphabetical order. Note that,
     core dev names are automatically included by the common_links.inc:
 
+.. _@TTV-Intrepid: https://github.com/TTV-Intrepid
+.. _Julian Heming: https://www.metoffice.gov.uk/research/people/julian-heming
 .. _@hsteptoe: https://github.com/hsteptoe
 
 
 .. comment
     Whatsnew resources in alphabetical order:
 
-
 .. _NEP13: https://numpy.org/neps/nep-0013-ufunc-overrides.html
 .. _NEP18: https://numpy.org/neps/nep-0018-array-function-protocol.html
+.. _pypa/setuptools#1684: https://github.com/pypa/setuptools/issues/1684
+.. _SciTools/cartopy@fcb784d: https://github.com/SciTools/cartopy/commit/fcb784daa65d95ed9a74b02ca292801c02bc4108
+.. _SciTools/cartopy@8860a81: https://github.com/SciTools/cartopy/commit/8860a8186d4dc62478e74c83f3b2b3e8f791372e
+.. _dask/dask#9555: https://github.com/dask/dask/pull/9555
diff --git a/lib/iris/tests/experimental/test_animate.py b/lib/iris/tests/integration/plot/test_animate.py
similarity index 94%
rename from lib/iris/tests/experimental/test_animate.py
rename to lib/iris/tests/integration/plot/test_animate.py
index d8010767b8..ef19dbb108 100644
--- a/lib/iris/tests/experimental/test_animate.py
+++ b/lib/iris/tests/integration/plot/test_animate.py
@@ -4,7 +4,7 @@
 # See COPYING and COPYING.LESSER in the root of the repository for full
 # licensing details.
 """
-Test the animation of cubes within iris.
+Integration tests for :func:`iris.plot.animate`.
 
 """
 
@@ -19,7 +19,6 @@
 
 # Run tests in no graphics mode if matplotlib is not available.
 if tests.MPL_AVAILABLE:
-    import iris.experimental.animate as animate
     import iris.plot as iplt
 
 
@@ -57,7 +56,7 @@ def test_cube_animation(self):
         # the animation.
         cube_iter = self.cube.slices(("latitude", "longitude"))
 
-        ani = animate.animate(cube_iter, iplt.contourf)
+        ani = iplt.animate(cube_iter, iplt.contourf)
 
         # Disconnect the first draw callback to stop the animation.
         ani._fig.canvas.mpl_disconnect(ani._first_draw_id)
diff --git a/lib/iris/tests/integration/test_netcdf.py b/lib/iris/tests/integration/test_netcdf.py
index 3feb637bf8..851c539ade 100644
--- a/lib/iris/tests/integration/test_netcdf.py
+++ b/lib/iris/tests/integration/test_netcdf.py
@@ -21,11 +21,13 @@
 import netCDF4 as nc
 import numpy as np
 import numpy.ma as ma
+import pytest
 
 import iris
 import iris.coord_systems
 from iris.coords import CellMethod, DimCoord
 from iris.cube import Cube, CubeList
+import iris.exceptions
 from iris.fileformats.netcdf import (
     CF_CONVENTIONS_VERSION,
     Saver,
@@ -903,5 +905,54 @@ def test_netcdf_with_no_constraint(self):
         self.assertEqual(len(cubes), 3)
 
 
+class TestSkippedCoord:
+    # If a coord/cell measure/etcetera cannot be added to the loaded Cube, a
+    #  Warning is raised and the coord is skipped.
+    # This 'catching' is generic to all CannotAddErrors, but currently the only
+    #  such problem that can exist in a NetCDF file is a mismatch of dimensions
+    #  between phenomenon and coord.
+
+    cdl_core = """
+dimensions:
+    length_scale = 1 ;
+    lat = 3 ;
+variables:
+    float lat(lat) ;
+        lat:standard_name = "latitude" ;
+        lat:units = "degrees_north" ;
+    short lst_unc_sys(length_scale) ;
+        lst_unc_sys:long_name = "uncertainty from large-scale systematic
+        errors" ;
+        lst_unc_sys:units = "kelvin" ;
+        lst_unc_sys:coordinates = "lat" ;
+
+data:
+    lat = 0, 1, 2;
+    """
+
+    @pytest.fixture(autouse=True)
+    def create_nc_file(self, tmp_path):
+        file_name = "dim_mismatch"
+        cdl = f"netcdf {file_name}" + "{\n" + self.cdl_core + "\n}"
+        self.nc_path = (tmp_path / file_name).with_suffix(".nc")
+        ncgen_from_cdl(
+            cdl_str=cdl,
+            cdl_path=None,
+            nc_path=str(self.nc_path),
+        )
+        yield
+        self.nc_path.unlink()
+
+    def test_lat_not_loaded(self):
+        # iris#5068 includes discussion of possible retention of the skipped
+        #  coords in the future.
+        with pytest.warns(
+            match="Missing data dimensions for multi-valued DimCoord"
+        ):
+            cube = iris.load_cube(self.nc_path)
+        with pytest.raises(iris.exceptions.CoordinateNotFoundError):
+            _ = cube.coord("lat")
+
+
 if __name__ == "__main__":
     tests.main()
diff --git a/lib/iris/tests/results/experimental/regrid/regrid_area_weighted_rectilinear_src_and_grid/const_lat_cross_section.cml b/lib/iris/tests/results/experimental/regrid/regrid_area_weighted_rectilinear_src_and_grid/const_lat_cross_section.cml
index b41c0e48c7..cc9deb4260 100644
--- a/lib/iris/tests/results/experimental/regrid/regrid_area_weighted_rectilinear_src_and_grid/const_lat_cross_section.cml
+++ b/lib/iris/tests/results/experimental/regrid/regrid_area_weighted_rectilinear_src_and_grid/const_lat_cross_section.cml
@@ -5,6 +5,60 @@
       <attribute name="source" value="Iris test case"/>
     </attributes>
     <coords>
+      <coord datadims="[0, 1]">
+        <auxCoord bounds="[[[367.88, 380.648],
+		 [372.945, 385.705],
+		 [369.978, 382.743],
+		 ..., 
+		 [319.021, 331.865],
+		 [299.908, 312.781],
+		 [273.08, 285.993]],
+
+		[[380.648, 399.802],
+		 [385.705, 404.847],
+		 [382.743, 401.891],
+		 ..., 
+		 [331.865, 351.13],
+		 [312.781, 332.091],
+		 [285.993, 305.365]],
+
+		[[399.802, 425.341],
+		 [404.847, 430.37],
+		 [401.891, 427.423],
+		 ..., 
+		 [351.13, 376.819],
+		 [332.091, 357.838],
+		 [305.365, 331.194]],
+
+		[[425.341, 457.267],
+		 [430.37, 462.277],
+		 [427.423, 459.342],
+		 ..., 
+		 [376.819, 408.932],
+		 [357.838, 390.024],
+		 [331.194, 363.483]],
+
+		[[457.267, 495.582],
+		 [462.277, 500.569],
+		 [459.342, 497.647],
+		 ..., 
+		 [408.932, 447.471],
+		 [390.024, 428.651],
+		 [363.483, 402.233]]]" id="9041e969" points="[[372.668, 377.73, 374.764, ..., 323.838,
+		 304.736, 277.922],
+		[388.629, 393.681, 390.721, ..., 339.892,
+		 320.827, 294.065],
+		[410.975, 416.013, 413.061, ..., 362.369,
+		 343.355, 316.665],
+		[439.707, 444.728, 441.786, ..., 391.27,
+		 372.322, 345.724],
+		[474.828, 479.827, 476.898, ..., 426.595,
+		 407.728, 381.243]]" shape="(5, 18)" standard_name="altitude" units="Unit('m')" value_type="float32">
+          <attributes>
+            <attribute name="positive" value="up"/>
+          </attributes>
+        </auxCoord>
+      </coord>
       <coord>
         <dimCoord id="1d45e087" points="[0.0]" shape="(1,)" standard_name="forecast_period" units="Unit('hours')" value_type="float64"/>
       </coord>
@@ -65,6 +119,12 @@
 		[0.993097, 0.989272],
 		[0.989272, 0.984692]]" id="a5c170db" long_name="sigma" points="[0.999424, 0.997504, 0.99482, 0.991375, 0.987171]" shape="(5,)" units="Unit('1')" value_type="float32"/>
       </coord>
+      <coord datadims="[1]">
+        <auxCoord id="b15e3a2e" points="[367.88, 372.945, 369.978, 364.397, 351.799,
+		331.411, 311.032, 291.492, 267.047, 251.732,
+		253.644, 276.966, 304.615, 329.212, 332.834,
+		319.021, 299.908, 273.08]" shape="(18,)" standard_name="surface_altitude" units="Unit('m')" value_type="float32"/>
+      </coord>
       <coord>
         <dimCoord id="cb784457" points="[347921.166667]" shape="(1,)" standard_name="time" units="Unit('hours since 1970-01-01 00:00:00', calendar='standard')" value_type="float64"/>
       </coord>
diff --git a/lib/iris/tests/results/experimental/regrid/regrid_area_weighted_rectilinear_src_and_grid/const_lon_cross_section.cml b/lib/iris/tests/results/experimental/regrid/regrid_area_weighted_rectilinear_src_and_grid/const_lon_cross_section.cml
index 8617be9372..fb3d2cdbcf 100644
--- a/lib/iris/tests/results/experimental/regrid/regrid_area_weighted_rectilinear_src_and_grid/const_lon_cross_section.cml
+++ b/lib/iris/tests/results/experimental/regrid/regrid_area_weighted_rectilinear_src_and_grid/const_lon_cross_section.cml
@@ -5,6 +5,65 @@
       <attribute name="source" value="Iris test case"/>
     </attributes>
     <coords>
+      <coord datadims="[0, 1]">
+        <auxCoord bounds="[[[385.034, 397.775],
+		 [375.867, 388.623],
+		 [333.195, 346.016],
+		 ..., 
+		 [399.275, 411.995],
+		 [411.788, 424.489],
+		 [384.595, 397.337]],
+
+		[[397.775, 416.889],
+		 [388.623, 407.758],
+		 [346.016, 365.249],
+		 ..., 
+		 [411.995, 431.076],
+		 [424.489, 443.541],
+		 [397.337, 416.452]],
+
+		[[416.889, 442.376],
+		 [407.758, 433.273],
+		 [365.249, 390.895],
+		 ..., 
+		 [431.076, 456.519],
+		 [443.541, 468.945],
+		 [416.452, 441.94]],
+
+		[[442.376, 474.236],
+		 [433.273, 465.168],
+		 [390.895, 422.953],
+		 ..., 
+		 [456.519, 488.325],
+		 [468.945, 500.704],
+		 [441.94, 473.802]],
+
+		[[474.236, 512.473],
+		 [465.168, 503.447],
+		 [422.953, 461.427],
+		 ..., 
+		 [488.325, 526.496],
+		 [500.704, 538.818],
+		 [473.802, 512.041]]]" id="9041e969" points="[[389.812, 380.651, 338.003, 295.528, 278.922,
+		 280.889, 327.146, 342.957, 368.656, 404.045,
+		 416.551, 389.373],
+		[405.739, 396.596, 354.03, 311.637, 295.063,
+		 297.026, 343.194, 358.975, 384.624, 419.945,
+		 432.427, 405.302],
+		[428.039, 418.92, 376.469, 334.19, 317.661,
+		 319.618, 365.662, 381.4, 406.981, 442.207,
+		 454.655, 427.603],
+		[456.712, 447.625, 405.321, 363.188, 346.716,
+		 348.667, 394.551, 410.235, 435.727, 470.831,
+		 483.236, 456.277],
+		[491.761, 482.712, 440.587, 398.633, 382.231,
+		 384.173, 429.863, 445.481, 470.864, 505.819,
+		 518.172, 491.328]]" shape="(5, 12)" standard_name="altitude" units="Unit('m')" value_type="float32">
+          <attributes>
+            <attribute name="positive" value="up"/>
+          </attributes>
+        </auxCoord>
+      </coord>
       <coord>
         <dimCoord id="1d45e087" points="[0.0]" shape="(1,)" standard_name="forecast_period" units="Unit('hours')" value_type="float64"/>
       </coord>
@@ -59,6 +118,11 @@
 		[0.993097, 0.989272],
 		[0.989272, 0.984692]]" id="a5c170db" long_name="sigma" points="[0.999424, 0.997504, 0.99482, 0.991375, 0.987171]" shape="(5,)" units="Unit('1')" value_type="float32"/>
       </coord>
+      <coord datadims="[1]">
+        <auxCoord id="b15e3a2e" points="[385.034, 375.867, 333.195, 290.696, 274.08,
+		276.048, 322.332, 338.152, 363.866, 399.275,
+		411.788, 384.595]" shape="(12,)" standard_name="surface_altitude" units="Unit('m')" value_type="float32"/>
+      </coord>
       <coord>
         <dimCoord id="cb784457" points="[347921.166667]" shape="(1,)" standard_name="time" units="Unit('hours since 1970-01-01 00:00:00', calendar='standard')" value_type="float64"/>
       </coord>
diff --git a/lib/iris/tests/results/experimental/ugrid/2D_1t_face_half_levels.cml b/lib/iris/tests/results/experimental/ugrid/2D_1t_face_half_levels.cml
index b863adcf55..7422bfe044 100644
--- a/lib/iris/tests/results/experimental/ugrid/2D_1t_face_half_levels.cml
+++ b/lib/iris/tests/results/experimental/ugrid/2D_1t_face_half_levels.cml
@@ -20,8 +20,8 @@
 		..., 
 		[-42.7342, -40.8934, -46.161, -48.912],
 		[-40.8934, -38.4268, -42.6612, -46.161],
-		[-38.4268, -35.2644, -38.4268, -42.6612]]" id="21594c35" long_name="Latitude of mesh nodes." points="[33.4328, 36.1226, 38.2012, ..., -44.791,
-		-42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/>
+		[-38.4268, -35.2644, -38.4268, -42.6612]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[33.4328, 36.1226, 38.2012, ..., -44.791,
+		-42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/>
       </coord>
       <coord datadims="[1]">
         <meshCoord bounds="[[-45.0, -37.5, -37.5, -45.0],
@@ -30,8 +30,8 @@
 		..., 
 		[-112.5, -120.0, -126.958, -118.361],
 		[-120.0, -127.5, -135.0, -126.958],
-		[-127.5, -135.0, -142.5, -135.0]]" id="fac9555b" long_name="Longitude of mesh nodes." points="[-41.3152, -33.8068, -26.296, ..., -119.377,
-		-127.321, -135.0]" shape="(864,)" standard_name="longitude" units="Unit('degrees')" value_type="float32"/>
+		[-127.5, -135.0, -142.5, -135.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[-41.3152, -33.8068, -26.296, ..., -119.377,
+		-127.321, -135.0]" shape="(864,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/>
       </coord>
       <coord datadims="[0]">
         <auxCoord bounds="[[21600.0, 21600.0]]" id="17a27d05" long_name="Time axis" points="[21600.0]" shape="(1,)" standard_name="time" units="Unit('seconds since 2016-01-01 15:00:00', calendar='standard')" value_type="float64" var_name="time_instant">
diff --git a/lib/iris/tests/results/experimental/ugrid/2D_72t_face_half_levels.cml b/lib/iris/tests/results/experimental/ugrid/2D_72t_face_half_levels.cml
index b46908a648..f9e0511ccb 100644
--- a/lib/iris/tests/results/experimental/ugrid/2D_72t_face_half_levels.cml
+++ b/lib/iris/tests/results/experimental/ugrid/2D_72t_face_half_levels.cml
@@ -20,8 +20,8 @@
 		..., 
 		[-42.7342, -40.8934, -46.161, -48.912],
 		[-40.8934, -38.4268, -42.6612, -46.161],
-		[-38.4268, -35.2644, -38.4268, -42.6612]]" id="21594c35" long_name="Latitude of mesh nodes." points="[33.4328, 36.1226, 38.2012, ..., -44.791,
-		-42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/>
+		[-38.4268, -35.2644, -38.4268, -42.6612]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[33.4328, 36.1226, 38.2012, ..., -44.791,
+		-42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/>
       </coord>
       <coord datadims="[1]">
         <meshCoord bounds="[[-45.0, -37.5, -37.5, -45.0],
@@ -30,8 +30,8 @@
 		..., 
 		[-112.5, -120.0, -126.958, -118.361],
 		[-120.0, -127.5, -135.0, -126.958],
-		[-127.5, -135.0, -142.5, -135.0]]" id="fac9555b" long_name="Longitude of mesh nodes." points="[-41.3152, -33.8068, -26.296, ..., -119.377,
-		-127.321, -135.0]" shape="(864,)" standard_name="longitude" units="Unit('degrees')" value_type="float32"/>
+		[-127.5, -135.0, -142.5, -135.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[-41.3152, -33.8068, -26.296, ..., -119.377,
+		-127.321, -135.0]" shape="(864,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/>
       </coord>
       <coord datadims="[0]">
         <auxCoord bounds="[[300.0, 300.0],
diff --git a/lib/iris/tests/results/experimental/ugrid/3D_1t_face_full_levels.cml b/lib/iris/tests/results/experimental/ugrid/3D_1t_face_full_levels.cml
index 57209e4ba7..2fb8b6e1f0 100644
--- a/lib/iris/tests/results/experimental/ugrid/3D_1t_face_full_levels.cml
+++ b/lib/iris/tests/results/experimental/ugrid/3D_1t_face_full_levels.cml
@@ -31,8 +31,8 @@
 		..., 
 		[-42.7342, -40.8934, -46.161, -48.912],
 		[-40.8934, -38.4268, -42.6612, -46.161],
-		[-38.4268, -35.2644, -38.4268, -42.6612]]" id="21594c35" long_name="Latitude of mesh nodes." points="[33.4328, 36.1226, 38.2012, ..., -44.791,
-		-42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/>
+		[-38.4268, -35.2644, -38.4268, -42.6612]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[33.4328, 36.1226, 38.2012, ..., -44.791,
+		-42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_full_levels_face_y"/>
       </coord>
       <coord datadims="[2]">
         <meshCoord bounds="[[-45.0, -37.5, -37.5, -45.0],
@@ -41,8 +41,8 @@
 		..., 
 		[-112.5, -120.0, -126.958, -118.361],
 		[-120.0, -127.5, -135.0, -126.958],
-		[-127.5, -135.0, -142.5, -135.0]]" id="fac9555b" long_name="Longitude of mesh nodes." points="[-41.3152, -33.8068, -26.296, ..., -119.377,
-		-127.321, -135.0]" shape="(864,)" standard_name="longitude" units="Unit('degrees')" value_type="float32"/>
+		[-127.5, -135.0, -142.5, -135.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[-41.3152, -33.8068, -26.296, ..., -119.377,
+		-127.321, -135.0]" shape="(864,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_full_levels_face_x"/>
       </coord>
       <coord datadims="[0]">
         <auxCoord bounds="[[21600.0, 21600.0]]" id="17a27d05" long_name="Time axis" points="[21600.0]" shape="(1,)" standard_name="time" units="Unit('seconds since 2016-01-01 15:00:00', calendar='standard')" value_type="float64" var_name="time_instant">
diff --git a/lib/iris/tests/results/experimental/ugrid/3D_1t_face_half_levels.cml b/lib/iris/tests/results/experimental/ugrid/3D_1t_face_half_levels.cml
index c260587921..9a819eee9e 100644
--- a/lib/iris/tests/results/experimental/ugrid/3D_1t_face_half_levels.cml
+++ b/lib/iris/tests/results/experimental/ugrid/3D_1t_face_half_levels.cml
@@ -31,8 +31,8 @@
 		..., 
 		[-42.7342, -40.8934, -46.161, -48.912],
 		[-40.8934, -38.4268, -42.6612, -46.161],
-		[-38.4268, -35.2644, -38.4268, -42.6612]]" id="21594c35" long_name="Latitude of mesh nodes." points="[33.4328, 36.1226, 38.2012, ..., -44.791,
-		-42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/>
+		[-38.4268, -35.2644, -38.4268, -42.6612]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[33.4328, 36.1226, 38.2012, ..., -44.791,
+		-42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/>
       </coord>
       <coord datadims="[2]">
         <meshCoord bounds="[[-45.0, -37.5, -37.5, -45.0],
@@ -41,8 +41,8 @@
 		..., 
 		[-112.5, -120.0, -126.958, -118.361],
 		[-120.0, -127.5, -135.0, -126.958],
-		[-127.5, -135.0, -142.5, -135.0]]" id="fac9555b" long_name="Longitude of mesh nodes." points="[-41.3152, -33.8068, -26.296, ..., -119.377,
-		-127.321, -135.0]" shape="(864,)" standard_name="longitude" units="Unit('degrees')" value_type="float32"/>
+		[-127.5, -135.0, -142.5, -135.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[-41.3152, -33.8068, -26.296, ..., -119.377,
+		-127.321, -135.0]" shape="(864,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/>
       </coord>
       <coord datadims="[0]">
         <auxCoord bounds="[[21600.0, 21600.0]]" id="17a27d05" long_name="Time axis" points="[21600.0]" shape="(1,)" standard_name="time" units="Unit('seconds since 2016-01-01 15:00:00', calendar='standard')" value_type="float64" var_name="time_instant">
diff --git a/lib/iris/tests/results/experimental/ugrid/3D_snow_pseudo_levels.cml b/lib/iris/tests/results/experimental/ugrid/3D_snow_pseudo_levels.cml
index e545e05fdc..9133d98e73 100644
--- a/lib/iris/tests/results/experimental/ugrid/3D_snow_pseudo_levels.cml
+++ b/lib/iris/tests/results/experimental/ugrid/3D_snow_pseudo_levels.cml
@@ -20,8 +20,8 @@
 		..., 
 		[-42.7342, -40.8934, -46.161, -48.912],
 		[-40.8934, -38.4268, -42.6612, -46.161],
-		[-38.4268, -35.2644, -38.4268, -42.6612]]" id="21594c35" long_name="Latitude of mesh nodes." points="[33.4328, 36.1226, 38.2012, ..., -44.791,
-		-42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/>
+		[-38.4268, -35.2644, -38.4268, -42.6612]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[33.4328, 36.1226, 38.2012, ..., -44.791,
+		-42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/>
       </coord>
       <coord datadims="[2]">
         <meshCoord bounds="[[-45.0, -37.5, -37.5, -45.0],
@@ -30,8 +30,8 @@
 		..., 
 		[-112.5, -120.0, -126.958, -118.361],
 		[-120.0, -127.5, -135.0, -126.958],
-		[-127.5, -135.0, -142.5, -135.0]]" id="fac9555b" long_name="Longitude of mesh nodes." points="[-41.3152, -33.8068, -26.296, ..., -119.377,
-		-127.321, -135.0]" shape="(864,)" standard_name="longitude" units="Unit('degrees')" value_type="float32"/>
+		[-127.5, -135.0, -142.5, -135.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[-41.3152, -33.8068, -26.296, ..., -119.377,
+		-127.321, -135.0]" shape="(864,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/>
       </coord>
       <coord datadims="[0]">
         <auxCoord bounds="[[21600.0, 21600.0]]" id="17a27d05" long_name="Time axis" points="[21600.0]" shape="(1,)" standard_name="time" units="Unit('seconds since 2016-01-01 15:00:00', calendar='standard')" value_type="float64" var_name="time_instant">
diff --git a/lib/iris/tests/results/experimental/ugrid/3D_soil_pseudo_levels.cml b/lib/iris/tests/results/experimental/ugrid/3D_soil_pseudo_levels.cml
index 4eedfc21b3..05aeab9ccb 100644
--- a/lib/iris/tests/results/experimental/ugrid/3D_soil_pseudo_levels.cml
+++ b/lib/iris/tests/results/experimental/ugrid/3D_soil_pseudo_levels.cml
@@ -20,8 +20,8 @@
 		..., 
 		[-42.7342, -40.8934, -46.161, -48.912],
 		[-40.8934, -38.4268, -42.6612, -46.161],
-		[-38.4268, -35.2644, -38.4268, -42.6612]]" id="21594c35" long_name="Latitude of mesh nodes." points="[33.4328, 36.1226, 38.2012, ..., -44.791,
-		-42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/>
+		[-38.4268, -35.2644, -38.4268, -42.6612]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[33.4328, 36.1226, 38.2012, ..., -44.791,
+		-42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/>
       </coord>
       <coord datadims="[2]">
         <meshCoord bounds="[[-45.0, -37.5, -37.5, -45.0],
@@ -30,8 +30,8 @@
 		..., 
 		[-112.5, -120.0, -126.958, -118.361],
 		[-120.0, -127.5, -135.0, -126.958],
-		[-127.5, -135.0, -142.5, -135.0]]" id="fac9555b" long_name="Longitude of mesh nodes." points="[-41.3152, -33.8068, -26.296, ..., -119.377,
-		-127.321, -135.0]" shape="(864,)" standard_name="longitude" units="Unit('degrees')" value_type="float32"/>
+		[-127.5, -135.0, -142.5, -135.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[-41.3152, -33.8068, -26.296, ..., -119.377,
+		-127.321, -135.0]" shape="(864,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/>
       </coord>
       <coord datadims="[0]">
         <auxCoord bounds="[[21600.0, 21600.0]]" id="17a27d05" long_name="Time axis" points="[21600.0]" shape="(1,)" standard_name="time" units="Unit('seconds since 2016-01-01 15:00:00', calendar='standard')" value_type="float64" var_name="time_instant">
diff --git a/lib/iris/tests/results/experimental/ugrid/3D_tile_pseudo_levels.cml b/lib/iris/tests/results/experimental/ugrid/3D_tile_pseudo_levels.cml
index 55155047bb..9dc3e08ee6 100644
--- a/lib/iris/tests/results/experimental/ugrid/3D_tile_pseudo_levels.cml
+++ b/lib/iris/tests/results/experimental/ugrid/3D_tile_pseudo_levels.cml
@@ -20,8 +20,8 @@
 		..., 
 		[-42.7342, -40.8934, -46.161, -48.912],
 		[-40.8934, -38.4268, -42.6612, -46.161],
-		[-38.4268, -35.2644, -38.4268, -42.6612]]" id="21594c35" long_name="Latitude of mesh nodes." points="[33.4328, 36.1226, 38.2012, ..., -44.791,
-		-42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/>
+		[-38.4268, -35.2644, -38.4268, -42.6612]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[33.4328, 36.1226, 38.2012, ..., -44.791,
+		-42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/>
       </coord>
       <coord datadims="[2]">
         <meshCoord bounds="[[-45.0, -37.5, -37.5, -45.0],
@@ -30,8 +30,8 @@
 		..., 
 		[-112.5, -120.0, -126.958, -118.361],
 		[-120.0, -127.5, -135.0, -126.958],
-		[-127.5, -135.0, -142.5, -135.0]]" id="fac9555b" long_name="Longitude of mesh nodes." points="[-41.3152, -33.8068, -26.296, ..., -119.377,
-		-127.321, -135.0]" shape="(864,)" standard_name="longitude" units="Unit('degrees')" value_type="float32"/>
+		[-127.5, -135.0, -142.5, -135.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[-41.3152, -33.8068, -26.296, ..., -119.377,
+		-127.321, -135.0]" shape="(864,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/>
       </coord>
       <coord datadims="[0]">
         <auxCoord bounds="[[21600.0, 21600.0]]" id="17a27d05" long_name="Time axis" points="[21600.0]" shape="(1,)" standard_name="time" units="Unit('seconds since 2016-01-01 15:00:00', calendar='standard')" value_type="float64" var_name="time_instant">
diff --git a/lib/iris/tests/results/experimental/ugrid/3D_veg_pseudo_levels.cml b/lib/iris/tests/results/experimental/ugrid/3D_veg_pseudo_levels.cml
index fc52fce0b3..7bb47c5296 100644
--- a/lib/iris/tests/results/experimental/ugrid/3D_veg_pseudo_levels.cml
+++ b/lib/iris/tests/results/experimental/ugrid/3D_veg_pseudo_levels.cml
@@ -20,8 +20,8 @@
 		..., 
 		[-42.7342, -40.8934, -46.161, -48.912],
 		[-40.8934, -38.4268, -42.6612, -46.161],
-		[-38.4268, -35.2644, -38.4268, -42.6612]]" id="21594c35" long_name="Latitude of mesh nodes." points="[33.4328, 36.1226, 38.2012, ..., -44.791,
-		-42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/>
+		[-38.4268, -35.2644, -38.4268, -42.6612]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[33.4328, 36.1226, 38.2012, ..., -44.791,
+		-42.1583, -38.815]" shape="(864,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/>
       </coord>
       <coord datadims="[2]">
         <meshCoord bounds="[[-45.0, -37.5, -37.5, -45.0],
@@ -30,8 +30,8 @@
 		..., 
 		[-112.5, -120.0, -126.958, -118.361],
 		[-120.0, -127.5, -135.0, -126.958],
-		[-127.5, -135.0, -142.5, -135.0]]" id="fac9555b" long_name="Longitude of mesh nodes." points="[-41.3152, -33.8068, -26.296, ..., -119.377,
-		-127.321, -135.0]" shape="(864,)" standard_name="longitude" units="Unit('degrees')" value_type="float32"/>
+		[-127.5, -135.0, -142.5, -135.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[-41.3152, -33.8068, -26.296, ..., -119.377,
+		-127.321, -135.0]" shape="(864,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/>
       </coord>
       <coord datadims="[0]">
         <auxCoord bounds="[[21600.0, 21600.0]]" id="17a27d05" long_name="Time axis" points="[21600.0]" shape="(1,)" standard_name="time" units="Unit('seconds since 2016-01-01 15:00:00', calendar='standard')" value_type="float64" var_name="time_instant">
diff --git a/lib/iris/tests/results/experimental/ugrid/surface_mean.cml b/lib/iris/tests/results/experimental/ugrid/surface_mean.cml
index 368b3508e3..8ccd602c11 100644
--- a/lib/iris/tests/results/experimental/ugrid/surface_mean.cml
+++ b/lib/iris/tests/results/experimental/ugrid/surface_mean.cml
@@ -20,8 +20,8 @@
 		..., 
 		[-37.7044, -36.9373, -37.9318, -38.7655],
 		[-36.9373, -36.1244, -37.0517, -37.9318],
-		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
-		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/>
+		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
+		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/>
       </coord>
       <coord datadims="[1]">
         <meshCoord bounds="[[315.0, 316.875, 316.875, 315.0],
@@ -30,8 +30,8 @@
 		..., 
 		[230.625, 228.75, 226.883, 228.774],
 		[228.75, 226.875, 225.0, 226.883],
-		[226.875, 225.0, 223.125, 225.0]]" id="fac9555b" long_name="Longitude of mesh nodes." points="[315.933, 317.808, 319.683, ..., 228.759,
-		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32"/>
+		[226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759,
+		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/>
       </coord>
       <coord datadims="[0]">
         <auxCoord bounds="[[15508800.0, 15595200.0]]" id="17a27d05" long_name="Time axis" points="[15552000.0]" shape="(1,)" standard_name="time" units="Unit('seconds since 2016-01-01 15:00:00', calendar='360_day')" value_type="float64" var_name="time_centered">
@@ -71,8 +71,8 @@
 		..., 
 		[-37.7044, -36.9373, -37.9318, -38.7655],
 		[-36.9373, -36.1244, -37.0517, -37.9318],
-		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
-		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/>
+		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
+		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/>
       </coord>
       <coord datadims="[1]">
         <meshCoord bounds="[[315.0, 316.875, 316.875, 315.0],
@@ -81,8 +81,8 @@
 		..., 
 		[230.625, 228.75, 226.883, 228.774],
 		[228.75, 226.875, 225.0, 226.883],
-		[226.875, 225.0, 223.125, 225.0]]" id="fac9555b" long_name="Longitude of mesh nodes." points="[315.933, 317.808, 319.683, ..., 228.759,
-		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32"/>
+		[226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759,
+		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/>
       </coord>
       <coord datadims="[0]">
         <auxCoord bounds="[[15508800.0, 15595200.0]]" id="17a27d05" long_name="Time axis" points="[15552000.0]" shape="(1,)" standard_name="time" units="Unit('seconds since 2016-01-01 15:00:00', calendar='360_day')" value_type="float64" var_name="time_centered">
@@ -122,8 +122,8 @@
 		..., 
 		[-37.7044, -36.9373, -37.9318, -38.7655],
 		[-36.9373, -36.1244, -37.0517, -37.9318],
-		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
-		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/>
+		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
+		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/>
       </coord>
       <coord datadims="[1]">
         <meshCoord bounds="[[315.0, 316.875, 316.875, 315.0],
@@ -132,8 +132,8 @@
 		..., 
 		[230.625, 228.75, 226.883, 228.774],
 		[228.75, 226.875, 225.0, 226.883],
-		[226.875, 225.0, 223.125, 225.0]]" id="fac9555b" long_name="Longitude of mesh nodes." points="[315.933, 317.808, 319.683, ..., 228.759,
-		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32"/>
+		[226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759,
+		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/>
       </coord>
       <coord datadims="[0]">
         <auxCoord bounds="[[15508800.0, 15595200.0]]" id="17a27d05" long_name="Time axis" points="[15552000.0]" shape="(1,)" standard_name="time" units="Unit('seconds since 2016-01-01 15:00:00', calendar='360_day')" value_type="float64" var_name="time_centered">
@@ -173,8 +173,8 @@
 		..., 
 		[-37.7044, -36.9373, -37.9318, -38.7655],
 		[-36.9373, -36.1244, -37.0517, -37.9318],
-		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
-		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/>
+		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
+		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/>
       </coord>
       <coord datadims="[1]">
         <meshCoord bounds="[[315.0, 316.875, 316.875, 315.0],
@@ -183,8 +183,8 @@
 		..., 
 		[230.625, 228.75, 226.883, 228.774],
 		[228.75, 226.875, 225.0, 226.883],
-		[226.875, 225.0, 223.125, 225.0]]" id="fac9555b" long_name="Longitude of mesh nodes." points="[315.933, 317.808, 319.683, ..., 228.759,
-		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32"/>
+		[226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759,
+		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/>
       </coord>
       <coord datadims="[0]">
         <auxCoord bounds="[[15508800.0, 15595200.0]]" id="17a27d05" long_name="Time axis" points="[15552000.0]" shape="(1,)" standard_name="time" units="Unit('seconds since 2016-01-01 15:00:00', calendar='360_day')" value_type="float64" var_name="time_centered">
@@ -224,8 +224,8 @@
 		..., 
 		[-37.7044, -36.9373, -37.9318, -38.7655],
 		[-36.9373, -36.1244, -37.0517, -37.9318],
-		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
-		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/>
+		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
+		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/>
       </coord>
       <coord datadims="[1]">
         <meshCoord bounds="[[315.0, 316.875, 316.875, 315.0],
@@ -234,8 +234,8 @@
 		..., 
 		[230.625, 228.75, 226.883, 228.774],
 		[228.75, 226.875, 225.0, 226.883],
-		[226.875, 225.0, 223.125, 225.0]]" id="fac9555b" long_name="Longitude of mesh nodes." points="[315.933, 317.808, 319.683, ..., 228.759,
-		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32"/>
+		[226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759,
+		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/>
       </coord>
       <coord datadims="[0]">
         <auxCoord bounds="[[15508800.0, 15595200.0]]" id="17a27d05" long_name="Time axis" points="[15552000.0]" shape="(1,)" standard_name="time" units="Unit('seconds since 2016-01-01 15:00:00', calendar='360_day')" value_type="float64" var_name="time_centered">
@@ -275,8 +275,8 @@
 		..., 
 		[-37.7044, -36.9373, -37.9318, -38.7655],
 		[-36.9373, -36.1244, -37.0517, -37.9318],
-		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
-		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/>
+		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
+		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/>
       </coord>
       <coord datadims="[1]">
         <meshCoord bounds="[[315.0, 316.875, 316.875, 315.0],
@@ -285,8 +285,8 @@
 		..., 
 		[230.625, 228.75, 226.883, 228.774],
 		[228.75, 226.875, 225.0, 226.883],
-		[226.875, 225.0, 223.125, 225.0]]" id="fac9555b" long_name="Longitude of mesh nodes." points="[315.933, 317.808, 319.683, ..., 228.759,
-		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32"/>
+		[226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759,
+		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/>
       </coord>
       <coord datadims="[0]">
         <auxCoord bounds="[[15508800.0, 15595200.0]]" id="17a27d05" long_name="Time axis" points="[15552000.0]" shape="(1,)" standard_name="time" units="Unit('seconds since 2016-01-01 15:00:00', calendar='360_day')" value_type="float64" var_name="time_centered">
@@ -326,8 +326,8 @@
 		..., 
 		[-37.7044, -36.9373, -37.9318, -38.7655],
 		[-36.9373, -36.1244, -37.0517, -37.9318],
-		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
-		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/>
+		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
+		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/>
       </coord>
       <coord datadims="[1]">
         <meshCoord bounds="[[315.0, 316.875, 316.875, 315.0],
@@ -336,8 +336,8 @@
 		..., 
 		[230.625, 228.75, 226.883, 228.774],
 		[228.75, 226.875, 225.0, 226.883],
-		[226.875, 225.0, 223.125, 225.0]]" id="fac9555b" long_name="Longitude of mesh nodes." points="[315.933, 317.808, 319.683, ..., 228.759,
-		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32"/>
+		[226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759,
+		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/>
       </coord>
       <coord datadims="[0]">
         <auxCoord bounds="[[15508800.0, 15595200.0]]" id="17a27d05" long_name="Time axis" points="[15552000.0]" shape="(1,)" standard_name="time" units="Unit('seconds since 2016-01-01 15:00:00', calendar='360_day')" value_type="float64" var_name="time_centered">
@@ -377,8 +377,8 @@
 		..., 
 		[-37.7044, -36.9373, -37.9318, -38.7655],
 		[-36.9373, -36.1244, -37.0517, -37.9318],
-		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
-		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/>
+		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
+		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/>
       </coord>
       <coord datadims="[1]">
         <meshCoord bounds="[[315.0, 316.875, 316.875, 315.0],
@@ -387,8 +387,8 @@
 		..., 
 		[230.625, 228.75, 226.883, 228.774],
 		[228.75, 226.875, 225.0, 226.883],
-		[226.875, 225.0, 223.125, 225.0]]" id="fac9555b" long_name="Longitude of mesh nodes." points="[315.933, 317.808, 319.683, ..., 228.759,
-		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32"/>
+		[226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759,
+		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/>
       </coord>
       <coord datadims="[0]">
         <auxCoord bounds="[[15508800.0, 15595200.0]]" id="17a27d05" long_name="Time axis" points="[15552000.0]" shape="(1,)" standard_name="time" units="Unit('seconds since 2016-01-01 15:00:00', calendar='360_day')" value_type="float64" var_name="time_centered">
@@ -428,8 +428,8 @@
 		..., 
 		[-37.7044, -36.9373, -37.9318, -38.7655],
 		[-36.9373, -36.1244, -37.0517, -37.9318],
-		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
-		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/>
+		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
+		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/>
       </coord>
       <coord datadims="[1]">
         <meshCoord bounds="[[315.0, 316.875, 316.875, 315.0],
@@ -438,8 +438,8 @@
 		..., 
 		[230.625, 228.75, 226.883, 228.774],
 		[228.75, 226.875, 225.0, 226.883],
-		[226.875, 225.0, 223.125, 225.0]]" id="fac9555b" long_name="Longitude of mesh nodes." points="[315.933, 317.808, 319.683, ..., 228.759,
-		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32"/>
+		[226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759,
+		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/>
       </coord>
       <coord datadims="[0]">
         <auxCoord bounds="[[15508800.0, 15595200.0]]" id="17a27d05" long_name="Time axis" points="[15552000.0]" shape="(1,)" standard_name="time" units="Unit('seconds since 2016-01-01 15:00:00', calendar='360_day')" value_type="float64" var_name="time_centered">
@@ -479,8 +479,8 @@
 		..., 
 		[-37.7044, -36.9373, -37.9318, -38.7655],
 		[-36.9373, -36.1244, -37.0517, -37.9318],
-		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
-		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/>
+		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
+		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/>
       </coord>
       <coord datadims="[1]">
         <meshCoord bounds="[[315.0, 316.875, 316.875, 315.0],
@@ -489,8 +489,8 @@
 		..., 
 		[230.625, 228.75, 226.883, 228.774],
 		[228.75, 226.875, 225.0, 226.883],
-		[226.875, 225.0, 223.125, 225.0]]" id="fac9555b" long_name="Longitude of mesh nodes." points="[315.933, 317.808, 319.683, ..., 228.759,
-		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32"/>
+		[226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759,
+		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/>
       </coord>
       <coord datadims="[0]">
         <auxCoord bounds="[[15508800.0, 15595200.0]]" id="17a27d05" long_name="Time axis" points="[15552000.0]" shape="(1,)" standard_name="time" units="Unit('seconds since 2016-01-01 15:00:00', calendar='360_day')" value_type="float64" var_name="time_centered">
@@ -530,8 +530,8 @@
 		..., 
 		[-37.7044, -36.9373, -37.9318, -38.7655],
 		[-36.9373, -36.1244, -37.0517, -37.9318],
-		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
-		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/>
+		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
+		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/>
       </coord>
       <coord datadims="[1]">
         <meshCoord bounds="[[315.0, 316.875, 316.875, 315.0],
@@ -540,8 +540,8 @@
 		..., 
 		[230.625, 228.75, 226.883, 228.774],
 		[228.75, 226.875, 225.0, 226.883],
-		[226.875, 225.0, 223.125, 225.0]]" id="fac9555b" long_name="Longitude of mesh nodes." points="[315.933, 317.808, 319.683, ..., 228.759,
-		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32"/>
+		[226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759,
+		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/>
       </coord>
       <coord datadims="[0]">
         <auxCoord bounds="[[15508800.0, 15595200.0]]" id="17a27d05" long_name="Time axis" points="[15552000.0]" shape="(1,)" standard_name="time" units="Unit('seconds since 2016-01-01 15:00:00', calendar='360_day')" value_type="float64" var_name="time_centered">
@@ -581,8 +581,8 @@
 		..., 
 		[-37.7044, -36.9373, -37.9318, -38.7655],
 		[-36.9373, -36.1244, -37.0517, -37.9318],
-		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
-		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/>
+		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
+		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/>
       </coord>
       <coord datadims="[1]">
         <meshCoord bounds="[[315.0, 316.875, 316.875, 315.0],
@@ -591,8 +591,8 @@
 		..., 
 		[230.625, 228.75, 226.883, 228.774],
 		[228.75, 226.875, 225.0, 226.883],
-		[226.875, 225.0, 223.125, 225.0]]" id="fac9555b" long_name="Longitude of mesh nodes." points="[315.933, 317.808, 319.683, ..., 228.759,
-		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32"/>
+		[226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759,
+		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/>
       </coord>
       <coord datadims="[0]">
         <auxCoord bounds="[[15508800.0, 15595200.0]]" id="17a27d05" long_name="Time axis" points="[15552000.0]" shape="(1,)" standard_name="time" units="Unit('seconds since 2016-01-01 15:00:00', calendar='360_day')" value_type="float64" var_name="time_centered">
@@ -632,8 +632,8 @@
 		..., 
 		[-37.7044, -36.9373, -37.9318, -38.7655],
 		[-36.9373, -36.1244, -37.0517, -37.9318],
-		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
-		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/>
+		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
+		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/>
       </coord>
       <coord datadims="[1]">
         <meshCoord bounds="[[315.0, 316.875, 316.875, 315.0],
@@ -642,8 +642,8 @@
 		..., 
 		[230.625, 228.75, 226.883, 228.774],
 		[228.75, 226.875, 225.0, 226.883],
-		[226.875, 225.0, 223.125, 225.0]]" id="fac9555b" long_name="Longitude of mesh nodes." points="[315.933, 317.808, 319.683, ..., 228.759,
-		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32"/>
+		[226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759,
+		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/>
       </coord>
       <coord datadims="[0]">
         <auxCoord bounds="[[15508800.0, 15595200.0]]" id="17a27d05" long_name="Time axis" points="[15552000.0]" shape="(1,)" standard_name="time" units="Unit('seconds since 2016-01-01 15:00:00', calendar='360_day')" value_type="float64" var_name="time_centered">
@@ -683,8 +683,8 @@
 		..., 
 		[-37.7044, -36.9373, -37.9318, -38.7655],
 		[-36.9373, -36.1244, -37.0517, -37.9318],
-		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
-		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/>
+		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
+		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/>
       </coord>
       <coord datadims="[1]">
         <meshCoord bounds="[[315.0, 316.875, 316.875, 315.0],
@@ -693,8 +693,8 @@
 		..., 
 		[230.625, 228.75, 226.883, 228.774],
 		[228.75, 226.875, 225.0, 226.883],
-		[226.875, 225.0, 223.125, 225.0]]" id="fac9555b" long_name="Longitude of mesh nodes." points="[315.933, 317.808, 319.683, ..., 228.759,
-		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32"/>
+		[226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759,
+		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/>
       </coord>
       <coord datadims="[0]">
         <auxCoord bounds="[[15508800.0, 15595200.0]]" id="17a27d05" long_name="Time axis" points="[15552000.0]" shape="(1,)" standard_name="time" units="Unit('seconds since 2016-01-01 15:00:00', calendar='360_day')" value_type="float64" var_name="time_centered">
@@ -734,8 +734,8 @@
 		..., 
 		[-37.7044, -36.9373, -37.9318, -38.7655],
 		[-36.9373, -36.1244, -37.0517, -37.9318],
-		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
-		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/>
+		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
+		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/>
       </coord>
       <coord datadims="[1]">
         <meshCoord bounds="[[315.0, 316.875, 316.875, 315.0],
@@ -744,8 +744,8 @@
 		..., 
 		[230.625, 228.75, 226.883, 228.774],
 		[228.75, 226.875, 225.0, 226.883],
-		[226.875, 225.0, 223.125, 225.0]]" id="fac9555b" long_name="Longitude of mesh nodes." points="[315.933, 317.808, 319.683, ..., 228.759,
-		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32"/>
+		[226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759,
+		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/>
       </coord>
       <coord datadims="[0]">
         <auxCoord bounds="[[15508800.0, 15595200.0]]" id="17a27d05" long_name="Time axis" points="[15552000.0]" shape="(1,)" standard_name="time" units="Unit('seconds since 2016-01-01 15:00:00', calendar='360_day')" value_type="float64" var_name="time_centered">
@@ -785,8 +785,8 @@
 		..., 
 		[-37.7044, -36.9373, -37.9318, -38.7655],
 		[-36.9373, -36.1244, -37.0517, -37.9318],
-		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
-		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/>
+		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
+		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/>
       </coord>
       <coord datadims="[1]">
         <meshCoord bounds="[[315.0, 316.875, 316.875, 315.0],
@@ -795,8 +795,8 @@
 		..., 
 		[230.625, 228.75, 226.883, 228.774],
 		[228.75, 226.875, 225.0, 226.883],
-		[226.875, 225.0, 223.125, 225.0]]" id="fac9555b" long_name="Longitude of mesh nodes." points="[315.933, 317.808, 319.683, ..., 228.759,
-		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32"/>
+		[226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759,
+		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/>
       </coord>
       <coord datadims="[0]">
         <auxCoord bounds="[[15508800.0, 15595200.0]]" id="17a27d05" long_name="Time axis" points="[15552000.0]" shape="(1,)" standard_name="time" units="Unit('seconds since 2016-01-01 15:00:00', calendar='360_day')" value_type="float64" var_name="time_centered">
@@ -836,8 +836,8 @@
 		..., 
 		[-37.7044, -36.9373, -37.9318, -38.7655],
 		[-36.9373, -36.1244, -37.0517, -37.9318],
-		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
-		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/>
+		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
+		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/>
       </coord>
       <coord datadims="[1]">
         <meshCoord bounds="[[315.0, 316.875, 316.875, 315.0],
@@ -846,8 +846,8 @@
 		..., 
 		[230.625, 228.75, 226.883, 228.774],
 		[228.75, 226.875, 225.0, 226.883],
-		[226.875, 225.0, 223.125, 225.0]]" id="fac9555b" long_name="Longitude of mesh nodes." points="[315.933, 317.808, 319.683, ..., 228.759,
-		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32"/>
+		[226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759,
+		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/>
       </coord>
       <coord datadims="[0]">
         <auxCoord bounds="[[15508800.0, 15595200.0]]" id="17a27d05" long_name="Time axis" points="[15552000.0]" shape="(1,)" standard_name="time" units="Unit('seconds since 2016-01-01 15:00:00', calendar='360_day')" value_type="float64" var_name="time_centered">
@@ -887,8 +887,8 @@
 		..., 
 		[-37.7044, -36.9373, -37.9318, -38.7655],
 		[-36.9373, -36.1244, -37.0517, -37.9318],
-		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="21594c35" long_name="Latitude of mesh nodes." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
-		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32"/>
+		[-36.1244, -35.2644, -36.1244, -37.0517]]" id="72da1058" long_name="Characteristic latitude of mesh faces." points="[34.8187, 35.6462, 36.4283, ..., -37.8421,
+		-37.0187, -36.1485]" shape="(13824,)" standard_name="latitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_y"/>
       </coord>
       <coord datadims="[1]">
         <meshCoord bounds="[[315.0, 316.875, 316.875, 315.0],
@@ -897,8 +897,8 @@
 		..., 
 		[230.625, 228.75, 226.883, 228.774],
 		[228.75, 226.875, 225.0, 226.883],
-		[226.875, 225.0, 223.125, 225.0]]" id="fac9555b" long_name="Longitude of mesh nodes." points="[315.933, 317.808, 319.683, ..., 228.759,
-		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32"/>
+		[226.875, 225.0, 223.125, 225.0]]" id="b5c6bdeb" long_name="Characteristic longitude of mesh faces." points="[315.933, 317.808, 319.683, ..., 228.759,
+		226.878, 225.0]" shape="(13824,)" standard_name="longitude" units="Unit('degrees')" value_type="float32" var_name="Mesh2d_half_levels_face_x"/>
       </coord>
       <coord datadims="[0]">
         <auxCoord bounds="[[15508800.0, 15595200.0]]" id="17a27d05" long_name="Time axis" points="[15552000.0]" shape="(1,)" standard_name="time" units="Unit('seconds since 2016-01-01 15:00:00', calendar='360_day')" value_type="float64" var_name="time_centered">
diff --git a/lib/iris/tests/results/imagerepo.json b/lib/iris/tests/results/imagerepo.json
index 342428ca67..92f0d8fc20 100644
--- a/lib/iris/tests/results/imagerepo.json
+++ b/lib/iris/tests/results/imagerepo.json
@@ -34,9 +34,10 @@
     "gallery_tests.test_plot_wind_barbs.0": "e9e161e996169316c1fe9e96c29e36739e13c07c3d61c07f39813929c07f3f01",
     "gallery_tests.test_plot_wind_speed.0": "e9e960e996169306c1fe9e96c29e36739e03c06c3d61c07f3da139e1c07f3f01",
     "gallery_tests.test_plot_wind_speed.1": "e9e960e996169306c1ee9f96c29e36739653c06c3d61c07f39a139e1c07f3f01",
-    "iris.tests.experimental.test_animate.IntegrationTest.test_cube_animation.0": "fe81c17e817e3e81817e3e81857e7a817e81c17e7e81c17e7a81817e817e8c2e",
-    "iris.tests.experimental.test_animate.IntegrationTest.test_cube_animation.1": "fe81857e817e7a85817e7a81857e7e817e81917a7e81817e7a81817e817e843e",
-    "iris.tests.experimental.test_animate.IntegrationTest.test_cube_animation.2": "be81817ec17e7a81c17e7e81857e3e803e81817a3e81c17e7a81c17ec97e2c2f",
+    "gallery_tests.test_plot_zonal_means.0": "b45b3071c9a4c9a6c69c363cc327cbb3cb9634d8c9e63cf336738c6634d8c384",
+    "iris.tests.integration.plot.test_animate.IntegrationTest.test_cube_animation.0": "fe81c17e817e3e81817e3e81857e7a817e81c17e7e81c17e7a81817e817e8c2e",
+    "iris.tests.integration.plot.test_animate.IntegrationTest.test_cube_animation.1": "fe81857e817e7a85817e7a81857e7e817e81917a7e81817e7a81817e817e843e",
+    "iris.tests.integration.plot.test_animate.IntegrationTest.test_cube_animation.2": "be81817ec17e7a81c17e7e81857e3e803e81817a3e81c17e7a81c17ec97e2c2f",
     "iris.tests.integration.plot.test_plot_2d_coords.Test.test_2d_coord_bounds_northpolarstereo.0": "e59661969e699659c0f719a6c967339a1992c07f3649c09c3f612669c07b3f66",
     "iris.tests.integration.plot.test_plot_2d_coords.Test.test_2d_coord_bounds_platecarree.0": "ee856299954a1da699b6915ec25b6e419729c42c3f84bd9fa6d262d1d1dac076",
     "iris.tests.integration.plot.test_plot_2d_coords.Test2dContour.test_2d_coords_contour.0": "b4b2643ecb05cb43b0f23d80c53c4e1d3e5990eb1f81c19f2f983cb1c4ff3e42",
diff --git a/lib/iris/tests/results/name/NAMEII_field__no_time_averaging.cml b/lib/iris/tests/results/name/NAMEII_field__no_time_averaging.cml
new file mode 100644
index 0000000000..9bc2c0d1ac
--- /dev/null
+++ b/lib/iris/tests/results/name/NAMEII_field__no_time_averaging.cml
@@ -0,0 +1,47 @@
+<?xml version="1.0" ?>
+<cubes xmlns="urn:x-iris:cubeml-0.2">
+  <cube dtype="float32" long_name="VOLCANIC_ASH_AIR_CONCENTRATION" units="g/m2">
+    <attributes>
+      <attribute name="End of release" value="0000UTC 13/01/2011"/>
+      <attribute name="Forecast duration" value="6 hours"/>
+      <attribute name="Met data" value="NWP Flow.ECMWF ERAInt Regional"/>
+      <attribute name="NAME Version" value="NAME III (version 7.2)"/>
+      <attribute name="Quantity" value="Air Concentration"/>
+      <attribute name="Release height" value="Multiple Sources"/>
+      <attribute name="Release location" value="Multiple Sources"/>
+      <attribute name="Release rate" value="Multiple Sources"/>
+      <attribute name="Run time" value="1333UTC 07/12/2018"/>
+      <attribute name="Species" value="VOLCANIC_ASH"/>
+      <attribute name="Species Category" value="VOLCANIC"/>
+      <attribute name="Start of release" value="2200UTC 12/01/2011"/>
+      <attribute name="Time Av or Int" value="No time averaging"/>
+      <attribute name="Title" value="VA_Tutorial"/>
+    </attributes>
+    <coords>
+      <coord datadims="[0]">
+        <dimCoord bounds="[[29.9375, 30.0625],
+		[30.0625, 30.1875]]" id="77a50eb5" points="[30.0, 30.125]" shape="(2,)" standard_name="latitude" units="Unit('degrees')" value_type="float64">
+          <geogCS earth_radius="6371229.0"/>
+        </dimCoord>
+      </coord>
+      <coord datadims="[1]">
+        <dimCoord bounds="[[9.9375, 10.0625],
+		[10.0625, 10.1875]]" id="f913a8b3" points="[10.0, 10.125]" shape="(2,)" standard_name="longitude" units="Unit('degrees')" value_type="float64">
+          <geogCS earth_radius="6371229.0"/>
+        </dimCoord>
+      </coord>
+      <coord>
+        <dimCoord bounds="[[359686.0, 359686.0]]" id="cb784457" points="[359686.0]" shape="(1,)" standard_name="time" units="Unit('hours since 1970-01-01 00:00:00', calendar='standard')" value_type="float64"/>
+      </coord>
+      <coord>
+        <auxCoord id="e92ff7b7" long_name="z" points="[Vertical integral]" shape="(1,)" units="Unit('no_unit')" value_type="string">
+          <attributes>
+            <attribute name="positive" value="up"/>
+          </attributes>
+        </auxCoord>
+      </coord>
+    </coords>
+    <cellMethods/>
+    <data dtype="float32" shape="(2, 2)" state="loaded"/>
+  </cube>
+</cubes>
diff --git a/lib/iris/tests/results/name/NAMEII_field__no_time_averaging_0.cml b/lib/iris/tests/results/name/NAMEII_field__no_time_averaging_0.cml
new file mode 100644
index 0000000000..8d1ad620d0
--- /dev/null
+++ b/lib/iris/tests/results/name/NAMEII_field__no_time_averaging_0.cml
@@ -0,0 +1,47 @@
+<?xml version="1.0" ?>
+<cubes xmlns="urn:x-iris:cubeml-0.2">
+  <cube dtype="float32" long_name="VOLCANIC_ASH_AIR_CONCENTRATION" units="g/m2">
+    <attributes>
+      <attribute name="End of release" value="0000UTC 13/01/2011"/>
+      <attribute name="Forecast duration" value="6 hours"/>
+      <attribute name="Met data" value="NWP Flow.ECMWF ERAInt Regional"/>
+      <attribute name="NAME Version" value="NAME III (version 7.2)"/>
+      <attribute name="Quantity" value="Air Concentration"/>
+      <attribute name="Release height" value="Multiple Sources"/>
+      <attribute name="Release location" value="Multiple Sources"/>
+      <attribute name="Release rate" value="Multiple Sources"/>
+      <attribute name="Run time" value="1333UTC 07/12/2018"/>
+      <attribute name="Species" value="VOLCANIC_ASH"/>
+      <attribute name="Species Category" value="VOLCANIC"/>
+      <attribute name="Start of release" value="2200UTC 12/01/2011"/>
+      <attribute name="Time Av or Int" value="No time averaging"/>
+      <attribute name="Title" value="VA_Tutorial"/>
+    </attributes>
+    <coords>
+      <coord datadims="[0]">
+        <dimCoord bounds="[[29.9375, 30.0625],
+		[30.0625, 30.1875]]" id="77a50eb5" points="[30.0, 30.125]" shape="(2,)" standard_name="latitude" units="Unit('degrees')" value_type="float64">
+          <geogCS earth_radius="6371229.0"/>
+        </dimCoord>
+      </coord>
+      <coord datadims="[1]">
+        <dimCoord bounds="[[9.9375, 10.0625],
+		[10.0625, 10.1875]]" id="f913a8b3" points="[10.0, 10.125]" shape="(2,)" standard_name="longitude" units="Unit('degrees')" value_type="float64">
+          <geogCS earth_radius="6371229.0"/>
+        </dimCoord>
+      </coord>
+      <coord>
+        <dimCoord bounds="[[359686.0, 359686.0]]" id="cb784457" points="[359686.0]" shape="(1,)" standard_name="time" units="Unit('hours since 1970-01-01 00:00:00', calendar='standard')" value_type="float64"/>
+      </coord>
+      <coord>
+        <auxCoord id="e92ff7b7" long_name="z" points="[Vertical integral]" shape="(1,)" units="Unit('no_unit')" value_type="string">
+          <attributes>
+            <attribute name="positive" value="up"/>
+          </attributes>
+        </auxCoord>
+      </coord>
+    </coords>
+    <cellMethods/>
+    <data checksum="0xecbb4b55" dtype="float32" shape="(2, 2)"/>
+  </cube>
+</cubes>
diff --git a/lib/iris/tests/results/trajectory/hybrid_height.cml b/lib/iris/tests/results/trajectory/hybrid_height.cml
index 972fa7b330..28e821b900 100644
--- a/lib/iris/tests/results/trajectory/hybrid_height.cml
+++ b/lib/iris/tests/results/trajectory/hybrid_height.cml
@@ -60,7 +60,7 @@
     <cellMethods/>
     <data checksum="0x2acdccca" dtype="int64" shape="(3, 4, 5, 6)"/>
   </cube>
-  <cube dtype="float64" standard_name="air_temperature" units="K">
+  <cube dtype="int64" standard_name="air_temperature" units="K">
     <coords>
       <coord datadims="[1, 2]">
         <auxCoord id="9041e969" points="[[5090, 5740, 6090, 6440],
@@ -95,6 +95,6 @@
       </coord>
     </coords>
     <cellMethods/>
-    <data checksum="0x11cdc1c8" dtype="float64" shape="(3, 4, 4)"/>
+    <data checksum="0x07fcebe7" dtype="int64" shape="(3, 4, 4)"/>
   </cube>
 </cubes>
diff --git a/lib/iris/tests/results/trajectory/tri_polar_latitude_slice.cml b/lib/iris/tests/results/trajectory/tri_polar_latitude_slice.cml
index 750d597493..7b5bbfc086 100644
--- a/lib/iris/tests/results/trajectory/tri_polar_latitude_slice.cml
+++ b/lib/iris/tests/results/trajectory/tri_polar_latitude_slice.cml
@@ -1,6 +1,6 @@
 <?xml version="1.0" ?>
 <cubes xmlns="urn:x-iris:cubeml-0.2">
-  <cube dtype="float64" long_name="Temperature" standard_name="sea_water_potential_temperature" units="degC" var_name="votemper">
+  <cube dtype="float32" long_name="Temperature" standard_name="sea_water_potential_temperature" units="degC" var_name="votemper">
     <attributes>
       <attribute name="Conventions" value="CF-1.1"/>
       <attribute name="DOMAIN_DIM_N001" value="x"/>
@@ -144,6 +144,6 @@
         <coord name="time"/>
       </cellMethod>
     </cellMethods>
-    <data checksum="0x3a8195d5" dtype="float64" shape="(1, 31, 85)"/>
+    <data checksum="0x49d623f6" dtype="float32" mask_checksum="0x117b1ebc" shape="(1, 31, 85)"/>
   </cube>
 </cubes>
diff --git a/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/collapse_all_dims.cml b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/collapse_all_dims.cml
index 9fc80a0e4d..e318abad67 100644
--- a/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/collapse_all_dims.cml
+++ b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/collapse_all_dims.cml
@@ -50,12 +50,7 @@
         </dimCoord>
       </coord>
       <coord>
-        <auxCoord bounds="[[3100, 3199]]" id="81f04efd" long_name="long-name" points="[3149]" shape="(1,)" standard_name="longitude" units="Unit('degrees_east')" value_type="int64">
-          <attributes>
-            <attribute name="a" value="1"/>
-            <attribute name="b" value="c"/>
-          </attributes>
-        </auxCoord>
+        <auxCoord bounds="[[3100, 3199]]" id="e0db29d6" points="[3149]" shape="(1,)" standard_name="longitude" units="Unit('unknown')" value_type="int64"/>
       </coord>
       <coord datadims="[2]">
         <meshCoord bounds="[[1100, 1101, 1102, 1103],
@@ -64,12 +59,7 @@
 		..., 
 		[1188, 1189, 1190, 1191],
 		[1192, 1193, 1194, 1195],
-		[1196, 1197, 1198, 1199]]" id="81f04efd" long_name="long-name" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('degrees_east')" value_type="int64">
-          <attributes>
-            <attribute name="a" value="1"/>
-            <attribute name="b" value="c"/>
-          </attributes>
-        </meshCoord>
+		[1196, 1197, 1198, 1199]]" id="e0db29d6" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('unknown')" value_type="int64"/>
       </coord>
       <coord datadims="[1]">
         <dimCoord id="38fd0305" points="[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
diff --git a/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/collapse_last_dims.cml b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/collapse_last_dims.cml
index 9fc80a0e4d..e318abad67 100644
--- a/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/collapse_last_dims.cml
+++ b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/collapse_last_dims.cml
@@ -50,12 +50,7 @@
         </dimCoord>
       </coord>
       <coord>
-        <auxCoord bounds="[[3100, 3199]]" id="81f04efd" long_name="long-name" points="[3149]" shape="(1,)" standard_name="longitude" units="Unit('degrees_east')" value_type="int64">
-          <attributes>
-            <attribute name="a" value="1"/>
-            <attribute name="b" value="c"/>
-          </attributes>
-        </auxCoord>
+        <auxCoord bounds="[[3100, 3199]]" id="e0db29d6" points="[3149]" shape="(1,)" standard_name="longitude" units="Unit('unknown')" value_type="int64"/>
       </coord>
       <coord datadims="[2]">
         <meshCoord bounds="[[1100, 1101, 1102, 1103],
@@ -64,12 +59,7 @@
 		..., 
 		[1188, 1189, 1190, 1191],
 		[1192, 1193, 1194, 1195],
-		[1196, 1197, 1198, 1199]]" id="81f04efd" long_name="long-name" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('degrees_east')" value_type="int64">
-          <attributes>
-            <attribute name="a" value="1"/>
-            <attribute name="b" value="c"/>
-          </attributes>
-        </meshCoord>
+		[1196, 1197, 1198, 1199]]" id="e0db29d6" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('unknown')" value_type="int64"/>
       </coord>
       <coord datadims="[1]">
         <dimCoord id="38fd0305" points="[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
diff --git a/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/collapse_middle_dim.cml b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/collapse_middle_dim.cml
index 82b7e25aa3..82c79a7577 100644
--- a/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/collapse_middle_dim.cml
+++ b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/collapse_middle_dim.cml
@@ -53,12 +53,7 @@
 		..., 
 		[1188, 1189, 1190, 1191],
 		[1192, 1193, 1194, 1195],
-		[1196, 1197, 1198, 1199]]" id="81f04efd" long_name="long-name" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('degrees_east')" value_type="int64">
-          <attributes>
-            <attribute name="a" value="1"/>
-            <attribute name="b" value="c"/>
-          </attributes>
-        </meshCoord>
+		[1196, 1197, 1198, 1199]]" id="e0db29d6" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('unknown')" value_type="int64"/>
       </coord>
       <coord datadims="[1]">
         <dimCoord id="38fd0305" points="[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
diff --git a/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/collapse_zeroth_dim.cml b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/collapse_zeroth_dim.cml
index 82b7e25aa3..82c79a7577 100644
--- a/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/collapse_zeroth_dim.cml
+++ b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/collapse_zeroth_dim.cml
@@ -53,12 +53,7 @@
 		..., 
 		[1188, 1189, 1190, 1191],
 		[1192, 1193, 1194, 1195],
-		[1196, 1197, 1198, 1199]]" id="81f04efd" long_name="long-name" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('degrees_east')" value_type="int64">
-          <attributes>
-            <attribute name="a" value="1"/>
-            <attribute name="b" value="c"/>
-          </attributes>
-        </meshCoord>
+		[1196, 1197, 1198, 1199]]" id="e0db29d6" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('unknown')" value_type="int64"/>
       </coord>
       <coord datadims="[1]">
         <dimCoord id="38fd0305" points="[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
diff --git a/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/slice.cml b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/slice.cml
index 82b7e25aa3..82c79a7577 100644
--- a/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/slice.cml
+++ b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/slice.cml
@@ -53,12 +53,7 @@
 		..., 
 		[1188, 1189, 1190, 1191],
 		[1192, 1193, 1194, 1195],
-		[1196, 1197, 1198, 1199]]" id="81f04efd" long_name="long-name" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('degrees_east')" value_type="int64">
-          <attributes>
-            <attribute name="a" value="1"/>
-            <attribute name="b" value="c"/>
-          </attributes>
-        </meshCoord>
+		[1196, 1197, 1198, 1199]]" id="e0db29d6" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('unknown')" value_type="int64"/>
       </coord>
       <coord datadims="[1]">
         <dimCoord id="38fd0305" points="[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
diff --git a/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/transposed.cml b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/transposed.cml
index 82b7e25aa3..82c79a7577 100644
--- a/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/transposed.cml
+++ b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/transposed.cml
@@ -53,12 +53,7 @@
 		..., 
 		[1188, 1189, 1190, 1191],
 		[1192, 1193, 1194, 1195],
-		[1196, 1197, 1198, 1199]]" id="81f04efd" long_name="long-name" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('degrees_east')" value_type="int64">
-          <attributes>
-            <attribute name="a" value="1"/>
-            <attribute name="b" value="c"/>
-          </attributes>
-        </meshCoord>
+		[1196, 1197, 1198, 1199]]" id="e0db29d6" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('unknown')" value_type="int64"/>
       </coord>
       <coord datadims="[1]">
         <dimCoord id="38fd0305" points="[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
diff --git a/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/collapse_all_dims.cml b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/collapse_all_dims.cml
index 9fc80a0e4d..e318abad67 100644
--- a/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/collapse_all_dims.cml
+++ b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/collapse_all_dims.cml
@@ -50,12 +50,7 @@
         </dimCoord>
       </coord>
       <coord>
-        <auxCoord bounds="[[3100, 3199]]" id="81f04efd" long_name="long-name" points="[3149]" shape="(1,)" standard_name="longitude" units="Unit('degrees_east')" value_type="int64">
-          <attributes>
-            <attribute name="a" value="1"/>
-            <attribute name="b" value="c"/>
-          </attributes>
-        </auxCoord>
+        <auxCoord bounds="[[3100, 3199]]" id="e0db29d6" points="[3149]" shape="(1,)" standard_name="longitude" units="Unit('unknown')" value_type="int64"/>
       </coord>
       <coord datadims="[2]">
         <meshCoord bounds="[[1100, 1101, 1102, 1103],
@@ -64,12 +59,7 @@
 		..., 
 		[1188, 1189, 1190, 1191],
 		[1192, 1193, 1194, 1195],
-		[1196, 1197, 1198, 1199]]" id="81f04efd" long_name="long-name" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('degrees_east')" value_type="int64">
-          <attributes>
-            <attribute name="a" value="1"/>
-            <attribute name="b" value="c"/>
-          </attributes>
-        </meshCoord>
+		[1196, 1197, 1198, 1199]]" id="e0db29d6" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('unknown')" value_type="int64"/>
       </coord>
       <coord datadims="[1]">
         <dimCoord id="38fd0305" points="[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
diff --git a/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/collapse_last_dims.cml b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/collapse_last_dims.cml
index 9fc80a0e4d..e318abad67 100644
--- a/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/collapse_last_dims.cml
+++ b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/collapse_last_dims.cml
@@ -50,12 +50,7 @@
         </dimCoord>
       </coord>
       <coord>
-        <auxCoord bounds="[[3100, 3199]]" id="81f04efd" long_name="long-name" points="[3149]" shape="(1,)" standard_name="longitude" units="Unit('degrees_east')" value_type="int64">
-          <attributes>
-            <attribute name="a" value="1"/>
-            <attribute name="b" value="c"/>
-          </attributes>
-        </auxCoord>
+        <auxCoord bounds="[[3100, 3199]]" id="e0db29d6" points="[3149]" shape="(1,)" standard_name="longitude" units="Unit('unknown')" value_type="int64"/>
       </coord>
       <coord datadims="[2]">
         <meshCoord bounds="[[1100, 1101, 1102, 1103],
@@ -64,12 +59,7 @@
 		..., 
 		[1188, 1189, 1190, 1191],
 		[1192, 1193, 1194, 1195],
-		[1196, 1197, 1198, 1199]]" id="81f04efd" long_name="long-name" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('degrees_east')" value_type="int64">
-          <attributes>
-            <attribute name="a" value="1"/>
-            <attribute name="b" value="c"/>
-          </attributes>
-        </meshCoord>
+		[1196, 1197, 1198, 1199]]" id="e0db29d6" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('unknown')" value_type="int64"/>
       </coord>
       <coord datadims="[1]">
         <dimCoord id="38fd0305" points="[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
diff --git a/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/collapse_middle_dim.cml b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/collapse_middle_dim.cml
index 82b7e25aa3..82c79a7577 100644
--- a/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/collapse_middle_dim.cml
+++ b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/collapse_middle_dim.cml
@@ -53,12 +53,7 @@
 		..., 
 		[1188, 1189, 1190, 1191],
 		[1192, 1193, 1194, 1195],
-		[1196, 1197, 1198, 1199]]" id="81f04efd" long_name="long-name" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('degrees_east')" value_type="int64">
-          <attributes>
-            <attribute name="a" value="1"/>
-            <attribute name="b" value="c"/>
-          </attributes>
-        </meshCoord>
+		[1196, 1197, 1198, 1199]]" id="e0db29d6" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('unknown')" value_type="int64"/>
       </coord>
       <coord datadims="[1]">
         <dimCoord id="38fd0305" points="[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
diff --git a/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/collapse_zeroth_dim.cml b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/collapse_zeroth_dim.cml
index 82b7e25aa3..82c79a7577 100644
--- a/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/collapse_zeroth_dim.cml
+++ b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/collapse_zeroth_dim.cml
@@ -53,12 +53,7 @@
 		..., 
 		[1188, 1189, 1190, 1191],
 		[1192, 1193, 1194, 1195],
-		[1196, 1197, 1198, 1199]]" id="81f04efd" long_name="long-name" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('degrees_east')" value_type="int64">
-          <attributes>
-            <attribute name="a" value="1"/>
-            <attribute name="b" value="c"/>
-          </attributes>
-        </meshCoord>
+		[1196, 1197, 1198, 1199]]" id="e0db29d6" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('unknown')" value_type="int64"/>
       </coord>
       <coord datadims="[1]">
         <dimCoord id="38fd0305" points="[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
diff --git a/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/slice.cml b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/slice.cml
index 82b7e25aa3..82c79a7577 100644
--- a/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/slice.cml
+++ b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/slice.cml
@@ -53,12 +53,7 @@
 		..., 
 		[1188, 1189, 1190, 1191],
 		[1192, 1193, 1194, 1195],
-		[1196, 1197, 1198, 1199]]" id="81f04efd" long_name="long-name" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('degrees_east')" value_type="int64">
-          <attributes>
-            <attribute name="a" value="1"/>
-            <attribute name="b" value="c"/>
-          </attributes>
-        </meshCoord>
+		[1196, 1197, 1198, 1199]]" id="e0db29d6" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('unknown')" value_type="int64"/>
       </coord>
       <coord datadims="[1]">
         <dimCoord id="38fd0305" points="[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
diff --git a/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/transposed.cml b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/transposed.cml
index 82b7e25aa3..82c79a7577 100644
--- a/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/transposed.cml
+++ b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/transposed.cml
@@ -53,12 +53,7 @@
 		..., 
 		[1188, 1189, 1190, 1191],
 		[1192, 1193, 1194, 1195],
-		[1196, 1197, 1198, 1199]]" id="81f04efd" long_name="long-name" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('degrees_east')" value_type="int64">
-          <attributes>
-            <attribute name="a" value="1"/>
-            <attribute name="b" value="c"/>
-          </attributes>
-        </meshCoord>
+		[1196, 1197, 1198, 1199]]" id="e0db29d6" points="[3100, 3101, 3102, ..., 3197, 3198, 3199]" shape="(100,)" standard_name="longitude" units="Unit('unknown')" value_type="int64"/>
       </coord>
       <coord datadims="[1]">
         <dimCoord id="38fd0305" points="[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
diff --git a/lib/iris/tests/test_cell.py b/lib/iris/tests/test_cell.py
index 03d3fa7d7c..21d2603072 100644
--- a/lib/iris/tests/test_cell.py
+++ b/lib/iris/tests/test_cell.py
@@ -169,9 +169,35 @@ def test_coord_bounds_cmp(self):
         self.assertTrue(self.e < 2)
 
     def test_cell_cell_cmp(self):
+        self.e = iris.coords.Cell(1)
+        self.f = iris.coords.Cell(1)
+
+        self.assertTrue(self.e == self.f)
+        self.assertEqual(hash(self.e), hash(self.f))
+
+        self.e = iris.coords.Cell(1)
+        self.f = iris.coords.Cell(1, [0, 2])
+
+        self.assertFalse(self.e == self.f)
+        self.assertNotEqual(hash(self.e), hash(self.f))
+
+        self.e = iris.coords.Cell(1, [0, 2])
+        self.f = iris.coords.Cell(1, [0, 2])
+
+        self.assertTrue(self.e == self.f)
+        self.assertEqual(hash(self.e), hash(self.f))
+
+        self.e = iris.coords.Cell(1, [0, 2])
+        self.f = iris.coords.Cell(1, [2, 0])
+
+        self.assertTrue(self.e == self.f)
+        self.assertEqual(hash(self.e), hash(self.f))
+
         self.e = iris.coords.Cell(0.7, [1.1, 1.9])
         self.f = iris.coords.Cell(0.8, [1.1, 1.9])
 
+        self.assertFalse(self.e == self.f)
+        self.assertNotEqual(hash(self.e), hash(self.f))
         self.assertFalse(self.e > self.f)
         self.assertTrue(self.e <= self.f)
         self.assertTrue(self.f >= self.e)
diff --git a/lib/iris/tests/test_constraints.py b/lib/iris/tests/test_constraints.py
index 1972cdeb90..e568105f91 100644
--- a/lib/iris/tests/test_constraints.py
+++ b/lib/iris/tests/test_constraints.py
@@ -67,6 +67,27 @@ def test_constraints(self):
         sub_list = self.slices.extract(constraint)
         self.assertEqual(len(sub_list), 70 * 6)
 
+    def test_coord_availability(self):
+        # "model_level_number" coordinate available
+        constraint = iris.Constraint(model_level_number=lambda x: True)
+        result = self.slices.extract(constraint)
+        self.assertTrue(result)
+
+        # "wibble" coordinate is not available
+        constraint = iris.Constraint(wibble=lambda x: False)
+        result = self.slices.extract(constraint)
+        self.assertFalse(result)
+
+        # "wibble" coordinate is not available
+        constraint = iris.Constraint(wibble=lambda x: True)
+        result = self.slices.extract(constraint)
+        self.assertFalse(result)
+
+        # "lambda x: False" always (confusingly) throws away the cube
+        constraint = iris.Constraint(model_level_number=lambda x: False)
+        result = self.slices.extract(constraint)
+        self.assertFalse(result)
+
     def test_mismatched_type(self):
         constraint = iris.Constraint(model_level_number="aardvark")
         sub_list = self.slices.extract(constraint)
diff --git a/lib/iris/tests/test_mapping.py b/lib/iris/tests/test_mapping.py
index a71385b5bc..202c319b61 100644
--- a/lib/iris/tests/test_mapping.py
+++ b/lib/iris/tests/test_mapping.py
@@ -242,12 +242,6 @@ def test_pcolormesh(self):
         iplt.pcolormesh(self.cube)
         self.check_graphic()
 
-    def test_grid(self):
-        iplt.pcolormesh(self.cube, facecolors="none", edgecolors="blue")
-        # the result is a graphic which has coloured edges. This is a mpl bug,
-        # see https://github.com/matplotlib/matplotlib/issues/1302
-        self.check_graphic()
-
     def test_outline(self):
         iplt.outline(self.cube)
         self.check_graphic()
diff --git a/lib/iris/tests/test_name.py b/lib/iris/tests/test_name.py
index 2843673da8..b4e91bafd7 100644
--- a/lib/iris/tests/test_name.py
+++ b/lib/iris/tests/test_name.py
@@ -8,6 +8,9 @@
 # import iris tests first so that some things can be initialised before
 # importing anything else
 import iris.tests as tests  # isort:skip
+
+import tempfile
+
 import iris
 
 
@@ -39,7 +42,7 @@ def test_NAMEIII_version2(self):
         )
         self.assertCMLApproxData(cubes, ("name", "NAMEIII_version2.cml"))
 
-    def test_NAMEII_trajectory(self):
+    def test_NAMEIII_trajectory(self):
         cubes = iris.load(
             tests.get_data_path(("NAME", "NAMEIII_trajectory.txt"))
         )
@@ -48,6 +51,32 @@ def test_NAMEII_trajectory(self):
             cubes, ("name", "NAMEIII_trajectory.cml"), checksum=False
         )
 
+    def test_NAMEII__no_time_averaging(self):
+        cubes = iris.load(
+            tests.get_data_path(("NAME", "NAMEII_no_time_averaging.txt"))
+        )
+
+        # Also check that it saves without error.
+        # This was previously failing, see https://github.com/SciTools/iris/issues/3288
+        with tempfile.TemporaryDirectory() as temp_dirpath:
+            iris.save(cubes, temp_dirpath + "/tmp.nc")
+
+        self.assertCML(
+            cubes[0],
+            (
+                "name",
+                "NAMEII_field__no_time_averaging_0.cml",
+            ),
+        )
+        self.assertCML(
+            cubes,
+            (
+                "name",
+                "NAMEII_field__no_time_averaging.cml",
+            ),
+            checksum=False,
+        )
+
 
 if __name__ == "__main__":
     tests.main()
diff --git a/lib/iris/tests/unit/analysis/cartography/test__get_lon_lat_coords.py b/lib/iris/tests/unit/analysis/cartography/test__get_lon_lat_coords.py
new file mode 100644
index 0000000000..612e5d8ecf
--- /dev/null
+++ b/lib/iris/tests/unit/analysis/cartography/test__get_lon_lat_coords.py
@@ -0,0 +1,114 @@
+# Copyright Iris contributors
+#
+# This file is part of Iris and is released under the LGPL license.
+# See COPYING and COPYING.LESSER in the root of the repository for full
+# licensing details.
+"""Test function :func:`iris.analysis.cartography._get_lon_lat_coords"""
+
+import pytest
+
+from iris.analysis.cartography import _get_lon_lat_coords as g_lon_lat
+from iris.coords import AuxCoord
+from iris.tests.stock import lat_lon_cube
+
+
+@pytest.fixture
+def dim_only_cube():
+    return lat_lon_cube()
+
+
+def test_dim_only(dim_only_cube):
+    t_lat, t_lon = dim_only_cube.dim_coords
+
+    lon, lat = g_lon_lat(dim_only_cube)
+
+    assert lon == t_lon
+    assert lat == t_lat
+
+
+@pytest.fixture
+def dim_aux_cube(dim_only_cube):
+    lat_dim, lon_dim = dim_only_cube.dim_coords
+
+    lat_aux = AuxCoord.from_coord(lat_dim)
+    lat_aux.standard_name = "grid_latitude"
+    lon_aux = AuxCoord.from_coord(lon_dim)
+    lon_aux.standard_name = "grid_longitude"
+
+    dim_aux_cube = dim_only_cube
+    dim_aux_cube.add_aux_coord(lat_aux, 0)
+    dim_aux_cube.add_aux_coord(lon_aux, 1)
+
+    return dim_aux_cube
+
+
+def test_dim_aux(dim_aux_cube):
+    t_lat_dim, t_lon_dim = dim_aux_cube.dim_coords
+
+    lon, lat = g_lon_lat(dim_aux_cube)
+
+    assert lon == t_lon_dim
+    assert lat == t_lat_dim
+
+
+@pytest.fixture
+def aux_only_cube(dim_aux_cube):
+    lon_dim, lat_dim = dim_aux_cube.dim_coords
+
+    aux_only_cube = dim_aux_cube
+    aux_only_cube.remove_coord(lon_dim)
+    aux_only_cube.remove_coord(lat_dim)
+
+    return dim_aux_cube
+
+
+def test_aux_only(aux_only_cube):
+    aux_lat, aux_lon = aux_only_cube.aux_coords
+
+    lon, lat = g_lon_lat(aux_only_cube)
+
+    assert lon == aux_lon
+    assert lat == aux_lat
+
+
+@pytest.fixture
+def double_dim_cube(dim_only_cube):
+    double_dim_cube = dim_only_cube
+    double_dim_cube.coord("latitude").standard_name = "grid_longitude"
+
+    return double_dim_cube
+
+
+def test_double_dim(double_dim_cube):
+    t_error_message = "with multiple.*is currently disallowed"
+
+    with pytest.raises(ValueError, match=t_error_message):
+        g_lon_lat(double_dim_cube)
+
+
+@pytest.fixture
+def double_aux_cube(aux_only_cube):
+    double_aux_cube = aux_only_cube
+    double_aux_cube.coord("grid_latitude").standard_name = "longitude"
+
+    return double_aux_cube
+
+
+def test_double_aux(double_aux_cube):
+    t_error_message = "with multiple.*is currently disallowed"
+
+    with pytest.raises(ValueError, match=t_error_message):
+        g_lon_lat(double_aux_cube)
+
+
+@pytest.fixture
+def missing_lat_cube(dim_only_cube):
+    missing_lat_cube = dim_only_cube
+    missing_lat_cube.remove_coord("latitude")
+
+    return missing_lat_cube
+
+
+def test_missing_coord(missing_lat_cube):
+    with pytest.raises(IndexError):
+        g_lon_lat(missing_lat_cube)
diff --git a/lib/iris/tests/unit/analysis/cartography/test_rotate_winds.py b/lib/iris/tests/unit/analysis/cartography/test_rotate_winds.py
index 7bd8fdb597..7952b3bb46 100644
--- a/lib/iris/tests/unit/analysis/cartography/test_rotate_winds.py
+++ b/lib/iris/tests/unit/analysis/cartography/test_rotate_winds.py
@@ -16,6 +16,7 @@
 import cartopy.crs as ccrs
 import numpy as np
 import numpy.ma as ma
+import pytest
 
 from iris.analysis.cartography import rotate_winds, unrotate_pole
 import iris.coord_systems
@@ -410,7 +411,11 @@ def test_transposed(self):
 class TestMasking(tests.IrisTest):
     def test_rotated_to_osgb(self):
         # Rotated Pole data with large extent.
-        x = np.linspace(311.9, 391.1, 10)
+        # A 'correct' answer is not known for this test; it is therefore
+        #  written as a 'benchmark' style test - a change in behaviour will
+        #  cause a test failure, requiring developers to approve/reject the
+        #  new behaviour.
+        x = np.linspace(221.9, 301.1, 10)
         y = np.linspace(-23.6, 24.8, 8)
         u, v = uv_cubes(x, y)
         ut, vt = rotate_winds(u, v, iris.coord_systems.OSGB())
@@ -422,14 +427,14 @@ def test_rotated_to_osgb(self):
         # Snapshot of mask with fixed tolerance of atol=2e-3
         expected_mask = np.array(
             [
-                [1, 1, 1, 0, 0, 0, 0, 0, 0, 1],
-                [1, 1, 1, 0, 0, 0, 0, 0, 0, 1],
-                [1, 1, 1, 1, 0, 0, 0, 0, 1, 1],
-                [1, 1, 1, 1, 0, 0, 0, 0, 1, 1],
-                [1, 1, 1, 1, 0, 0, 0, 0, 1, 1],
-                [1, 1, 1, 1, 1, 0, 0, 1, 1, 1],
-                [1, 1, 1, 1, 1, 0, 0, 1, 1, 1],
-                [1, 1, 1, 1, 1, 0, 0, 1, 1, 1],
+                [0, 0, 0, 1, 1, 1, 0, 0, 0, 1],
+                [0, 0, 0, 0, 1, 1, 1, 0, 1, 1],
+                [0, 0, 0, 0, 0, 1, 1, 1, 1, 1],
+                [0, 0, 0, 0, 1, 1, 1, 0, 0, 0],
+                [0, 0, 0, 1, 1, 1, 1, 1, 0, 0],
+                [0, 1, 1, 1, 1, 1, 1, 0, 0, 0],
+                [0, 1, 1, 1, 0, 1, 1, 1, 0, 0],
+                [0, 1, 0, 0, 0, 0, 1, 1, 1, 0],
             ],
             np.bool_,
         )
@@ -443,7 +448,7 @@ def test_rotated_to_osgb(self):
         # Calculate percentage error (note there are no zero magnitudes
         # so we can divide safely).
         anom = 100.0 * np.abs(res_mag - expected_mag) / expected_mag
-        self.assertTrue(anom[~ut.data.mask].max() < 0.1)
+        assert anom[~ut.data.mask].max() == pytest.approx(0.3227935)
 
     def test_rotated_to_unrotated(self):
         # Suffiently accurate so that no mask is introduced.
diff --git a/lib/iris/tests/unit/analysis/regrid/test__CurvilinearRegridder.py b/lib/iris/tests/unit/analysis/regrid/test__CurvilinearRegridder.py
index 68db839d06..9b0160aee4 100644
--- a/lib/iris/tests/unit/analysis/regrid/test__CurvilinearRegridder.py
+++ b/lib/iris/tests/unit/analysis/regrid/test__CurvilinearRegridder.py
@@ -15,11 +15,12 @@
 
 from iris.analysis._regrid import CurvilinearRegridder as Regridder
 from iris.analysis.cartography import rotate_pole
+from iris.aux_factory import HybridHeightFactory
 from iris.coord_systems import GeogCS, RotatedGeogCS
 from iris.coords import AuxCoord, DimCoord
 from iris.cube import Cube
 from iris.fileformats.pp import EARTH_RADIUS
-from iris.tests.stock import global_pp, lat_lon_cube
+from iris.tests.stock import global_pp, lat_lon_cube, realistic_4d
 
 RESULT_DIR = ("analysis", "regrid")
 
@@ -169,6 +170,88 @@ def test_caching(self):
         )
 
 
+class Test__derived_coord(tests.IrisTest):
+    def setUp(self):
+        src = realistic_4d()[0]
+        tgt = realistic_4d()
+        new_lon, new_lat = np.meshgrid(
+            src.coord("grid_longitude").points,
+            src.coord("grid_latitude").points,
+        )
+        coord_system = src.coord("grid_latitude").coord_system
+        lat = AuxCoord(
+            new_lat, standard_name="latitude", coord_system=coord_system
+        )
+        lon = AuxCoord(
+            new_lon, standard_name="longitude", coord_system=coord_system
+        )
+        lat_t = AuxCoord(
+            new_lat.T, standard_name="latitude", coord_system=coord_system
+        )
+        lon_t = AuxCoord(
+            new_lon.T, standard_name="longitude", coord_system=coord_system
+        )
+
+        src.remove_coord("grid_latitude")
+        src.remove_coord("grid_longitude")
+        src_t = src.copy()
+        src.add_aux_coord(lat, [1, 2])
+        src.add_aux_coord(lon, [1, 2])
+        src_t.add_aux_coord(lat_t, [2, 1])
+        src_t.add_aux_coord(lon_t, [2, 1])
+        self.src = src.copy()
+        self.src_t = src_t
+        self.tgt = tgt
+        self.altitude = src.coord("altitude")
+        transposed_src = src.copy()
+        transposed_src.transpose([0, 2, 1])
+        self.altitude_transposed = transposed_src.coord("altitude")
+
+    def test_no_transpose(self):
+        rg = Regridder(self.src, self.tgt)
+        res = rg(self.src)
+
+        assert len(res.aux_factories) == 1 and isinstance(
+            res.aux_factories[0], HybridHeightFactory
+        )
+        assert np.allclose(res.coord("altitude").points, self.altitude.points)
+
+    def test_cube_transposed(self):
+        rg = Regridder(self.src, self.tgt)
+        transposed_cube = self.src.copy()
+        transposed_cube.transpose([0, 2, 1])
+        res = rg(transposed_cube)
+
+        assert len(res.aux_factories) == 1 and isinstance(
+            res.aux_factories[0], HybridHeightFactory
+        )
+        assert np.allclose(
+            res.coord("altitude").points, self.altitude_transposed.points
+        )
+
+    def test_coord_transposed(self):
+        rg = Regridder(self.src_t, self.tgt)
+        res = rg(self.src_t)
+
+        assert len(res.aux_factories) == 1 and isinstance(
+            res.aux_factories[0], HybridHeightFactory
+        )
+        assert np.allclose(
+            res.coord("altitude").points, self.altitude_transposed.points
+        )
+
+    def test_both_transposed(self):
+        rg = Regridder(self.src_t, self.tgt)
+        transposed_cube = self.src_t.copy()
+        transposed_cube.transpose([0, 2, 1])
+        res = rg(transposed_cube)
+
+        assert len(res.aux_factories) == 1 and isinstance(
+            res.aux_factories[0], HybridHeightFactory
+        )
+        assert np.allclose(res.coord("altitude").points, self.altitude.points)
+
+
 @tests.skip_data
 class Test___call____bad_src(tests.IrisTest):
     def setUp(self):
@@ -219,7 +302,7 @@ def test_multidim(self):
         grid_cube.add_dim_coord(grid_y_coord, 0)
         grid_cube.add_dim_coord(grid_x_coord, 1)
 
-        # Define some key points in true-lat/lon thta have known positions
+        # Define some key points in true-lat/lon that have known positions
         # First 3x2 points in the centre of each output cell.
         x_centres, y_centres = np.meshgrid(
             grid_x_coord.points, grid_y_coord.points
diff --git a/lib/iris/tests/unit/analysis/trajectory/test_interpolate.py b/lib/iris/tests/unit/analysis/trajectory/test_interpolate.py
index dad781ed74..f1b9711068 100644
--- a/lib/iris/tests/unit/analysis/trajectory/test_interpolate.py
+++ b/lib/iris/tests/unit/analysis/trajectory/test_interpolate.py
@@ -12,7 +12,10 @@
 # importing anything else.
 import iris.tests as tests  # isort:skip
 
+from collections import namedtuple
+
 import numpy as np
+import pytest
 
 from iris.analysis.trajectory import interpolate
 from iris.coords import AuxCoord, DimCoord
@@ -38,15 +41,15 @@ def test_unknown_method(self):
             interpolate(cube, sample_point, method="linekar")
 
 
-class TestNearest(tests.IrisTest):
+class TestNearest:
     # Test interpolation with 'nearest' method.
     # This is basically a wrapper to the routine:
     #   'analysis._interpolate_private._nearest_neighbour_indices_ndcoords'.
     # That has its own test, so we don't test the basic calculation
     # exhaustively here.  Instead we check the way it handles the source and
     # result cubes (especially coordinates).
-
-    def setUp(self):
+    @pytest.fixture
+    def src_cube(self):
         cube = iris.tests.stock.simple_3d()
         # Actually, this cube *isn't* terribly realistic, as the lat+lon coords
         # have integer type, which in this case produces some peculiar results.
@@ -54,46 +57,43 @@ def setUp(self):
         for coord_name in ("longitude", "latitude"):
             coord = cube.coord(coord_name)
             coord.points = coord.points.astype(float)
-        self.test_cube = cube
+        return cube
+
+    @pytest.fixture
+    def single_point(self, src_cube):
         # Define coordinates for a single-point testcase.
         y_val, x_val = 0, -90
-        # Work out cube indices of the testpoint.
-        self.single_point_iy = np.where(
-            cube.coord("latitude").points == y_val
-        )[0][0]
-        self.single_point_ix = np.where(
-            cube.coord("longitude").points == x_val
-        )[0][0]
+
         # Use slightly-different values to test nearest-neighbour operation.
-        self.single_sample_point = [
+        sample_point = [
             ("latitude", [y_val + 19.23]),
             ("longitude", [x_val - 17.54]),
         ]
 
-    def test_single_point_same_cube(self):
-        # Check exact result matching for a single point.
-        cube = self.test_cube
-        result = interpolate(cube, self.single_sample_point, method="nearest")
-        # Check that the result is a single trajectory point, exactly equal to
-        # the expected part of the original data.
-        self.assertEqual(result.shape[-1], 1)
-        result = result[..., 0]
-        expected = cube[:, self.single_point_iy, self.single_point_ix]
-        self.assertEqual(result, expected)
+        # Work out cube indices of the testpoint.
+        single_point_iy = np.where(src_cube.coord("latitude").points == y_val)[
+            0
+        ][0]
+        single_point_ix = np.where(
+            src_cube.coord("longitude").points == x_val
+        )[0][0]
 
-    def test_multi_point_same_cube(self):
-        # Check an exact result for multiple points.
-        cube = self.test_cube
+        point = namedtuple("point", "ix iy sample_point")
+        return point(single_point_ix, single_point_iy, sample_point)
+
+    @pytest.fixture
+    def multi_sample_points(self):
         # Use latitude selection to recreate a whole row of the original cube.
-        sample_points = [
+        return [
             ("longitude", [-180, -90, 0, 90]),
             ("latitude", [0, 0, 0, 0]),
         ]
-        result = interpolate(cube, sample_points, method="nearest")
 
+    @pytest.fixture
+    def expected_multipoint_cube(self, src_cube):
         # The result should be identical to a single latitude section of the
         # original, but with modified coords (latitude has 4 repeated zeros).
-        expected = cube[:, 1, :]
+        expected = src_cube[:, 1, :]
         # Result 'longitude' is now an aux coord.
         co_x = expected.coord("longitude")
         expected.remove_coord(co_x)
@@ -104,36 +104,86 @@ def test_multi_point_same_cube(self):
             [0, 0, 0, 0], standard_name="latitude", units="degrees"
         )
         expected.add_aux_coord(co_y, 1)
-        self.assertEqual(result, expected)
 
-    def test_aux_coord_noninterpolation_dim(self):
+        return expected
+
+    def test_single_point_same_cube(self, src_cube, single_point):
+        # Check exact result matching for a single point.
+        result = interpolate(
+            src_cube, single_point.sample_point, method="nearest"
+        )
+        # Check that the result is a single trajectory point, exactly equal to
+        # the expected part of the original data.
+        assert result.shape[-1] == 1
+        result = result[..., 0]
+        expected = src_cube[:, single_point.iy, single_point.ix]
+        assert result == expected
+
+    def test_multi_point_same_cube(
+        self, src_cube, multi_sample_points, expected_multipoint_cube
+    ):
+        # Check an exact result for multiple points.
+        result = interpolate(src_cube, multi_sample_points, method="nearest")
+        assert result == expected_multipoint_cube
+
+    def test_mask_preserved(
+        self, src_cube, multi_sample_points, expected_multipoint_cube
+    ):
+        mask = np.zeros_like(src_cube.data)
+        mask[:, :, 1] = 1
+        src_cube.data = np.ma.array(src_cube.data, mask=mask)
+
+        expected_multipoint_cube.data = np.ma.array(
+            expected_multipoint_cube.data, mask=mask[:, 0]
+        )
+
+        result = interpolate(src_cube, multi_sample_points, method="nearest")
+        assert result == expected_multipoint_cube
+        assert np.allclose(
+            result.data.mask, expected_multipoint_cube.data.mask
+        )
+
+    def test_dtype_preserved(
+        self, src_cube, multi_sample_points, expected_multipoint_cube
+    ):
+        src_cube.data = src_cube.data.astype(np.int16)
+
+        result = interpolate(src_cube, multi_sample_points, method="nearest")
+        assert result == expected_multipoint_cube
+        assert np.allclose(result.data, expected_multipoint_cube.data)
+        assert result.data.dtype == np.int16
+
+    def test_aux_coord_noninterpolation_dim(self, src_cube, single_point):
         # Check exact result with an aux-coord mapped to an uninterpolated dim.
-        cube = self.test_cube
-        cube.add_aux_coord(DimCoord([17, 19], long_name="aux0"), 0)
+        src_cube.add_aux_coord(DimCoord([17, 19], long_name="aux0"), 0)
 
         # The result cube should exactly equal a single source point.
-        result = interpolate(cube, self.single_sample_point, method="nearest")
-        self.assertEqual(result.shape[-1], 1)
+        result = interpolate(
+            src_cube, single_point.sample_point, method="nearest"
+        )
+        assert result.shape[-1] == 1
         result = result[..., 0]
-        expected = cube[:, self.single_point_iy, self.single_point_ix]
-        self.assertEqual(result, expected)
+        expected = src_cube[:, single_point.iy, single_point.ix]
+        assert result == expected
 
-    def test_aux_coord_one_interp_dim(self):
+    def test_aux_coord_one_interp_dim(self, src_cube, single_point):
         # Check exact result with an aux-coord over one interpolation dims.
-        cube = self.test_cube
-        cube.add_aux_coord(AuxCoord([11, 12, 13, 14], long_name="aux_x"), 2)
+        src_cube.add_aux_coord(
+            AuxCoord([11, 12, 13, 14], long_name="aux_x"), 2
+        )
 
         # The result cube should exactly equal a single source point.
-        result = interpolate(cube, self.single_sample_point, method="nearest")
-        self.assertEqual(result.shape[-1], 1)
+        result = interpolate(
+            src_cube, single_point.sample_point, method="nearest"
+        )
+        assert result.shape[-1] == 1
         result = result[..., 0]
-        expected = cube[:, self.single_point_iy, self.single_point_ix]
-        self.assertEqual(result, expected)
+        expected = src_cube[:, single_point.iy, single_point.ix]
+        assert result == expected
 
-    def test_aux_coord_both_interp_dims(self):
+    def test_aux_coord_both_interp_dims(self, src_cube, single_point):
         # Check exact result with an aux-coord over both interpolation dims.
-        cube = self.test_cube
-        cube.add_aux_coord(
+        src_cube.add_aux_coord(
             AuxCoord(
                 [[11, 12, 13, 14], [21, 22, 23, 24], [31, 32, 33, 34]],
                 long_name="aux_xy",
@@ -142,17 +192,18 @@ def test_aux_coord_both_interp_dims(self):
         )
 
         # The result cube should exactly equal a single source point.
-        result = interpolate(cube, self.single_sample_point, method="nearest")
-        self.assertEqual(result.shape[-1], 1)
+        result = interpolate(
+            src_cube, single_point.sample_point, method="nearest"
+        )
+        assert result.shape[-1] == 1
         result = result[..., 0]
-        expected = cube[:, self.single_point_iy, self.single_point_ix]
-        self.assertEqual(result, expected)
+        expected = src_cube[:, single_point.iy, single_point.ix]
+        assert result == expected
 
-    def test_aux_coord_fail_mixed_dims(self):
+    def test_aux_coord_fail_mixed_dims(self, src_cube, single_point):
         # Check behaviour with an aux-coord mapped over both interpolation and
         # non-interpolation dims : not supported.
-        cube = self.test_cube
-        cube.add_aux_coord(
+        src_cube.add_aux_coord(
             AuxCoord(
                 [[111, 112, 113, 114], [211, 212, 213, 214]],
                 long_name="aux_0x",
@@ -163,22 +214,23 @@ def test_aux_coord_fail_mixed_dims(self):
             "Coord aux_0x at one x-y position has the shape.*"
             "instead of being a single point"
         )
-        with self.assertRaisesRegex(ValueError, msg):
-            interpolate(cube, self.single_sample_point, method="nearest")
+        with pytest.raises(ValueError, match=msg):
+            interpolate(src_cube, single_point.sample_point, method="nearest")
 
-    def test_metadata(self):
+    def test_metadata(self, src_cube, single_point):
         # Check exact result matching for a single point, with additional
         # attributes and cell-methods.
-        cube = self.test_cube
-        cube.attributes["ODD_ATTR"] = "string-value-example"
-        cube.add_cell_method(iris.coords.CellMethod("mean", "area"))
-        result = interpolate(cube, self.single_sample_point, method="nearest")
+        src_cube.attributes["ODD_ATTR"] = "string-value-example"
+        src_cube.add_cell_method(iris.coords.CellMethod("mean", "area"))
+        result = interpolate(
+            src_cube, single_point.sample_point, method="nearest"
+        )
         # Check that the result is a single trajectory point, exactly equal to
         # the expected part of the original data.
-        self.assertEqual(result.shape[-1], 1)
+        assert result.shape[-1] == 1
         result = result[..., 0]
-        expected = cube[:, self.single_point_iy, self.single_point_ix]
-        self.assertEqual(result, expected)
+        expected = src_cube[:, single_point.iy, single_point.ix]
+        assert result == expected
 
 
 class TestLinear(tests.IrisTest):
diff --git a/lib/iris/tests/unit/coords/test_Cell.py b/lib/iris/tests/unit/coords/test_Cell.py
index 650f9ded6c..81370bd0de 100644
--- a/lib/iris/tests/unit/coords/test_Cell.py
+++ b/lib/iris/tests/unit/coords/test_Cell.py
@@ -30,33 +30,6 @@ def assert_raises_on_comparison(self, cell, other, exception_type, regexp):
         with self.assertRaisesRegex(exception_type, regexp):
             cell >= other
 
-    def test_cftime_cell(self):
-        # Check that cell comparison when the cell contains
-        # cftime.datetime objects raises an exception otherwise
-        # this will fall back to id comparison producing unreliable
-        # results.
-        cell = Cell(cftime.datetime(2010, 3, 21))
-        dt = mock.Mock(timetuple=mock.Mock())
-        self.assert_raises_on_comparison(
-            cell, dt, TypeError, "determine the order of cftime"
-        )
-        self.assert_raises_on_comparison(
-            cell, 23, TypeError, "determine the order of cftime"
-        )
-        self.assert_raises_on_comparison(
-            cell, "hello", TypeError, "Unexpected type.*str"
-        )
-
-    def test_cftime_other(self):
-        # Check that cell comparison to a cftime.datetime object
-        # raises an exception otherwise this will fall back to id comparison
-        # producing unreliable results.
-        dt = cftime.datetime(2010, 3, 21)
-        cell = Cell(mock.Mock(timetuple=mock.Mock()))
-        self.assert_raises_on_comparison(
-            cell, dt, TypeError, "determine the order of cftime"
-        )
-
     def test_PartialDateTime_bounded_cell(self):
         # Check that bounded comparisions to a PartialDateTime
         # raise an exception. These are not supported as they
@@ -85,10 +58,9 @@ def test_PartialDateTime_unbounded_cell(self):
     def test_datetime_unbounded_cell(self):
         # Check that cell comparison works with datetimes.
         dt = datetime.datetime(2000, 6, 15)
-        cell = Cell(datetime.datetime(2000, 1, 1))
-        # Note the absence of the inverse of these
-        # e.g. self.assertGreater(dt, cell).
-        # See http://bugs.python.org/issue8005
+        cell = Cell(cftime.datetime(2000, 1, 1))
+        self.assertGreater(dt, cell)
+        self.assertGreaterEqual(dt, cell)
         self.assertLess(cell, dt)
         self.assertLessEqual(cell, dt)
 
diff --git a/lib/iris/tests/unit/coords/test__DimensionalMetadata.py b/lib/iris/tests/unit/coords/test__DimensionalMetadata.py
index f9316ff92c..83fcbc4512 100644
--- a/lib/iris/tests/unit/coords/test__DimensionalMetadata.py
+++ b/lib/iris/tests/unit/coords/test__DimensionalMetadata.py
@@ -911,11 +911,11 @@ def test_meshcoord(self):
         result = self.repr_str_strings(meshco)
         expected = [
             (
-                "<MeshCoord: longitude / (degrees_east)  "
+                "<MeshCoord: longitude / (unknown)  "
                 "mesh(test_mesh) location(face)  "
                 "[...]+bounds  shape(3,)>"
             ),
-            "MeshCoord :  longitude / (degrees_east)",
+            "MeshCoord :  longitude / (unknown)",
             "    mesh: <Mesh: 'test_mesh'>",
             "    location: 'face'",
             "    points: [3100, 3101, 3102]",
@@ -926,10 +926,6 @@ def test_meshcoord(self):
             "    shape: (3,)  bounds(3, 4)",
             "    dtype: int64",
             "    standard_name: 'longitude'",
-            "    long_name: 'long-name'",
-            "    attributes:",
-            "        a  1",
-            "        b  'c'",
             "    axis: 'x'",
         ]
         self.assertLines(expected, result)
diff --git a/lib/iris/tests/unit/cube/test_Cube.py b/lib/iris/tests/unit/cube/test_Cube.py
index f38d6ef35d..5d120a6982 100644
--- a/lib/iris/tests/unit/cube/test_Cube.py
+++ b/lib/iris/tests/unit/cube/test_Cube.py
@@ -9,6 +9,7 @@
 # importing anything else.
 import iris.tests as tests  # isort:skip
 
+from collections import namedtuple
 from itertools import permutations
 from unittest import mock
 
@@ -1864,12 +1865,14 @@ class Test_copy(tests.IrisTest):
     def _check_copy(self, cube, cube_copy):
         self.assertIsNot(cube_copy, cube)
         self.assertEqual(cube_copy, cube)
-        self.assertIsNot(cube_copy.data, cube.data)
+        self.assertIsNot(cube_copy.core_data(), cube.core_data())
         if ma.isMaskedArray(cube.data):
             self.assertMaskedArrayEqual(cube_copy.data, cube.data)
             if cube.data.mask is not ma.nomask:
                 # "No mask" is a constant : all other cases must be distinct.
-                self.assertIsNot(cube_copy.data.mask, cube.data.mask)
+                self.assertIsNot(
+                    cube_copy.core_data().mask, cube.core_data().mask
+                )
         else:
             self.assertArrayEqual(cube_copy.data, cube.data)
 
@@ -1910,6 +1913,9 @@ def test__masked_scalar_arraymask(self):
         self._check_copy(cube, cube.copy())
 
     def test__lazy(self):
+        # 2022-11-02: Dask's current behaviour is that the computed array will
+        #  be the same for cube and cube.copy(), even if the Dask arrays are
+        #  different.
         cube = Cube(as_lazy_data(np.array([1, 0])))
         self._check_copy(cube, cube.copy())
 
@@ -2527,6 +2533,25 @@ def test_fail_remove_ancilliary_variable_by_name(self):
             self.cube.remove_ancillary_variable("notname")
 
 
+class TestCoords(tests.IrisTest):
+    def setUp(self):
+        cube = Cube(np.arange(6).reshape(2, 3))
+        x_coord = DimCoord(points=np.array([2, 3, 4]), long_name="x")
+        cube.add_dim_coord(x_coord, 1)
+        self.x_coord = x_coord
+        self.cube = cube
+
+    def test_bad_coord(self):
+        bad_coord = self.x_coord.copy()
+        bad_coord.attributes = {"bad": "attribute"}
+        re = (
+            "Expected to find exactly 1 coordinate matching the given "
+            "'x' coordinate's metadata, but found none."
+        )
+        with self.assertRaisesRegex(CoordinateNotFoundError, re):
+            _ = self.cube.coord(bad_coord)
+
+
 class Test__getitem_CellMeasure(tests.IrisTest):
     def setUp(self):
         cube = Cube(np.arange(6).reshape(2, 3))
@@ -2937,64 +2962,254 @@ def test_cell_method_correct_order(self):
         self.assertTrue(cube1 == cube2)
 
 
+@pytest.fixture
+def simplecube():
+    return stock.simple_2d_w_cell_measure_ancil_var()
+
+
 class Test__dimensional_metadata:
-    @pytest.fixture
-    def cube(self):
-        return stock.simple_2d_w_cell_measure_ancil_var()
+    """
+    Tests for the "Cube._dimensional_data" method.
+
+    NOTE: test could all be static methods, but that adds a line to each definition.
+    """
 
-    def test_not_found(self, cube):
+    def test_not_found(self, simplecube):
         with pytest.raises(KeyError, match="was not found in"):
-            cube._dimensional_metadata("grid_latitude")
+            simplecube._dimensional_metadata("grid_latitude")
 
-    def test_dim_coord_name_found(self, cube):
-        res = cube._dimensional_metadata("bar")
-        assert res == cube.coord("bar")
+    def test_dim_coord_name_found(self, simplecube):
+        res = simplecube._dimensional_metadata("bar")
+        assert res == simplecube.coord("bar")
 
-    def test_dim_coord_instance_found(self, cube):
-        res = cube._dimensional_metadata(cube.coord("bar"))
-        assert res == cube.coord("bar")
+    def test_dim_coord_instance_found(self, simplecube):
+        res = simplecube._dimensional_metadata(simplecube.coord("bar"))
+        assert res == simplecube.coord("bar")
 
-    def test_aux_coord_name_found(self, cube):
-        res = cube._dimensional_metadata("wibble")
-        assert res == cube.coord("wibble")
+    def test_aux_coord_name_found(self, simplecube):
+        res = simplecube._dimensional_metadata("wibble")
+        assert res == simplecube.coord("wibble")
 
-    def test_aux_coord_instance_found(self, cube):
-        res = cube._dimensional_metadata(cube.coord("wibble"))
-        assert res == cube.coord("wibble")
+    def test_aux_coord_instance_found(self, simplecube):
+        res = simplecube._dimensional_metadata(simplecube.coord("wibble"))
+        assert res == simplecube.coord("wibble")
 
-    def test_cell_measure_name_found(self, cube):
-        res = cube._dimensional_metadata("cell_area")
-        assert res == cube.cell_measure("cell_area")
+    def test_cell_measure_name_found(self, simplecube):
+        res = simplecube._dimensional_metadata("cell_area")
+        assert res == simplecube.cell_measure("cell_area")
 
-    def test_cell_measure_instance_found(self, cube):
-        res = cube._dimensional_metadata(cube.cell_measure("cell_area"))
-        assert res == cube.cell_measure("cell_area")
+    def test_cell_measure_instance_found(self, simplecube):
+        res = simplecube._dimensional_metadata(
+            simplecube.cell_measure("cell_area")
+        )
+        assert res == simplecube.cell_measure("cell_area")
 
-    def test_ancillary_var_name_found(self, cube):
-        res = cube._dimensional_metadata("quality_flag")
-        assert res == cube.ancillary_variable("quality_flag")
+    def test_ancillary_var_name_found(self, simplecube):
+        res = simplecube._dimensional_metadata("quality_flag")
+        assert res == simplecube.ancillary_variable("quality_flag")
 
-    def test_ancillary_var_instance_found(self, cube):
-        res = cube._dimensional_metadata(
-            cube.ancillary_variable("quality_flag")
+    def test_ancillary_var_instance_found(self, simplecube):
+        res = simplecube._dimensional_metadata(
+            simplecube.ancillary_variable("quality_flag")
         )
-        assert res == cube.ancillary_variable("quality_flag")
+        assert res == simplecube.ancillary_variable("quality_flag")
 
-    def test_two_with_same_name(self, cube):
+    def test_two_with_same_name(self, simplecube):
         # If a cube has two _DimensionalMetadata objects with the same name, the
         # current behaviour results in _dimensional_metadata returning the first
         # one it finds.
-        cube.cell_measure("cell_area").rename("wibble")
-        res = cube._dimensional_metadata("wibble")
-        assert res == cube.coord("wibble")
+        simplecube.cell_measure("cell_area").rename("wibble")
+        res = simplecube._dimensional_metadata("wibble")
+        assert res == simplecube.coord("wibble")
 
-    def test_two_with_same_name_specify_instance(self, cube):
+    def test_two_with_same_name_specify_instance(self, simplecube):
         # The cube has two _DimensionalMetadata objects with the same name so
         # we specify the _DimensionalMetadata instance to ensure it returns the
         # correct one.
-        cube.cell_measure("cell_area").rename("wibble")
-        res = cube._dimensional_metadata(cube.cell_measure("wibble"))
-        assert res == cube.cell_measure("wibble")
+        simplecube.cell_measure("cell_area").rename("wibble")
+        res = simplecube._dimensional_metadata(
+            simplecube.cell_measure("wibble")
+        )
+        assert res == simplecube.cell_measure("wibble")
+
+
+class TestReprs:
+    """
+    Confirm that str(cube), repr(cube) and cube.summary() work by creating a fresh
+    :class:`iris._representation.cube_printout.CubePrinter` object, and using it
+    in the expected ways.
+
+    Notes
+    -----
+    This only tests code connectivity.  The functionality is tested elsewhere, in
+    `iris.tests.unit._representation.cube_printout.test_CubePrintout`.
+    """
+
+    # Note: logically this could be a staticmethod, but that seems to upset Pytest
+    @pytest.fixture
+    def patched_cubeprinter(self):
+        target = "iris._representation.cube_printout.CubePrinter"
+        instance_mock = mock.MagicMock(
+            to_string=mock.MagicMock(
+                return_value=""
+            )  # NB this must return a string
+        )
+        with mock.patch(target, return_value=instance_mock) as class_mock:
+            yield class_mock, instance_mock
+
+    @staticmethod
+    def _check_expected_effects(
+        simplecube, patched_cubeprinter, oneline, padding
+    ):
+        class_mock, instance_mock = patched_cubeprinter
+        assert class_mock.call_args_list == [
+            # "CubePrinter()" was called exactly once, with the cube as arg
+            mock.call(simplecube)
+        ]
+        assert instance_mock.to_string.call_args_list == [
+            # "CubePrinter(cube).to_string()" was called exactly once, with these args
+            mock.call(oneline=oneline, name_padding=padding)
+        ]
+
+    def test_str_effects(self, simplecube, patched_cubeprinter):
+        str(simplecube)
+        self._check_expected_effects(
+            simplecube, patched_cubeprinter, oneline=False, padding=35
+        )
+
+    def test_repr_effects(self, simplecube, patched_cubeprinter):
+        repr(simplecube)
+        self._check_expected_effects(
+            simplecube, patched_cubeprinter, oneline=True, padding=1
+        )
+
+    def test_summary_effects(self, simplecube, patched_cubeprinter):
+        simplecube.summary(
+            shorten=mock.sentinel.oneliner, name_padding=mock.sentinel.padding
+        )
+        self._check_expected_effects(
+            simplecube,
+            patched_cubeprinter,
+            oneline=mock.sentinel.oneliner,
+            padding=mock.sentinel.padding,
+        )
+
+
+class TestHtmlRepr:
+    """
+    Confirm that Cube._repr_html_() creates a fresh
+    :class:`iris.experimental.representation.CubeRepresentation` object, and uses it
+    in the expected way.
+
+    Notes
+    -----
+    This only tests code connectivity.  The functionality is tested elsewhere, in
+    `iris.tests.unit.experimental.representation.test_CubeRepresentation`.
+    """
+
+    # Note: logically this could be a staticmethod, but that seems to upset Pytest
+    @pytest.fixture
+    def patched_cubehtml(self):
+        target = "iris.experimental.representation.CubeRepresentation"
+        instance_mock = mock.MagicMock(
+            repr_html=mock.MagicMock(
+                return_value=""
+            )  # NB this must return a string
+        )
+        with mock.patch(target, return_value=instance_mock) as class_mock:
+            yield class_mock, instance_mock
+
+    @staticmethod
+    def test__repr_html__effects(simplecube, patched_cubehtml):
+        simplecube._repr_html_()
+
+        class_mock, instance_mock = patched_cubehtml
+        assert class_mock.call_args_list == [
+            # "CubeRepresentation()" was called exactly once, with the cube as arg
+            mock.call(simplecube)
+        ]
+        assert instance_mock.repr_html.call_args_list == [
+            # "CubeRepresentation(cube).repr_html()" was called exactly once, with no args
+            mock.call()
+        ]
+
+
+class Test__cell_methods:
+    @pytest.fixture(autouse=True)
+    def cell_measures_testdata(self):
+        self.cube = Cube([0])
+        self.cm = CellMethod("mean", "time", "6hr")
+        self.cm2 = CellMethod("max", "latitude", "4hr")
+
+    def test_none(self):
+        assert self.cube.cell_methods == ()
+
+    def test_one(self):
+        cube = Cube([0], cell_methods=[self.cm])
+        expected = (self.cm,)
+        assert expected == cube.cell_methods
+
+    def test_empty_assigns(self):
+        testargs = [(), [], {}, 0, 0.0, False, None]
+        results = []
+        for arg in testargs:
+            cube = self.cube.copy()
+            cube.cell_methods = arg  # assign test object
+            results.append(cube.cell_methods)  # capture what is read back
+        expected_results = [()] * len(testargs)
+        assert expected_results == results
+
+    def test_single_assigns(self):
+        cms = (self.cm, self.cm2)
+        # Any type of iterable ought to work
+        # But N.B. *not* testing sets, as order is not stable
+        testargs = [cms, list(cms), {cm: 1 for cm in cms}]
+        results = []
+        for arg in testargs:
+            cube = self.cube.copy()
+            cube.cell_methods = arg  # assign test object
+            results.append(cube.cell_methods)  # capture what is read back
+        expected_results = [cms] * len(testargs)
+        assert expected_results == results
+
+    def test_fail_assign_noniterable(self):
+        test_object = object()
+        with pytest.raises(TypeError, match="not iterable"):
+            self.cube.cell_methods = test_object
+
+    def test_fail_create_noniterable(self):
+        test_object = object()
+        with pytest.raises(TypeError, match="not iterable"):
+            Cube([0], cell_methods=test_object)
+
+    def test_fail_assign_noncellmethod(self):
+        test_object = object()
+        with pytest.raises(ValueError, match="not an iris.coords.CellMethod"):
+            self.cube.cell_methods = (test_object,)
+
+    def test_fail_create_noncellmethod(self):
+        test_object = object()
+        with pytest.raises(ValueError, match="not an iris.coords.CellMethod"):
+            Cube([0], cell_methods=[test_object])
+
+    def test_assign_derivedcellmethod(self):
+        class DerivedCellMethod(CellMethod):
+            pass
+
+        test_object = DerivedCellMethod("mean", "time", "6hr")
+        cms = (test_object,)
+        self.cube.cell_methods = (test_object,)
+        assert cms == self.cube.cell_methods
+
+    def test_fail_assign_duckcellmethod(self):
+        # Can't currently assign a "duck-typed" CellMethod replacement, since
+        # implementation requires class membership (boo!)
+        DuckCellMethod = namedtuple("DuckCellMethod", CellMethod._names)
+        test_object = DuckCellMethod(
+            *CellMethod._names
+        )  # fill props with value==name
+        with pytest.raises(ValueError, match="not an iris.coords.CellMethod"):
+            self.cube.cell_methods = (test_object,)
 
 
 if __name__ == "__main__":
diff --git a/lib/iris/tests/unit/cube/test_CubeList.py b/lib/iris/tests/unit/cube/test_CubeList.py
index 1ebfe57773..86457d3888 100644
--- a/lib/iris/tests/unit/cube/test_CubeList.py
+++ b/lib/iris/tests/unit/cube/test_CubeList.py
@@ -735,5 +735,36 @@ def test_copy(self):
         self.assertIsInstance(self.copied_cube_list, iris.cube.CubeList)
 
 
+class TestHtmlRepr:
+    """
+    Confirm that Cubelist._repr_html_() creates a fresh
+    :class:`iris.experimental.representation.CubeListRepresentation` object, and uses
+    it in the expected way.
+
+    Notes
+    -----
+    This only tests code connectivity.  The functionality is tested elsewhere, at
+    `iris.tests.unit.experimental.representation.test_CubeListRepresentation`
+    """
+
+    @staticmethod
+    def test__repr_html_():
+        test_cubelist = CubeList([])
+
+        target = "iris.experimental.representation.CubeListRepresentation"
+        with mock.patch(target) as class_mock:
+            # Exercise the function-under-test.
+            test_cubelist._repr_html_()
+
+        assert class_mock.call_args_list == [
+            # "CubeListRepresentation()" was called exactly once, with the cubelist as arg
+            mock.call(test_cubelist)
+        ]
+        assert class_mock.return_value.repr_html.call_args_list == [
+            # "CubeListRepresentation(cubelist).repr_html()" was called exactly once, with no args
+            mock.call()
+        ]
+
+
 if __name__ == "__main__":
     tests.main()
diff --git a/lib/iris/tests/unit/cube/test_Cube__aggregated_by.py b/lib/iris/tests/unit/cube/test_Cube__aggregated_by.py
index 3230e3de00..9e60631c33 100644
--- a/lib/iris/tests/unit/cube/test_Cube__aggregated_by.py
+++ b/lib/iris/tests/unit/cube/test_Cube__aggregated_by.py
@@ -22,6 +22,7 @@
 from iris.coords import AncillaryVariable, AuxCoord, CellMeasure, DimCoord
 from iris.cube import Cube
 import iris.exceptions
+from iris.tests.stock import realistic_4d
 
 
 class Test_aggregated_by(tests.IrisTest):
@@ -841,5 +842,52 @@ def test_clim_in_no_clim_op(self):
         self.assertFalse(categorised_coord.climatological)
 
 
+class Test_aggregated_by__derived(tests.IrisTest):
+    def setUp(self):
+        self.cube = realistic_4d()[:, :10, :6, :8]
+        self.time_cat_coord = AuxCoord(
+            [0, 0, 1, 1, 2, 2], long_name="time_cat"
+        )
+        self.cube.add_aux_coord(self.time_cat_coord, 0)
+        height_data = np.zeros(self.cube.shape[1])
+        height_data[5:] = 1
+        self.height_cat_coord = AuxCoord(height_data, long_name="height_cat")
+        self.cube.add_aux_coord(self.height_cat_coord, 1)
+        self.aggregator = iris.analysis.MEAN
+
+    def test_grouped_dim(self):
+        """
+        Check that derived coordinates are maintained when the coordinates they
+        derive from are aggregated.
+        """
+        result = self.cube.aggregated_by(
+            self.height_cat_coord,
+            self.aggregator,
+        )
+        assert len(result.aux_factories) == 1
+        altitude = result.coord("altitude")
+        assert altitude.shape == (2, 6, 8)
+
+        # Check the bounds are derived as expected.
+        orig_alt_bounds = self.cube.coord("altitude").bounds
+        bounds_0 = orig_alt_bounds[0::5, :, :, 0]
+        bounds_1 = orig_alt_bounds[4::5, :, :, 1]
+        expected_bounds = np.stack([bounds_0, bounds_1], axis=-1)
+        assert np.array_equal(expected_bounds, result.coord("altitude").bounds)
+
+    def test_ungrouped_dim(self):
+        """
+        Check that derived coordinates are preserved when aggregating along a
+        different axis.
+        """
+        result = self.cube.aggregated_by(
+            self.time_cat_coord,
+            self.aggregator,
+        )
+        assert len(result.aux_factories) == 1
+        altitude = result.coord("altitude")
+        assert altitude == self.cube.coord("altitude")
+
+
 if __name__ == "__main__":
     tests.main()
diff --git a/lib/iris/tests/unit/experimental/ugrid/mesh/test_MeshCoord.py b/lib/iris/tests/unit/experimental/ugrid/mesh/test_MeshCoord.py
index ce99a8b4be..538cecdc7d 100644
--- a/lib/iris/tests/unit/experimental/ugrid/mesh/test_MeshCoord.py
+++ b/lib/iris/tests/unit/experimental/ugrid/mesh/test_MeshCoord.py
@@ -16,9 +16,10 @@
 
 import dask.array as da
 import numpy as np
+import pytest
 
 from iris._lazy_data import as_lazy_data, is_lazy_data
-from iris.common.metadata import BaseMetadata
+from iris.common.metadata import BaseMetadata, CoordMetadata
 from iris.coords import AuxCoord, Coord
 from iris.cube import Cube
 from iris.experimental.ugrid.mesh import Connectivity, Mesh, MeshCoord
@@ -45,16 +46,11 @@ def test_derived_properties(self):
         # underlying mesh coordinate.
         for axis in Mesh.AXES:
             meshcoord = sample_meshcoord(axis=axis)
-            # N.B.
-            node_x_coord = meshcoord.mesh.coord(include_nodes=True, axis=axis)
-            for key in node_x_coord.metadata._fields:
+            face_x_coord = meshcoord.mesh.coord(include_faces=True, axis=axis)
+            for key in face_x_coord.metadata._fields:
                 meshval = getattr(meshcoord, key)
-                if key == "var_name":
-                    # var_name is unused.
-                    self.assertIsNone(meshval)
-                else:
-                    # names, units and attributes are derived from the node coord.
-                    self.assertEqual(meshval, getattr(node_x_coord, key))
+                # All relevant attributes are derived from the face coord.
+                self.assertEqual(meshval, getattr(face_x_coord, key))
 
     def test_fail_bad_mesh(self):
         with self.assertRaisesRegex(TypeError, "must be a.*Mesh"):
@@ -270,10 +266,11 @@ def setUp(self):
     def _expected_elements_regexp(
         self,
         standard_name="longitude",
-        long_name="long-name",
-        attributes=True,
+        long_name=None,
+        attributes=False,
         location="face",
         axis="x",
+        var_name=None,
     ):
         # Printed name is standard or long -- we don't have a case with neither
         coord_name = standard_name or long_name
@@ -282,24 +279,30 @@ def _expected_elements_regexp(
         regexp = f"MeshCoord :  {coord_name} / [^\n]+\n *"
         regexp += r"mesh: \<Mesh: 'test_mesh'>\n *"
         regexp += f"location: '{location}'\n *"
+
         # Now some optional sections : whichever comes first will match
         # arbitrary content leading up to it.
-        matched_any_upto = False
-        if standard_name:
-            regexp += ".*"
-            matched_any_upto = True
-            regexp += f"standard_name: '{standard_name}'\n *"
-        if long_name:
+        matched_upto = False
+
+        def upto_first_expected(regexp, matched_any_upto):
             if not matched_any_upto:
                 regexp += ".*"
                 matched_any_upto = True
+            return regexp, matched_any_upto
+
+        if standard_name:
+            regexp, matched_upto = upto_first_expected(regexp, matched_upto)
+            regexp += f"standard_name: '{standard_name}'\n *"
+        if long_name:
+            regexp, matched_upto = upto_first_expected(regexp, matched_upto)
             regexp += f"long_name: '{long_name}'\n *"
+        if var_name:
+            regexp, matched_upto = upto_first_expected(regexp, matched_upto)
+            regexp += f"var_name: '{var_name}'\n *"
         if attributes:
             # if we expected attributes, they should come next
             # TODO: change this when each attribute goes on a new line
-            if not matched_any_upto:
-                regexp += ".*"
-                matched_any_upto = True
+            regexp, matched_upto = upto_first_expected(regexp, matched_upto)
             # match 'attributes:' followed by N*lines with larger indent
             regexp += "attributes:(\n        [^ \n]+ +[^ \n]+)+\n    "
         # After those items, expect 'axis' next
@@ -314,7 +317,7 @@ def test_repr(self):
         # A simple check for the condensed form.
         result = repr(self.meshcoord)
         expected = (
-            "<MeshCoord: longitude / (degrees_east)  "
+            "<MeshCoord: longitude / (unknown)  "
             "mesh(test_mesh) location(face)  [...]+bounds  shape(3,)>"
         )
         self.assertEqual(expected, result)
@@ -331,7 +334,7 @@ def test_repr_lazy(self):
         self.assertTrue(self.meshcoord.has_lazy_bounds())
 
         expected = (
-            "<MeshCoord: longitude / (degrees_east)  "
+            "<MeshCoord: longitude / (unknown)  "
             "mesh(test_mesh) location(face)  <lazy>+bounds  shape(3,)>"
         )
         self.assertEqual(expected, result)
@@ -342,7 +345,7 @@ def test_repr__nameless_mesh(self):
         assert self.mesh.name() == "unknown"
         result = repr(self.meshcoord)
         re_expected = (
-            r".MeshCoord: longitude / \(degrees_east\)  "
+            r".MeshCoord: longitude / \(unknown\)  "
             r"mesh\(.Mesh object at 0x[^>]+.\) location\(face\) "
         )
         self.assertRegex(result, re_expected)
@@ -392,18 +395,6 @@ def test_str_no_long_name(self):
         re_expected = self._expected_elements_regexp(long_name=False)
         self.assertRegex(result, re_expected)
 
-    def test_str_no_standard_name(self):
-        mesh = self.mesh
-        # Remove the standard_name of the node coord in the mesh.
-        node_coord = mesh.coord(include_nodes=True, axis="x")
-        node_coord.standard_name = None
-        node_coord.axis = "x"  # This is required : but it's a kludge !!
-        # Make a new meshcoord, based on the modified mesh.
-        meshcoord = sample_meshcoord(mesh=self.mesh)
-        result = str(meshcoord)
-        re_expected = self._expected_elements_regexp(standard_name=False)
-        self.assertRegex(result, re_expected)
-
     def test_str_no_attributes(self):
         mesh = self.mesh
         # No attributes on the node coord in the mesh.
@@ -451,9 +442,11 @@ def test_cube_dims(self):
 
     def test_find_by_name(self):
         meshcoord = self.meshcoord
+        # hack to give it a long name
+        meshcoord.long_name = "odd_case"
         cube = self.cube
         self.assertIs(cube.coord(standard_name="longitude"), meshcoord)
-        self.assertIs(cube.coord(long_name="long-name"), meshcoord)
+        self.assertIs(cube.coord(long_name="odd_case"), meshcoord)
 
     def test_find_by_axis(self):
         meshcoord = self.meshcoord
@@ -796,5 +789,157 @@ def test_bounds_badvalues__lazy(self):
         self._check_bounds_bad_index_values(lazy=True)
 
 
+class Test__metadata:
+    def setup_mesh(self, location, axis):
+        # Create a standard test mesh + attach it to the test instance.
+        mesh = sample_mesh()
+
+        # Modify the metadata of specific coordinates used in this test.
+        def select_coord(location, axis):
+            kwargs = {f"include_{location}s": True, "axis": axis}
+            return mesh.coord(**kwargs)
+
+        node_coord = select_coord("node", axis)
+        location_coord = select_coord(location, axis)
+        for i_place, coord in enumerate((node_coord, location_coord)):
+            coord.standard_name = "longitude" if axis == "x" else "latitude"
+            coord.units = "degrees"
+            coord.long_name = f"long_name_{i_place}"
+            coord.var_name = f"var_name_{i_place}"
+            coord.attributes = {"att": i_place}
+
+        # attach all the relevant testcase context to the test instance.
+        self.mesh = mesh
+        self.location = location
+        self.axis = axis
+        self.location_coord = location_coord
+        self.node_coord = node_coord
+
+    def coord_metadata_matches(self, test_coord, ref_coord):
+        # Check that two coords match, in all the basic Coord identity/phenomenon
+        # metadata fields -- so it works even between coords of different subclasses.
+        for key in CoordMetadata._fields:
+            assert getattr(test_coord, key) == getattr(ref_coord, key)
+
+    @pytest.fixture(params=["face", "edge"])
+    def location_face_or_edge(self, request):
+        # Fixture to parametrise over location = face/edge
+        return request.param
+
+    @pytest.fixture(params=["x", "y"])
+    def axis_x_or_y(self, request):
+        # Fixture to parametrise over axis = X/Y
+        return request.param
+
+    def test_node_meshcoord(self, axis_x_or_y):
+        # MeshCoord metadata matches that of the relevant node coord.
+        self.setup_mesh(location="node", axis=axis_x_or_y)
+        meshcoord = self.mesh.to_MeshCoord(
+            location=self.location, axis=self.axis
+        )
+        self.coord_metadata_matches(meshcoord, self.node_coord)
+
+    def test_faceedge_basic(self, location_face_or_edge, axis_x_or_y):
+        # MeshCoord metadata matches that of the face/edge ("points") coord.
+        self.setup_mesh(location_face_or_edge, axis_x_or_y)
+        meshcoord = self.mesh.to_MeshCoord(
+            location=self.location, axis=self.axis
+        )
+        self.coord_metadata_matches(meshcoord, self.location_coord)
+
+    @pytest.mark.parametrize(
+        "fieldname", ["long_name", "var_name", "attributes"]
+    )
+    def test_faceedge_dontcare_fields(
+        self, location_face_or_edge, axis_x_or_y, fieldname
+    ):
+        # Check that it's ok for the face/edge and node coords to have different
+        # long-name, var-name or attributes.
+        self.setup_mesh(location_face_or_edge, axis_x_or_y)
+        if fieldname == "attributes":
+            different_value = {"myattrib": "different attributes"}
+        else:
+            # others are just arbitrary strings.
+            different_value = "different"
+        setattr(self.location_coord, fieldname, different_value)
+        # Mostly.. just check this does not cause an error, as it would do if we
+        # modified "standard_name" or "units" (see other tests) ...
+        meshcoord = self.mesh.to_MeshCoord(
+            location=self.location, axis=self.axis
+        )
+        # ... but also, check that the result matches the expected face/edge coord.
+        self.coord_metadata_matches(meshcoord, self.location_coord)
+
+    def test_faceedge_fail_mismatched_stdnames(
+        self, location_face_or_edge, axis_x_or_y
+    ):
+        # Different "standard_name" for node and face/edge causes an error.
+        self.setup_mesh(location_face_or_edge, axis_x_or_y)
+        node_name = f"projection_{axis_x_or_y}_coordinate"
+        self.node_coord.standard_name = node_name
+        location_name = "longitude" if axis_x_or_y == "x" else "latitude"
+        msg = (
+            "Node coordinate .*"
+            f"disagrees with the {location_face_or_edge} coordinate .*, "
+            'in having a "standard_name" value of '
+            f"'{node_name}' instead of '{location_name}'"
+        )
+        with pytest.raises(ValueError, match=msg):
+            self.mesh.to_MeshCoord(
+                location=location_face_or_edge, axis=axis_x_or_y
+            )
+
+    def test_faceedge_fail_missing_stdnames(
+        self, location_face_or_edge, axis_x_or_y
+    ):
+        # "standard_name" compared with None also causes an error.
+        self.setup_mesh(location_face_or_edge, axis_x_or_y)
+        self.node_coord.standard_name = None
+        # N.B. in the absence of a standard-name, we **must** provide an extra ".axis"
+        # property, or the coordinate cannot be correctly identified in the Mesh.
+        # This is a bit of a kludge, but works with current code.
+        self.node_coord.axis = axis_x_or_y
+
+        location_name = "longitude" if axis_x_or_y == "x" else "latitude"
+        msg = (
+            "Node coordinate .*"
+            f"disagrees with the {location_face_or_edge} coordinate .*, "
+            'in having a "standard_name" value of '
+            f"None instead of '{location_name}'"
+        )
+        with pytest.raises(ValueError, match=msg):
+            self.mesh.to_MeshCoord(
+                location=location_face_or_edge, axis=axis_x_or_y
+            )
+
+    def test_faceedge_fail_mismatched_units(
+        self, location_face_or_edge, axis_x_or_y
+    ):
+        # Different "units" for node and face/edge causes an error.
+        self.setup_mesh(location_face_or_edge, axis_x_or_y)
+        self.node_coord.units = "hPa"
+        msg = (
+            "Node coordinate .*"
+            f"disagrees with the {location_face_or_edge} coordinate .*, "
+            'in having a "units" value of '
+            "'hPa' instead of 'degrees'"
+        )
+        with pytest.raises(ValueError, match=msg):
+            self.mesh.to_MeshCoord(
+                location=location_face_or_edge, axis=axis_x_or_y
+            )
+
+    def test_faceedge_missing_units(self, location_face_or_edge, axis_x_or_y):
+        # Units compared with a None ("unknown") is not an error.
+        self.setup_mesh(location_face_or_edge, axis_x_or_y)
+        self.node_coord.units = None
+        # This is OK
+        meshcoord = self.mesh.to_MeshCoord(
+            location=self.location, axis=self.axis
+        )
+        # ... but also, check that the result matches the expected face/edge coord.
+        self.coord_metadata_matches(meshcoord, self.location_coord)
+
+
 if __name__ == "__main__":
     tests.main()
diff --git a/lib/iris/tests/unit/fileformats/nc_load_rules/actions/__init__.py b/lib/iris/tests/unit/fileformats/nc_load_rules/actions/__init__.py
index c18bdb8399..0cc3d09426 100644
--- a/lib/iris/tests/unit/fileformats/nc_load_rules/actions/__init__.py
+++ b/lib/iris/tests/unit/fileformats/nc_load_rules/actions/__init__.py
@@ -15,7 +15,7 @@
 import iris.fileformats._nc_load_rules.engine
 from iris.fileformats.cf import CFReader
 import iris.fileformats.netcdf
-from iris.fileformats.netcdf import _load_cube
+from iris.fileformats.netcdf.loader import _load_cube
 from iris.tests.stock.netcdf import ncgen_from_cdl
 
 """
@@ -83,11 +83,11 @@ def load_cube_from_cdl(self, cdl_string, cdl_path, nc_path):
         # Grab a data variable : FOR NOW always grab the 'phenom' variable.
         cf_var = cf.cf_group.data_variables["phenom"]
 
-        engine = iris.fileformats.netcdf._actions_engine()
+        engine = iris.fileformats.netcdf.loader._actions_engine()
 
         # If debug enabled, switch on the activation summary debug output.
         # Use 'patch' so it is restored after the test.
-        self.patch("iris.fileformats.netcdf.DEBUG", self.debug)
+        self.patch("iris.fileformats.netcdf.loader.DEBUG", self.debug)
 
         with warnings.catch_warnings():
             warnings.filterwarnings(
diff --git a/lib/iris/tests/unit/fileformats/nc_load_rules/helpers/test_build_ancil_var.py b/lib/iris/tests/unit/fileformats/nc_load_rules/helpers/test_build_ancil_var.py
new file mode 100644
index 0000000000..b057a41a3e
--- /dev/null
+++ b/lib/iris/tests/unit/fileformats/nc_load_rules/helpers/test_build_ancil_var.py
@@ -0,0 +1,77 @@
+# Copyright Iris contributors
+#
+# This file is part of Iris and is released under the LGPL license.
+# See COPYING and COPYING.LESSER in the root of the repository for full
+# licensing details.
+"""
+Test function :func:`iris.fileformats._nc_load_rules.helpers.build_ancil_var`.
+
+"""
+
+from unittest import mock
+
+import numpy as np
+import pytest
+
+from iris.exceptions import CannotAddError
+from iris.fileformats._nc_load_rules.helpers import build_ancil_var
+
+
+@pytest.fixture
+def mock_engine():
+    return mock.Mock(
+        cube=mock.Mock(),
+        cf_var=mock.Mock(dimensions=("foo", "bar")),
+        filename="DUMMY",
+        cube_parts=dict(ancillary_variables=[]),
+    )
+
+
+@pytest.fixture
+def mock_cf_av_var(monkeypatch):
+    data = np.arange(6)
+    output = mock.Mock(
+        dimensions=("foo",),
+        scale_factor=1,
+        add_offset=0,
+        cf_name="wibble",
+        cf_data=mock.MagicMock(chunking=mock.Mock(return_value=None), spec=[]),
+        standard_name=None,
+        long_name="wibble",
+        units="m2",
+        shape=data.shape,
+        dtype=data.dtype,
+        __getitem__=lambda self, key: data[key],
+    )
+
+    # Create patch for deferred loading that prevents attempted
+    # file access. This assumes that output is defined in the test case.
+    def patched__getitem__(proxy_self, keys):
+        if proxy_self.variable_name == output.cf_name:
+            return output[keys]
+        raise RuntimeError()
+
+    monkeypatch.setattr(
+        "iris.fileformats.netcdf.NetCDFDataProxy.__getitem__",
+        patched__getitem__,
+    )
+
+    return output
+
+
+def test_not_added(monkeypatch, mock_engine, mock_cf_av_var):
+    # Confirm that the ancillary variable will be skipped if a CannotAddError
+    #  is raised when attempting to add.
+    def mock_add_ancillary_variable(_, __):
+        raise CannotAddError("foo")
+
+    with monkeypatch.context() as m:
+        m.setattr(
+            mock_engine.cube,
+            "add_ancillary_variable",
+            mock_add_ancillary_variable,
+        )
+        with pytest.warns(match="ancillary variable not added to Cube: foo"):
+            build_ancil_var(mock_engine, mock_cf_av_var)
+
+    assert mock_engine.cube_parts["ancillary_variables"] == []
diff --git a/lib/iris/tests/unit/fileformats/nc_load_rules/helpers/test_build_auxiliary_coordinate.py b/lib/iris/tests/unit/fileformats/nc_load_rules/helpers/test_build_auxiliary_coordinate.py
index 95f892454b..13622b72e2 100644
--- a/lib/iris/tests/unit/fileformats/nc_load_rules/helpers/test_build_auxiliary_coordinate.py
+++ b/lib/iris/tests/unit/fileformats/nc_load_rules/helpers/test_build_auxiliary_coordinate.py
@@ -16,8 +16,10 @@
 from unittest import mock
 
 import numpy as np
+import pytest
 
 from iris.coords import AuxCoord
+from iris.exceptions import CannotAddError
 from iris.fileformats._nc_load_rules.helpers import build_auxiliary_coordinate
 from iris.fileformats.cf import CFVariable
 
@@ -280,6 +282,11 @@ def get_cf_bounds_var(coord_var):
             new=get_cf_bounds_var,
         )
 
+        # test_not_added() has been written in pytest-style, but the rest of
+        #  the class is pending migration. Defining self.monkeypatch (not the
+        #  typical practice in pure pytest) allows this transitional state.
+        self.monkeypatch = pytest.MonkeyPatch()
+
     def check_case_aux_coord_construction(self, climatology=False):
         # Test a generic auxiliary coordinate, with or without
         # a climatological coord.
@@ -305,6 +312,19 @@ def test_aux_coord_construction(self):
     def test_aux_coord_construction__climatology(self):
         self.check_case_aux_coord_construction(climatology=True)
 
+    def test_not_added(self):
+        # Confirm that the coord will be skipped if a CannotAddError is raised
+        #  when attempting to add.
+        def mock_add_aux_coord(_, __):
+            raise CannotAddError("foo")
+
+        with self.monkeypatch.context() as m:
+            m.setattr(self.engine.cube, "add_aux_coord", mock_add_aux_coord)
+            with pytest.warns(match="coordinate not added to Cube: foo"):
+                build_auxiliary_coordinate(self.engine, self.cf_coord_var)
+
+        assert self.engine.cube_parts["coordinates"] == []
+
 
 if __name__ == "__main__":
     tests.main()
diff --git a/lib/iris/tests/unit/fileformats/nc_load_rules/helpers/test_build_cell_measure.py b/lib/iris/tests/unit/fileformats/nc_load_rules/helpers/test_build_cell_measure.py
new file mode 100644
index 0000000000..efbb0649c9
--- /dev/null
+++ b/lib/iris/tests/unit/fileformats/nc_load_rules/helpers/test_build_cell_measure.py
@@ -0,0 +1,74 @@
+# Copyright Iris contributors
+#
+# This file is part of Iris and is released under the LGPL license.
+# See COPYING and COPYING.LESSER in the root of the repository for full
+# licensing details.
+"""
+Test function :func:`iris.fileformats._nc_load_rules.helpers.build_cell_measure`.
+
+"""
+
+from unittest import mock
+
+import numpy as np
+import pytest
+
+from iris.exceptions import CannotAddError
+from iris.fileformats._nc_load_rules.helpers import build_cell_measures
+
+
+@pytest.fixture
+def mock_engine():
+    return mock.Mock(
+        cube=mock.Mock(),
+        cf_var=mock.Mock(dimensions=("foo", "bar")),
+        filename="DUMMY",
+        cube_parts=dict(cell_measures=[]),
+    )
+
+
+@pytest.fixture
+def mock_cf_cm_var(monkeypatch):
+    data = np.arange(6)
+    output = mock.Mock(
+        dimensions=("foo",),
+        scale_factor=1,
+        add_offset=0,
+        cf_name="wibble",
+        cf_data=mock.MagicMock(chunking=mock.Mock(return_value=None), spec=[]),
+        standard_name=None,
+        long_name="wibble",
+        units="m2",
+        shape=data.shape,
+        dtype=data.dtype,
+        __getitem__=lambda self, key: data[key],
+        cf_measure="area",
+    )
+
+    # Create patch for deferred loading that prevents attempted
+    # file access. This assumes that output is defined in the test case.
+    def patched__getitem__(proxy_self, keys):
+        if proxy_self.variable_name == output.cf_name:
+            return output[keys]
+        raise RuntimeError()
+
+    monkeypatch.setattr(
+        "iris.fileformats.netcdf.NetCDFDataProxy.__getitem__",
+        patched__getitem__,
+    )
+
+    return output
+
+
+def test_not_added(monkeypatch, mock_engine, mock_cf_cm_var):
+    # Confirm that the cell measure will be skipped if a CannotAddError is
+    #  raised when attempting to add.
+    def mock_add_cell_measure(_, __):
+        raise CannotAddError("foo")
+
+    with monkeypatch.context() as m:
+        m.setattr(mock_engine.cube, "add_cell_measure", mock_add_cell_measure)
+        with pytest.warns(match="cell measure not added to Cube: foo"):
+            build_cell_measures(mock_engine, mock_cf_cm_var)
+
+    assert mock_engine.cube_parts["cell_measures"] == []
diff --git a/lib/iris/tests/unit/fileformats/nc_load_rules/helpers/test_build_dimension_coordinate.py b/lib/iris/tests/unit/fileformats/nc_load_rules/helpers/test_build_dimension_coordinate.py
index a75678d923..b485937cb1 100644
--- a/lib/iris/tests/unit/fileformats/nc_load_rules/helpers/test_build_dimension_coordinate.py
+++ b/lib/iris/tests/unit/fileformats/nc_load_rules/helpers/test_build_dimension_coordinate.py
@@ -17,8 +17,10 @@
 import warnings
 
 import numpy as np
+import pytest
 
 from iris.coords import AuxCoord, DimCoord
+from iris.exceptions import CannotAddError
 from iris.fileformats._nc_load_rules.helpers import build_dimension_coordinate
 
 
@@ -73,6 +75,12 @@ def setUp(self):
         )
         self.bounds = bounds
 
+        # test_dimcoord_not_added() and test_auxcoord_not_added have been
+        #  written in pytest-style, but the rest of the class is pending
+        #  migration. Defining self.monkeypatch (not the
+        #  typical practice in pure pytest) allows this transitional state.
+        self.monkeypatch = pytest.MonkeyPatch()
+
     def _set_cf_coord_var(self, points):
         self.cf_coord_var = mock.Mock(
             dimensions=("foo",),
@@ -233,6 +241,40 @@ def test_aux_coord_construction(self):
                 warnings.warn.call_args[0][0],
             )
 
+    def test_dimcoord_not_added(self):
+        # Confirm that the coord will be skipped if a CannotAddError is raised
+        #  when attempting to add.
+        def mock_add_dim_coord(_, __):
+            raise CannotAddError("foo")
+
+        with self.monkeypatch.context() as m:
+            m.setattr(self.engine.cube, "add_dim_coord", mock_add_dim_coord)
+
+            self._set_cf_coord_var(np.arange(6))
+
+            with self.deferred_load_patch, self.get_cf_bounds_var_patch:
+                with pytest.warns(match="coordinate not added to Cube: foo"):
+                    build_dimension_coordinate(self.engine, self.cf_coord_var)
+
+        assert self.engine.cube_parts["coordinates"] == []
+
+    def test_auxcoord_not_added(self):
+        # Confirm that a gracefully-created auxiliary coord will also be
+        #  skipped if a CannotAddError is raised when attempting to add.
+        def mock_add_aux_coord(_, __):
+            raise CannotAddError("foo")
+
+        with self.monkeypatch.context() as m:
+            m.setattr(self.engine.cube, "add_aux_coord", mock_add_aux_coord)
+
+            self._set_cf_coord_var(np.array([1, 3, 2, 4, 6, 5]))
+
+            with self.deferred_load_patch, self.get_cf_bounds_var_patch:
+                with pytest.warns(match="coordinate not added to Cube: foo"):
+                    build_dimension_coordinate(self.engine, self.cf_coord_var)
+
+        assert self.engine.cube_parts["coordinates"] == []
+
 
 class TestBoundsVertexDim(tests.IrisTest, RulesTestMixin):
     def setUp(self):
diff --git a/lib/iris/tests/unit/fileformats/netcdf/loader/__init__.py b/lib/iris/tests/unit/fileformats/netcdf/loader/__init__.py
new file mode 100644
index 0000000000..7c2ae96158
--- /dev/null
+++ b/lib/iris/tests/unit/fileformats/netcdf/loader/__init__.py
@@ -0,0 +1,6 @@
+# Copyright Iris contributors
+#
+# This file is part of Iris and is released under the LGPL license.
+# See COPYING and COPYING.LESSER in the root of the repository for full
+# licensing details.
+"""Unit tests for the :mod:`iris.fileformats.netcdf.loader` module."""
diff --git a/lib/iris/tests/unit/fileformats/netcdf/test__get_cf_var_data.py b/lib/iris/tests/unit/fileformats/netcdf/loader/test__get_cf_var_data.py
similarity index 97%
rename from lib/iris/tests/unit/fileformats/netcdf/test__get_cf_var_data.py
rename to lib/iris/tests/unit/fileformats/netcdf/loader/test__get_cf_var_data.py
index 1bf39591d2..054c8e2db1 100644
--- a/lib/iris/tests/unit/fileformats/netcdf/test__get_cf_var_data.py
+++ b/lib/iris/tests/unit/fileformats/netcdf/loader/test__get_cf_var_data.py
@@ -16,7 +16,7 @@
 
 from iris._lazy_data import _optimum_chunksize
 import iris.fileformats.cf
-from iris.fileformats.netcdf import _get_cf_var_data
+from iris.fileformats.netcdf.loader import _get_cf_var_data
 
 
 class Test__get_cf_var_data(tests.IrisTest):
diff --git a/lib/iris/tests/unit/fileformats/netcdf/test__load_aux_factory.py b/lib/iris/tests/unit/fileformats/netcdf/loader/test__load_aux_factory.py
similarity index 99%
rename from lib/iris/tests/unit/fileformats/netcdf/test__load_aux_factory.py
rename to lib/iris/tests/unit/fileformats/netcdf/loader/test__load_aux_factory.py
index eb9da6b5d6..841935cc81 100644
--- a/lib/iris/tests/unit/fileformats/netcdf/test__load_aux_factory.py
+++ b/lib/iris/tests/unit/fileformats/netcdf/loader/test__load_aux_factory.py
@@ -16,7 +16,7 @@
 
 from iris.coords import DimCoord
 from iris.cube import Cube
-from iris.fileformats.netcdf import _load_aux_factory
+from iris.fileformats.netcdf.loader import _load_aux_factory
 
 
 class TestAtmosphereHybridSigmaPressureCoordinate(tests.IrisTest):
diff --git a/lib/iris/tests/unit/fileformats/netcdf/test__load_cube.py b/lib/iris/tests/unit/fileformats/netcdf/loader/test__load_cube.py
similarity index 96%
rename from lib/iris/tests/unit/fileformats/netcdf/test__load_cube.py
rename to lib/iris/tests/unit/fileformats/netcdf/loader/test__load_cube.py
index 0e98eec916..6e28a2f8e4 100644
--- a/lib/iris/tests/unit/fileformats/netcdf/test__load_cube.py
+++ b/lib/iris/tests/unit/fileformats/netcdf/loader/test__load_cube.py
@@ -15,7 +15,7 @@
 
 from iris.coords import DimCoord
 import iris.fileformats.cf
-from iris.fileformats.netcdf import _load_cube
+from iris.fileformats.netcdf.loader import _load_cube
 
 
 class TestCoordAttributes(tests.IrisTest):
@@ -28,7 +28,7 @@ def _patcher(engine, cf, cf_group):
         engine.cube_parts["coordinates"] = coordinates
 
     def setUp(self):
-        this = "iris.fileformats.netcdf._assert_case_specific_facts"
+        this = "iris.fileformats.netcdf.loader._assert_case_specific_facts"
         patch = mock.patch(this, side_effect=self._patcher)
         patch.start()
         self.addCleanup(patch.stop)
@@ -112,7 +112,7 @@ def test_flag_pass_thru_multi(self):
 
 class TestCubeAttributes(tests.IrisTest):
     def setUp(self):
-        this = "iris.fileformats.netcdf._assert_case_specific_facts"
+        this = "iris.fileformats.netcdf.loader._assert_case_specific_facts"
         patch = mock.patch(this)
         patch.start()
         self.addCleanup(patch.stop)
diff --git a/lib/iris/tests/unit/fileformats/netcdf/test__translate_constraints_to_var_callback.py b/lib/iris/tests/unit/fileformats/netcdf/loader/test__translate_constraints_to_var_callback.py
similarity index 97%
rename from lib/iris/tests/unit/fileformats/netcdf/test__translate_constraints_to_var_callback.py
rename to lib/iris/tests/unit/fileformats/netcdf/loader/test__translate_constraints_to_var_callback.py
index fb08ffda2b..77bb0d3950 100644
--- a/lib/iris/tests/unit/fileformats/netcdf/test__translate_constraints_to_var_callback.py
+++ b/lib/iris/tests/unit/fileformats/netcdf/loader/test__translate_constraints_to_var_callback.py
@@ -13,7 +13,9 @@
 
 import iris
 from iris.fileformats.cf import CFDataVariable
-from iris.fileformats.netcdf import _translate_constraints_to_var_callback
+from iris.fileformats.netcdf.loader import (
+    _translate_constraints_to_var_callback,
+)
 
 # import iris tests first so that some things can be initialised before
 # importing anything else
diff --git a/lib/iris/tests/unit/fileformats/netcdf/saver/__init__.py b/lib/iris/tests/unit/fileformats/netcdf/saver/__init__.py
new file mode 100644
index 0000000000..a68d5fc5d0
--- /dev/null
+++ b/lib/iris/tests/unit/fileformats/netcdf/saver/__init__.py
@@ -0,0 +1,6 @@
+# Copyright Iris contributors
+#
+# This file is part of Iris and is released under the LGPL license.
+# See COPYING and COPYING.LESSER in the root of the repository for full
+# licensing details.
+"""Unit tests for the :mod:`iris.fileformats.netcdf.saver` module."""
diff --git a/lib/iris/tests/unit/fileformats/netcdf/test__FillValueMaskCheckAndStoreTarget.py b/lib/iris/tests/unit/fileformats/netcdf/saver/test__FillValueMaskCheckAndStoreTarget.py
similarity index 97%
rename from lib/iris/tests/unit/fileformats/netcdf/test__FillValueMaskCheckAndStoreTarget.py
rename to lib/iris/tests/unit/fileformats/netcdf/saver/test__FillValueMaskCheckAndStoreTarget.py
index 01ba7ff38d..77209efafc 100644
--- a/lib/iris/tests/unit/fileformats/netcdf/test__FillValueMaskCheckAndStoreTarget.py
+++ b/lib/iris/tests/unit/fileformats/netcdf/saver/test__FillValueMaskCheckAndStoreTarget.py
@@ -17,7 +17,7 @@
 
 import numpy as np
 
-from iris.fileformats.netcdf import _FillValueMaskCheckAndStoreTarget
+from iris.fileformats.netcdf.saver import _FillValueMaskCheckAndStoreTarget
 
 
 class Test__FillValueMaskCheckAndStoreTarget(tests.IrisTest):
diff --git a/lib/iris/tests/unit/fileformats/netcdf/test_Saver.py b/lib/iris/tests/unit/fileformats/netcdf/test_Saver.py
index e17082b5e9..174a46fdb7 100644
--- a/lib/iris/tests/unit/fileformats/netcdf/test_Saver.py
+++ b/lib/iris/tests/unit/fileformats/netcdf/test_Saver.py
@@ -203,7 +203,7 @@ def test_big_endian(self):
 
     def test_zlib(self):
         cube = self._simple_cube(">f4")
-        api = self.patch("iris.fileformats.netcdf.netCDF4")
+        api = self.patch("iris.fileformats.netcdf.saver.netCDF4")
         # Define mocked default fill values to prevent deprecation warning (#4374).
         api.default_fillvals = collections.defaultdict(lambda: -99.0)
         with Saver("/dummy/path", "NETCDF4") as saver:
diff --git a/lib/iris/tests/unit/fileformats/netcdf/test_save.py b/lib/iris/tests/unit/fileformats/netcdf/test_save.py
index 669a3c4137..030edbfce2 100644
--- a/lib/iris/tests/unit/fileformats/netcdf/test_save.py
+++ b/lib/iris/tests/unit/fileformats/netcdf/test_save.py
@@ -143,7 +143,7 @@ def test_None(self):
         # Test that when no fill_value argument is passed, the fill_value
         # argument to Saver.write is None or not present.
         cubes = self._make_cubes()
-        with mock.patch("iris.fileformats.netcdf.Saver") as Saver:
+        with mock.patch("iris.fileformats.netcdf.saver.Saver") as Saver:
             save(cubes, "dummy.nc")
 
         # Get the Saver.write mock
@@ -161,7 +161,7 @@ def test_single(self):
         # that value is passed to each call to Saver.write
         cubes = self._make_cubes()
         fill_value = 12345.0
-        with mock.patch("iris.fileformats.netcdf.Saver") as Saver:
+        with mock.patch("iris.fileformats.netcdf.saver.Saver") as Saver:
             save(cubes, "dummy.nc", fill_value=fill_value)
 
         # Get the Saver.write mock
@@ -178,7 +178,7 @@ def test_multiple(self):
         # each element is passed to separate calls to Saver.write
         cubes = self._make_cubes()
         fill_values = [123.0, 456.0, 789.0]
-        with mock.patch("iris.fileformats.netcdf.Saver") as Saver:
+        with mock.patch("iris.fileformats.netcdf.saver.Saver") as Saver:
             save(cubes, "dummy.nc", fill_value=fill_values)
 
         # Get the Saver.write mock
@@ -195,7 +195,7 @@ def test_single_string(self):
         # that value is passed to calls to Saver.write
         cube = Cube(["abc", "def", "hij"])
         fill_value = "xyz"
-        with mock.patch("iris.fileformats.netcdf.Saver") as Saver:
+        with mock.patch("iris.fileformats.netcdf.saver.Saver") as Saver:
             save(cube, "dummy.nc", fill_value=fill_value)
 
         # Get the Saver.write mock
@@ -211,7 +211,7 @@ def test_multi_wrong_length(self):
         # is passed as the fill_value argument, an error is raised
         cubes = self._make_cubes()
         fill_values = [1.0, 2.0, 3.0, 4.0]
-        with mock.patch("iris.fileformats.netcdf.Saver"):
+        with mock.patch("iris.fileformats.netcdf.saver.Saver"):
             with self.assertRaises(ValueError):
                 save(cubes, "dummy.nc", fill_value=fill_values)
 
diff --git a/lib/iris/tests/unit/fileformats/pp/test__data_bytes_to_shaped_array.py b/lib/iris/tests/unit/fileformats/pp/test__data_bytes_to_shaped_array.py
index 83475c6782..73913c6219 100644
--- a/lib/iris/tests/unit/fileformats/pp/test__data_bytes_to_shaped_array.py
+++ b/lib/iris/tests/unit/fileformats/pp/test__data_bytes_to_shaped_array.py
@@ -17,10 +17,49 @@
 
 import numpy as np
 import numpy.ma as ma
+import pytest
 
 import iris.fileformats.pp as pp
 
 
+@pytest.mark.parametrize("data_shape", [(2, 3)])
+@pytest.mark.parametrize(
+    "expected_shape", [(2, 3), (3, 2), (1, 3), (2, 2), (3, 3), (2, 4)]
+)
+@pytest.mark.parametrize(
+    "data_type", [np.float32, np.int32, np.int16, np.int8]
+)
+def test_data_padding__no_compression(data_shape, expected_shape, data_type):
+    data = np.empty(data_shape, dtype=data_type)
+
+    # create the field data buffer
+    buffer = io.BytesIO()
+    buffer.write(data)
+    buffer.seek(0)
+    data_bytes = buffer.read()
+
+    lbpack = pp.SplittableInt(0, dict(n1=0, n2=1))
+    boundary_packing = None
+    mdi = -1
+    args = (
+        data_bytes,
+        lbpack,
+        boundary_packing,
+        expected_shape,
+        data_type,
+        mdi,
+    )
+    data_length, expected_length = np.prod(data_shape), np.prod(expected_shape)
+
+    if expected_length <= data_length:
+        result = pp._data_bytes_to_shaped_array(*args)
+        assert result.shape == expected_shape
+    else:
+        emsg = r"data containing \d+ words does not match expected length"
+        with pytest.raises(ValueError, match=emsg):
+            _ = pp._data_bytes_to_shaped_array(*args)
+
+
 class Test__data_bytes_to_shaped_array__lateral_boundary_compression(
     tests.IrisTest
 ):
diff --git a/lib/iris/tests/unit/fileformats/pp/test_save.py b/lib/iris/tests/unit/fileformats/pp/test_save.py
index 45012dc8bd..8200259cca 100644
--- a/lib/iris/tests/unit/fileformats/pp/test_save.py
+++ b/lib/iris/tests/unit/fileformats/pp/test_save.py
@@ -13,6 +13,8 @@
 
 import cf_units
 import cftime
+import numpy as np
+import pytest
 
 from iris.coords import CellMethod, DimCoord
 from iris.fileformats._ff_cross_references import STASH_TRANS
@@ -21,6 +23,27 @@
 import iris.tests.stock as stock
 
 
+@pytest.mark.parametrize(
+    "unit,modulus",
+    [
+        (cf_units.Unit("radians"), 2 * np.pi),
+        (cf_units.Unit("degrees"), 360.0),
+        (None, 360.0),
+    ],
+)
+def test_grid_and_pole__scalar_dim_longitude(unit, modulus):
+    cube = stock.lat_lon_cube()[:, -1:]
+    assert cube.ndim == 2
+    lon = cube.coord("longitude")
+    lon.units = unit
+
+    field = _pp_save_ppfield_values(cube)
+    bdx = modulus
+    assert field.bdx == bdx
+    assert field.bzx == (lon.points[0] - bdx)
+    assert field.lbnpt == lon.points.size
+
+
 def _pp_save_ppfield_values(cube):
     """
     Emulate saving a cube as PP, and capture the resulting PP field values.
diff --git a/lib/iris/tests/unit/representation/cube_printout/test_CubePrintout.py b/lib/iris/tests/unit/representation/cube_printout/test_CubePrintout.py
index ff42acf566..21fc8efa73 100644
--- a/lib/iris/tests/unit/representation/cube_printout/test_CubePrintout.py
+++ b/lib/iris/tests/unit/representation/cube_printout/test_CubePrintout.py
@@ -349,6 +349,20 @@ def test_section_vector_ancils(self):
         ]
         self.assertEqual(rep, expected)
 
+    def test_section_vector_ancils_length_1(self):
+        # Check ancillary variables that map to a cube dimension of length 1
+        # are not interpreted as scalar ancillary variables.
+        cube = Cube(np.zeros((1, 3)), long_name="name", units=1)
+        cube.add_ancillary_variable(AncillaryVariable([0], long_name="av1"), 0)
+
+        rep = cube_replines(cube)
+        expected = [
+            "name / (1)                          (-- : 1; -- : 3)",
+            "    Ancillary variables:",
+            "        av1                             x       -",
+        ]
+        self.assertEqual(rep, expected)
+
     def test_section_vector_cell_measures(self):
         cube = Cube(np.zeros((2, 3)), long_name="name", units=1)
         cube.add_cell_measure(CellMeasure([0, 1, 2], long_name="cm"), 1)
@@ -361,6 +375,20 @@ def test_section_vector_cell_measures(self):
         ]
         self.assertEqual(rep, expected)
 
+    def test_section_vector_cell_measures_length_1(self):
+        # Check cell measures that map to a cube dimension of length 1 are not
+        # interpreted as scalar cell measures.
+        cube = Cube(np.zeros((2, 1)), long_name="name", units=1)
+        cube.add_cell_measure(CellMeasure([0], long_name="cm"), 1)
+
+        rep = cube_replines(cube)
+        expected = [
+            "name / (1)                          (-- : 2; -- : 1)",
+            "    Cell measures:",
+            "        cm                              -       x",
+        ]
+        self.assertEqual(rep, expected)
+
     def test_section_scalar_coords(self):
         # incl points + bounds
         # TODO: ought to incorporate coord-based summary
@@ -424,8 +452,8 @@ def test_section_scalar_ancillaries(self):
         rep = cube_replines(cube)
         expected = [
             "name / (1)                          (-- : 2; -- : 3)",
-            "    Ancillary variables:",
-            "        av                              -       -",
+            "    Scalar ancillary variables:",
+            "        av",
         ]
         self.assertEqual(rep, expected)
 
diff --git a/lib/iris/tests/unit/representation/cube_summary/test_CubeSummary.py b/lib/iris/tests/unit/representation/cube_summary/test_CubeSummary.py
index 8314c5c9ae..bcf31a016f 100644
--- a/lib/iris/tests/unit/representation/cube_summary/test_CubeSummary.py
+++ b/lib/iris/tests/unit/representation/cube_summary/test_CubeSummary.py
@@ -75,6 +75,7 @@ def test_blank_cube(self):
             "Mesh:",
             "Scalar coordinates:",
             "Scalar cell measures:",
+            "Scalar ancillary variables:",
             "Cell methods:",
             "Attributes:",
         ]
@@ -222,7 +223,7 @@ def test_scalar_cube(self):
         self.assertTrue(
             all(sect.is_empty() for sect in rep.vector_sections.values())
         )
-        self.assertEqual(len(rep.scalar_sections), 5)
+        self.assertEqual(len(rep.scalar_sections), 6)
         self.assertEqual(
             len(rep.scalar_sections["Scalar coordinates:"].contents), 1
         )