From f41fbbb37c54a093b8c7ca8ad698fb7a58ac3a26 Mon Sep 17 00:00:00 2001 From: Ruth Comer <10599679+rcomer@users.noreply.github.com> Date: Fri, 25 Mar 2022 09:36:26 +0000 Subject: [PATCH 01/15] Preserve position when replacing Axes with GeoAxes (#4273) * preserve position * add test * comment to clarify test * whatsnew * use original position --- docs/src/whatsnew/dev.rst | 8 +++- lib/iris/plot.py | 2 + .../test__replace_axes_with_cartopy_axes.py | 45 +++++++++++++++++++ 3 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 lib/iris/tests/unit/plot/test__replace_axes_with_cartopy_axes.py diff --git a/docs/src/whatsnew/dev.rst b/docs/src/whatsnew/dev.rst index 63839825e1..8821c6c676 100644 --- a/docs/src/whatsnew/dev.rst +++ b/docs/src/whatsnew/dev.rst @@ -41,14 +41,18 @@ This document explains the changes made to Iris for this release #. `@rcomer`_ reverted part of the change from :pull:`3906` so that :func:`iris.plot.plot` no longer defaults to placing a "Y" coordinate (e.g. latitude) on the y-axis of the plot. (:issue:`4493`, :pull:`4601`) - -#. `@rcomer`_ enabled passing of scalar objects to :func:`~iris.plot.plot` and + +#. `@rcomer`_ enabled passing of scalar objects to :func:`~iris.plot.plot` and :func:`~iris.plot.scatter`. (:pull:`4616`) #. `@rcomer`_ fixed :meth:`~iris.cube.Cube.aggregated_by` with `mdtol` for 1D cubes where an aggregated section is entirely masked, reported at :issue:`3190`. (:pull:`4246`) +#. `@rcomer`_ ensured that a :class:`matplotlib.axes.Axes`'s position is preserved + when Iris replaces it with a :class:`cartopy.mpl.geoaxes.GeoAxes`, fixing + :issue:`1157`. (:pull:`4273`) + 💣 Incompatible Changes ======================= diff --git a/lib/iris/plot.py b/lib/iris/plot.py index d886ac1cf9..10bd740306 100644 --- a/lib/iris/plot.py +++ b/lib/iris/plot.py @@ -843,7 +843,9 @@ def _replace_axes_with_cartopy_axes(cartopy_proj): ylabel=ax.get_ylabel(), ) else: + position = ax.get_position(original=True) _ = fig.add_axes( + position, projection=cartopy_proj, title=ax.get_title(), xlabel=ax.get_xlabel(), diff --git a/lib/iris/tests/unit/plot/test__replace_axes_with_cartopy_axes.py b/lib/iris/tests/unit/plot/test__replace_axes_with_cartopy_axes.py new file mode 100644 index 0000000000..c4416c587d --- /dev/null +++ b/lib/iris/tests/unit/plot/test__replace_axes_with_cartopy_axes.py @@ -0,0 +1,45 @@ +# 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 `iris.plot.__replace_axes_with_cartopy_axes` function.""" + +# Import iris.tests first so that some things can be initialised before +# importing anything else. +import iris.tests as tests # isort:skip + +import cartopy.crs as ccrs +import matplotlib.pyplot as plt + +from iris.plot import _replace_axes_with_cartopy_axes + + +@tests.skip_plot +class Test_replace_axes_with_cartopy_axes(tests.IrisTest): + def setUp(self): + self.fig = plt.figure() + + def test_preserve_position(self): + position = [0.17, 0.65, 0.2, 0.2] + projection = ccrs.PlateCarree() + + plt.axes(position) + _replace_axes_with_cartopy_axes(projection) + result = plt.gca() + + # result should be the same as an axes created directly with the projection. + expected = plt.axes(position, projection=projection) + + # get_position returns mpl.transforms.Bbox object, for which equality does + # not appear to be implemented. Compare the bounds (tuple) instead. + self.assertEqual( + expected.get_position().bounds, result.get_position().bounds + ) + + def tearDown(self): + plt.close(self.fig) + + +if __name__ == "__main__": + tests.main() From 4cc233682cd044a247bd6487446eaff4f4789537 Mon Sep 17 00:00:00 2001 From: Ruth Comer <10599679+rcomer@users.noreply.github.com> Date: Fri, 25 Mar 2022 10:10:40 +0000 Subject: [PATCH 02/15] Lazy Percentile Aggregator (#3901) * implement lazy percentile * fix map_complete_blocks tests * fix legacy masked type no mask tests * add test for map_complete_blocks on dask array * handle axis expressed as sequence * fix docstring * add lazy_aggregate method and mdtol handling * reshape for multi-dim collapse * mdtol handling for added trailing dimension * update aggregator instance docstring * simplify array reshape * factor out array reshaping as decorator * reduce repetition in tests * PERCENTILE multi-axis tests * add tests for array wrangling * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * py3.7 * test_PERCENTILE.py tweaks * cut down post_process tests * clarify wrapper tests * whatsnew * revert py3.7 change * Update lib/iris/analysis/__init__.py Co-authored-by: Will Benfold <69585101+wjbenfold@users.noreply.github.com> * review: remove docstring kwarg; define _real_percentile * review: blank line for note * review: remove scalar_percent check * review: test__axis_to_single_trailing.py * review: simplify structure * review: _percentile docstring args * review: remove axis description from _percentile docstring * review: don't use rollaxis * review: generalize LazyMixin to also work for non-lazy. Rename * review: move Test_lazy_aggregate tests into MaskedCalcMixin * review: generalise MaskedCalcMixin * review: remove stray as_lazy_data calls * review: use mixins for Test_aggregate * review: move multi axis tests to their own mixin * review: use MultiAxisMixin in both lazy classes * review: add tests for fast real aggregation * docstrings * review: test clarity tweaks * review: test alphap, betap passed to scipy * review: use assertEqual Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Will Benfold <69585101+wjbenfold@users.noreply.github.com> --- docs/src/whatsnew/dev.rst | 2 + lib/iris/_lazy_data.py | 13 +- lib/iris/analysis/__init__.py | 195 +++++++++--- .../tests/unit/analysis/test_PERCENTILE.py | 284 +++++++++++++++--- .../analysis/test_PercentileAggregator.py | 32 +- .../analysis/test__axis_to_single_trailing.py | 150 +++++++++ .../lazy_data/test_map_complete_blocks.py | 10 + 7 files changed, 579 insertions(+), 107 deletions(-) create mode 100644 lib/iris/tests/unit/analysis/test__axis_to_single_trailing.py diff --git a/docs/src/whatsnew/dev.rst b/docs/src/whatsnew/dev.rst index 8821c6c676..9ed3cb23c2 100644 --- a/docs/src/whatsnew/dev.rst +++ b/docs/src/whatsnew/dev.rst @@ -34,6 +34,8 @@ This document explains the changes made to Iris for this release #. `@wjbenfold`_ added support for ``false_easting`` and ``false_northing`` to :class:`~iris.coord_system.Mercator`. (:issue:`3107`, :pull:`4524`) +#. `@rcomer`_ implemented lazy aggregation for the + :obj:`iris.analysis.PERCENTILE` aggregator. (:pull:`3901`) 🐛 Bugs Fixed ============= diff --git a/lib/iris/_lazy_data.py b/lib/iris/_lazy_data.py index 038f9d9337..27f09b2a35 100644 --- a/lib/iris/_lazy_data.py +++ b/lib/iris/_lazy_data.py @@ -359,7 +359,7 @@ def map_complete_blocks(src, func, dims, out_sizes): Args: - * src (:class:`~iris.cube.Cube`): + * src (:class:`~iris.cube.Cube` or array-like): Source cube that function is applied to. * func: Function to apply. @@ -369,10 +369,15 @@ def map_complete_blocks(src, func, dims, out_sizes): Output size of dimensions that cannot be chunked. """ - if not src.has_lazy_data(): + if is_lazy_data(src): + data = src + elif not hasattr(src, "has_lazy_data"): + # Not a lazy array and not a cube. So treat as ordinary numpy array. + return func(src) + elif not src.has_lazy_data(): return func(src.data) - - data = src.lazy_data() + else: + data = src.lazy_data() # Ensure dims are not chunked in_chunks = list(data.chunks) diff --git a/lib/iris/analysis/__init__.py b/lib/iris/analysis/__init__.py index 69f76e68eb..f9eb1a412a 100644 --- a/lib/iris/analysis/__init__.py +++ b/lib/iris/analysis/__init__.py @@ -37,6 +37,7 @@ from collections import OrderedDict from collections.abc import Iterable +import functools from functools import wraps import dask.array as da @@ -683,7 +684,7 @@ class PercentileAggregator(_Aggregator): """ - def __init__(self, units_func=None, lazy_func=None, **kwargs): + def __init__(self, units_func=None, **kwargs): """ Create a percentile aggregator. @@ -696,11 +697,6 @@ def __init__(self, units_func=None, lazy_func=None, **kwargs): Returns an :class:`cf_units.Unit`, or a value that can be made into one. - * lazy_func (callable or None): - An alternative to :data:`call_func` implementing a lazy - aggregation. Note that, it need not support all features of the - main operation, but should raise an error in unhandled cases. - Additional kwargs:: Passed through to :data:`call_func` and :data:`lazy_func`. @@ -718,7 +714,7 @@ def __init__(self, units_func=None, lazy_func=None, **kwargs): None, _percentile, units_func=units_func, - lazy_func=lazy_func, + lazy_func=_build_dask_mdtol_function(_percentile), **kwargs, ) @@ -764,6 +760,45 @@ def aggregate(self, data, axis, **kwargs): return _Aggregator.aggregate(self, data, axis, **kwargs) + def lazy_aggregate(self, data, axis, **kwargs): + """ + Perform aggregation over the data with a lazy operation, analogous to + the 'aggregate' result. + + Keyword arguments are passed through to the data aggregation function + (for example, the "percent" keyword for a percentile aggregator). + This function is usually used in conjunction with update_metadata(), + which should be passed the same keyword arguments. + + Args: + + * data (array): + A lazy array (:class:`dask.array.Array`). + + * axis (int or list of int): + The dimensions to aggregate over -- note that this is defined + differently to the 'aggregate' method 'axis' argument, which only + accepts a single dimension index. + + Kwargs: + + * kwargs: + All keyword arguments are passed through to the data aggregation + function. + + Returns: + A lazy array representing the result of the aggregation operation + (:class:`dask.array.Array`). + + """ + + msg = "{} aggregator requires the mandatory keyword argument {!r}." + for arg in self._args: + if arg not in kwargs: + raise ValueError(msg.format(self.name(), arg)) + + return _Aggregator.lazy_aggregate(self, data, axis, **kwargs) + def post_process(self, collapsed_cube, data_result, coords, **kwargs): """ Process the result from :func:`iris.analysis.Aggregator.aggregate`. @@ -1127,7 +1162,9 @@ def _build_dask_mdtol_function(dask_stats_function): call signature : "dask_stats_function(data, axis=axis, **kwargs)". It must be masked-data tolerant, i.e. it ignores masked input points and performs a calculation on only the unmasked points. - For example, mean([1, --, 2]) = (1 + 2) / 2 = 1.5. + For example, mean([1, --, 2]) = (1 + 2) / 2 = 1.5. If an additional + dimension is created by 'dask_function', it is assumed to be the trailing + one (as for '_percentile'). The returned value is a new function operating on dask arrays. It has the call signature `stat(data, axis=-1, mdtol=None, **kwargs)`. @@ -1147,6 +1184,12 @@ def inner_stat(array, axis=-1, mdtol=None, **kwargs): points_per_calc = array.size / dask_result.size masked_point_fractions = point_mask_counts / points_per_calc boolean_mask = masked_point_fractions > mdtol + if dask_result.ndim > boolean_mask.ndim: + # dask_stats_function created trailing dimension. + boolean_mask = da.broadcast_to( + boolean_mask.reshape(boolean_mask.shape + (1,)), + dask_result.shape, + ) # Return an mdtol-masked version of the basic result. result = da.ma.masked_array( da.ma.getdata(dask_result), boolean_mask @@ -1156,30 +1199,47 @@ def inner_stat(array, axis=-1, mdtol=None, **kwargs): return inner_stat -def _percentile(data, axis, percent, fast_percentile_method=False, **kwargs): +def _axis_to_single_trailing(stats_function): """ - The percentile aggregator is an additive operation. This means that - it *may* introduce a new dimension to the data for the statistic being - calculated, but only if more than one percentile point is requested. + Given a statistical function that acts on the trailing axis of a 1D or 2D + array, wrap it so that higher dimension arrays can be passed, as well as any + axis as int or tuple. - If a new additive dimension is formed, then it will always be the last - dimension of the resulting percentile data payload. + """ - Kwargs: + @wraps(stats_function) + def inner_stat(data, axis, *args, **kwargs): + # Get data as a 1D or 2D view with the target axis as the trailing one. + if not isinstance(axis, Iterable): + axis = (axis,) + end = range(-len(axis), 0) + + data = np.moveaxis(data, axis, end) + shape = data.shape[: -len(axis)] # Shape of dims we won't collapse. + if shape: + data = data.reshape(np.prod(shape), -1) + else: + data = data.flatten() - * fast_percentile_method (boolean) : - When set to True, uses the numpy.percentiles method as a faster - alternative to the scipy.mstats.mquantiles method. Does not handle - masked arrays. + result = stats_function(data, *args, **kwargs) + + # Ensure to unflatten any leading dimensions. + if shape: + # Account for the additive dimension if necessary. + if result.size > np.prod(shape): + shape += (-1,) + result = result.reshape(shape) + + return result + + return inner_stat + + +def _calc_percentile(data, percent, fast_percentile_method=False, **kwargs): + """ + Calculate percentiles along the trailing axis of a 1D or 2D array. """ - # Ensure that the target axis is the last dimension. - data = np.rollaxis(data, axis, start=data.ndim) - shape = data.shape[:-1] - # Flatten any leading dimensions. - if shape: - data = data.reshape([np.prod(shape), data.shape[-1]]) - # Perform the percentile calculation. if fast_percentile_method: msg = "Cannot use fast np.percentile method with masked array." if ma.is_masked(data): @@ -1187,28 +1247,63 @@ def _percentile(data, axis, percent, fast_percentile_method=False, **kwargs): result = np.percentile(data, percent, axis=-1) result = result.T else: - quantiles = np.array(percent) / 100.0 + quantiles = percent / 100.0 result = scipy.stats.mstats.mquantiles( data, quantiles, axis=-1, **kwargs ) if not ma.isMaskedArray(data) and not ma.is_masked(result): - result = np.asarray(result) + return np.asarray(result) else: - result = ma.MaskedArray(result) + return ma.MaskedArray(result) + + +@_axis_to_single_trailing +def _percentile(data, percent, fast_percentile_method=False, **kwargs): + """ + The percentile aggregator is an additive operation. This means that + it *may* introduce a new dimension to the data for the statistic being + calculated, but only if more than one percentile point is requested. + + If a new additive dimension is formed, then it will always be the last + dimension of the resulting percentile data payload. + + Args: + + * data (array-like) + array from which percentiles are to be calculated + + Kwargs: + + * fast_percentile_method (boolean) + When set to True, uses the numpy.percentiles method as a faster + alternative to the scipy.mstats.mquantiles method. Does not handle + masked arrays. + + **kwargs + passed to scipy.stats.mstats.mquantiles if fast_percentile_method is + False + + """ + if not isinstance(percent, Iterable): + percent = [percent] + percent = np.array(percent) + + # Perform the percentile calculation. + _partial_percentile = functools.partial( + _calc_percentile, + percent=percent, + fast_percentile_method=fast_percentile_method, + **kwargs, + ) + + result = iris._lazy_data.map_complete_blocks( + data, _partial_percentile, (-1,), percent.shape + ) - # Ensure to unflatten any leading dimensions. - if shape: - if not isinstance(percent, Iterable): - percent = [percent] - percent = np.array(percent) - # Account for the additive dimension. - if percent.shape > (1,): - shape += percent.shape - result = result.reshape(shape) # Check whether to reduce to a scalar result, as per the behaviour # of other aggregators. - if result.shape == (1,) and quantiles.ndim == 0: - result = result[0] + if result.shape == (1,): + result = np.squeeze(result) return result @@ -1738,9 +1833,10 @@ def interp_order(length): PERCENTILE = PercentileAggregator(alphap=1, betap=1) """ -An :class:`~iris.analysis.PercentileAggregator` instance that calculates the +A :class:`~iris.analysis.PercentileAggregator` instance that calculates the percentile over a :class:`~iris.cube.Cube`, as computed by -:func:`scipy.stats.mstats.mquantiles`. +:func:`scipy.stats.mstats.mquantiles` (default) or :func:`numpy.percentile` (if +fast_percentile_method is True). **Required** kwargs associated with the use of this aggregator: @@ -1755,6 +1851,11 @@ def interp_order(length): * betap (float): Plotting positions parameter, see :func:`scipy.stats.mstats.mquantiles`. Defaults to 1. +* fast_percentile_method (boolean): + When set to True, uses :func:`numpy.percentile` method as a faster + alternative to the :func:`scipy.stats.mstats.mquantiles` method. alphap and + betap are ignored. An exception is raised if the data are masked. + Defaults to False. **For example**: @@ -1762,7 +1863,15 @@ def interp_order(length): result = cube.collapsed('time', iris.analysis.PERCENTILE, percent=[10, 90]) -This aggregator handles masked data. +This aggregator handles masked data and lazy data. + +.. note:: + + Performance of this aggregator on lazy data is particularly sensitive to + the dask array chunking, so it may be useful to test with various chunk + sizes for a given application. Any chunking along the dimensions to be + aggregated is removed by the aggregator prior to calculating the + percentiles. """ diff --git a/lib/iris/tests/unit/analysis/test_PERCENTILE.py b/lib/iris/tests/unit/analysis/test_PERCENTILE.py index 52648f6fb8..1e4ba3af0f 100644 --- a/lib/iris/tests/unit/analysis/test_PERCENTILE.py +++ b/lib/iris/tests/unit/analysis/test_PERCENTILE.py @@ -9,92 +9,276 @@ # importing anything else. import iris.tests as tests # isort:skip +from unittest import mock + import numpy as np import numpy.ma as ma +from iris._lazy_data import as_concrete_data, as_lazy_data, is_lazy_data from iris.analysis import PERCENTILE -class Test_aggregate(tests.IrisTest): - def test_missing_mandatory_kwarg(self): - emsg = "percentile aggregator requires .* keyword argument 'percent'" - with self.assertRaisesRegex(ValueError, emsg): - PERCENTILE.aggregate("dummy", axis=0) +class AggregateMixin: + """ + Percentile aggregation tests for both numpy and scipy methods within lazy + and real percentile aggregation. + + """ + + def check_percentile_calc( + self, data, axis, percent, expected, approx=False, **kwargs + ): + if self.lazy: + data = as_lazy_data(data) + + expected = np.array(expected) + + actual = self.agg_method( + data, + axis=axis, + percent=percent, + fast_percentile_method=self.fast, + **kwargs, + ) + + self.assertTupleEqual(actual.shape, expected.shape) + is_lazy = is_lazy_data(actual) + + if self.lazy: + self.assertTrue(is_lazy) + actual = as_concrete_data(actual) + else: + self.assertFalse(is_lazy) + + if approx: + self.assertArrayAlmostEqual(actual, expected) + else: + self.assertArrayEqual(actual, expected) def test_1d_single(self): data = np.arange(11) - actual = PERCENTILE.aggregate(data, axis=0, percent=50) + axis = 0 + percent = 50 expected = 5 - self.assertTupleEqual(actual.shape, ()) - self.assertEqual(actual, expected) - - def test_masked_1d_single(self): - data = ma.arange(11) - data[3:7] = ma.masked - actual = PERCENTILE.aggregate(data, axis=0, percent=50) - expected = 7 - self.assertTupleEqual(actual.shape, ()) - self.assertEqual(actual, expected) + self.check_percentile_calc(data, axis, percent, expected) def test_1d_multi(self): data = np.arange(11) percent = np.array([20, 50, 90]) - actual = PERCENTILE.aggregate(data, axis=0, percent=percent) + axis = 0 expected = [2, 5, 9] - self.assertTupleEqual(actual.shape, percent.shape) - self.assertArrayEqual(actual, expected) + self.check_percentile_calc(data, axis, percent, expected) + + def test_2d_single(self): + shape = (2, 11) + data = np.arange(np.prod(shape)).reshape(shape) + axis = 0 + percent = 50 + expected = np.arange(shape[-1]) + 5.5 + self.check_percentile_calc(data, axis, percent, expected) + + def test_2d_multi(self): + shape = (2, 10) + data = np.arange(np.prod(shape)).reshape(shape) + axis = 0 + percent = np.array([10, 50, 90, 100]) + expected = np.tile(np.arange(shape[-1]), percent.size) + expected = expected.reshape(percent.size, shape[-1]).T + 1 + expected = expected + (percent / 10 - 1) + self.check_percentile_calc(data, axis, percent, expected, approx=True) + + +class MaskedAggregateMixin: + """ + Tests for calculations on masked data. Will only work if using the standard + (scipy) method. Needs to be used with AggregateMixin, as these tests re-use its + method. + + """ + + def test_masked_1d_single(self): + data = ma.arange(11) + data[3:7] = ma.masked + axis = 0 + percent = 50 + expected = 7 + self.check_percentile_calc(data, axis, percent, expected) def test_masked_1d_multi(self): data = ma.arange(11) data[3:9] = ma.masked percent = np.array([25, 50, 75]) - actual = PERCENTILE.aggregate(data, axis=0, percent=percent) + axis = 0 expected = [1, 2, 9] - self.assertTupleEqual(actual.shape, percent.shape) - self.assertArrayEqual(actual, expected) - - def test_2d_single(self): - shape = (2, 11) - data = np.arange(np.prod(shape)).reshape(shape) - actual = PERCENTILE.aggregate(data, axis=0, percent=50) - self.assertTupleEqual(actual.shape, shape[-1:]) - expected = np.arange(shape[-1]) + 5.5 - self.assertArrayEqual(actual, expected) + self.check_percentile_calc(data, axis, percent, expected) def test_masked_2d_single(self): shape = (2, 11) data = ma.arange(np.prod(shape)).reshape(shape) data[0, ::2] = ma.masked data[1, 1::2] = ma.masked - actual = PERCENTILE.aggregate(data, axis=0, percent=50) - self.assertTupleEqual(actual.shape, shape[-1:]) + axis = 0 + percent = 50 + # data has only one value for each column being aggregated, so result + # should be that value. expected = np.empty(shape[-1:]) expected[1::2] = data[0, 1::2] expected[::2] = data[1, ::2] - self.assertArrayEqual(actual, expected) - - def test_2d_multi(self): - shape = (2, 10) - data = np.arange(np.prod(shape)).reshape(shape) - percent = np.array([10, 50, 90, 100]) - actual = PERCENTILE.aggregate(data, axis=0, percent=percent) - self.assertTupleEqual(actual.shape, (shape[-1], percent.size)) - expected = np.tile(np.arange(shape[-1]), percent.size) - expected = expected.reshape(percent.size, shape[-1]).T + 1 - expected = expected + (percent / 10 - 1) - self.assertArrayAlmostEqual(actual, expected) + self.check_percentile_calc(data, axis, percent, expected) def test_masked_2d_multi(self): shape = (3, 10) data = ma.arange(np.prod(shape)).reshape(shape) data[1] = ma.masked percent = np.array([10, 50, 70, 80]) - actual = PERCENTILE.aggregate(data, axis=0, percent=percent) - self.assertTupleEqual(actual.shape, (shape[-1], percent.size)) - expected = np.tile(np.arange(shape[-1]), percent.size) - expected = expected.reshape(percent.size, shape[-1]).T - expected = expected + (percent / 10 * 2) - self.assertArrayAlmostEqual(actual, expected) + axis = 0 + mdtol = 0.1 + + # First column is just 0 and 20. Percentiles of these can be calculated as + # linear interpolation. + expected = percent / 100 * 20 + # Other columns are first column plus column number. + expected = ( + np.broadcast_to(expected, (shape[-1], percent.size)) + + np.arange(shape[-1])[:, np.newaxis] + ) + + self.check_percentile_calc( + data, axis, percent, expected, mdtol=mdtol, approx=True + ) + + @mock.patch("scipy.stats.mstats.mquantiles") + def test_default_kwargs_passed(self, mocked_mquantiles): + data = np.arange(5) + percent = 50 + axis = 0 + self.agg_method(data, axis=axis, percent=percent) + for key in ["alphap", "betap"]: + self.assertEqual(mocked_mquantiles.call_args.kwargs[key], 1) + + @mock.patch("scipy.stats.mstats.mquantiles") + def test_chosen_kwargs_passed(self, mocked_mquantiles): + data = np.arange(5) + percent = 50 + axis = 0 + self.agg_method( + data, axis=axis, percent=percent, alphap=0.6, betap=0.5 + ) + for key, val in zip(["alphap", "betap"], [0.6, 0.5]): + self.assertEqual(mocked_mquantiles.call_args.kwargs[key], val) + + +class Test_aggregate(tests.IrisTest, AggregateMixin, MaskedAggregateMixin): + """Tests for standard aggregation method on real data.""" + + def setUp(self): + self.fast = False + self.lazy = False + self.agg_method = PERCENTILE.aggregate + + def test_missing_mandatory_kwarg(self): + emsg = "percentile aggregator requires .* keyword argument 'percent'" + with self.assertRaisesRegex(ValueError, emsg): + PERCENTILE.aggregate("dummy", axis=0) + + +class Test_fast_aggregate(tests.IrisTest, AggregateMixin): + """Tests for fast percentile method on real data.""" + + def setUp(self): + self.fast = True + self.lazy = False + self.agg_method = PERCENTILE.aggregate + + def test_masked(self): + shape = (2, 11) + data = ma.arange(np.prod(shape)).reshape(shape) + data[0, ::2] = ma.masked + emsg = "Cannot use fast np.percentile method with masked array." + with self.assertRaisesRegex(TypeError, emsg): + PERCENTILE.aggregate( + data, axis=0, percent=50, fast_percentile_method=True + ) + + +class MultiAxisMixin: + """ + Tests for axis passed as a tuple. Only relevant for lazy aggregation since + axis is always specified as int for real aggregation. + + """ + + def test_multi_axis(self): + data = np.arange(24).reshape((2, 3, 4)) + collapse_axes = (0, 2) + lazy_data = as_lazy_data(data) + percent = 30 + actual = PERCENTILE.lazy_aggregate( + lazy_data, + axis=collapse_axes, + percent=percent, + fast_percentile_method=self.fast, + ) + self.assertTrue(is_lazy_data(actual)) + result = as_concrete_data(actual) + self.assertTupleEqual(result.shape, (3,)) + for num, sub_result in enumerate(result): + # results should be the same as percentiles calculated from slices. + self.assertArrayAlmostEqual( + sub_result, np.percentile(data[:, num, :], percent) + ) + + def test_multi_axis_multi_percent(self): + data = np.arange(24).reshape((2, 3, 4)) + collapse_axes = (0, 2) + lazy_data = as_lazy_data(data) + percent = [20, 30, 50, 70, 80] + actual = PERCENTILE.lazy_aggregate( + lazy_data, + axis=collapse_axes, + percent=percent, + fast_percentile_method=self.fast, + ) + self.assertTrue(is_lazy_data(actual)) + result = as_concrete_data(actual) + self.assertTupleEqual(result.shape, (3, 5)) + for num, sub_result in enumerate(result): + # results should be the same as percentiles calculated from slices. + self.assertArrayAlmostEqual( + sub_result, np.percentile(data[:, num, :], percent) + ) + + +class Test_lazy_fast_aggregate(tests.IrisTest, AggregateMixin, MultiAxisMixin): + """Tests for fast aggregation on lazy data.""" + + def setUp(self): + self.fast = True + self.lazy = True + self.agg_method = PERCENTILE.lazy_aggregate + + def test_masked(self): + shape = (2, 11) + data = ma.arange(np.prod(shape)).reshape(shape) + data[0, ::2] = ma.masked + data = as_lazy_data(data) + actual = PERCENTILE.lazy_aggregate( + data, axis=0, percent=50, fast_percentile_method=True + ) + emsg = "Cannot use fast np.percentile method with masked array." + with self.assertRaisesRegex(TypeError, emsg): + as_concrete_data(actual) + + +class Test_lazy_aggregate( + tests.IrisTest, AggregateMixin, MaskedAggregateMixin, MultiAxisMixin +): + """Tests for standard aggregation on lazy data.""" + + def setUp(self): + self.fast = False + self.lazy = True + self.agg_method = PERCENTILE.lazy_aggregate class Test_name(tests.IrisTest): diff --git a/lib/iris/tests/unit/analysis/test_PercentileAggregator.py b/lib/iris/tests/unit/analysis/test_PercentileAggregator.py index a8e6ed28ed..f11cd7a8d3 100644 --- a/lib/iris/tests/unit/analysis/test_PercentileAggregator.py +++ b/lib/iris/tests/unit/analysis/test_PercentileAggregator.py @@ -14,9 +14,11 @@ from unittest import mock +import dask.array as da import numpy as np -from iris.analysis import PercentileAggregator, _percentile +from iris._lazy_data import as_concrete_data +from iris.analysis import PercentileAggregator from iris.coords import AuxCoord, DimCoord from iris.cube import Cube @@ -24,16 +26,10 @@ class Test(tests.IrisTest): def test_init(self): name = "percentile" - call_func = _percentile units_func = mock.sentinel.units_func - lazy_func = mock.sentinel.lazy_func - aggregator = PercentileAggregator( - units_func=units_func, lazy_func=lazy_func - ) + aggregator = PercentileAggregator(units_func=units_func) self.assertEqual(aggregator.name(), name) - self.assertIs(aggregator.call_func, call_func) self.assertIs(aggregator.units_func, units_func) - self.assertIs(aggregator.lazy_func, lazy_func) self.assertIsNone(aggregator.cell_method) @@ -85,7 +81,7 @@ def test_simple_multiple_points(self): self.cube_simple, data, coords, **kwargs ) self.assertEqual(actual.shape, percent.shape + self.cube_simple.shape) - expected = np.rollaxis(data, -1) + expected = data.T self.assertArrayEqual(actual.data, expected) name = "percentile_over_time" coord = actual.coord(name) @@ -119,13 +115,29 @@ def test_multi_multiple_points(self): self.cube_multi, data, coords, **kwargs ) self.assertEqual(actual.shape, percent.shape + self.cube_multi.shape) - expected = np.rollaxis(data, -1) + expected = np.moveaxis(data, -1, 0) self.assertArrayEqual(actual.data, expected) name = "percentile_over_time" coord = actual.coord(name) expected = AuxCoord(percent, long_name=name, units="percent") self.assertEqual(coord, expected) + def test_multi_multiple_points_lazy(self): + # Check that lazy data is preserved. + aggregator = PercentileAggregator() + percent = np.array([17, 29, 81]) + kwargs = dict(percent=percent) + shape = self.cube_multi.shape + percent.shape + data = da.arange(np.prod(shape)).reshape(shape) + coords = [self.coord_multi_0] + actual = aggregator.post_process( + self.cube_multi, data, coords, **kwargs + ) + self.assertEqual(actual.shape, percent.shape + self.cube_multi.shape) + self.assertTrue(actual.has_lazy_data()) + expected = np.moveaxis(as_concrete_data(data), -1, 0) + self.assertArrayEqual(actual.data, expected) + if __name__ == "__main__": tests.main() diff --git a/lib/iris/tests/unit/analysis/test__axis_to_single_trailing.py b/lib/iris/tests/unit/analysis/test__axis_to_single_trailing.py new file mode 100644 index 0000000000..505a00df78 --- /dev/null +++ b/lib/iris/tests/unit/analysis/test__axis_to_single_trailing.py @@ -0,0 +1,150 @@ +# 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 :data:`iris.analysis._axis_to_single_trailing` function.""" + +# Import iris.tests first so that some things can be initialised before +# importing anything else. +import iris.tests as tests # isort:skip + +from unittest import mock + +import dask.array as da +import numpy as np + +from iris._lazy_data import as_concrete_data, as_lazy_data, is_lazy_data +from iris.analysis import _axis_to_single_trailing + + +class TestInputReshape(tests.IrisTest): + """Tests to make sure correct array is passed into stat function.""" + + def setUp(self): + self.stat_func = mock.Mock() + + def check_input(self, data, axis, expected): + """ + Given data and axis passed to the wrapped function, check that expected + array is passed to the inner function. + + """ + wrapped_stat_func = _axis_to_single_trailing(self.stat_func) + wrapped_stat_func(data, axis=axis) + # Can't use Mock.assert_called_with because array equality is ambiguous + # get hold of the first arg instead. + self.assertArrayEqual(self.stat_func.call_args.args[0], expected) + + def test_1d_input(self): + # Trailing axis chosen, so array should be unchanged. + data = np.arange(5) + axis = 0 + self.check_input(data, axis, data) + + def test_2d_input_trailing(self): + # Trailing axis chosen, so array should be unchanged. + data = np.arange(6).reshape(2, 3) + axis = 1 + self.stat_func.return_value = np.empty(2) + self.check_input(data, axis, data) + + def test_2d_input_transpose(self): + # Leading axis chosen, so array should be transposed. + data = np.arange(6).reshape(2, 3) + axis = 0 + self.stat_func.return_value = np.empty(3) + self.check_input(data, axis, data.T) + + def test_3d_input_middle(self): + # Middle axis is chosen, should be moved to end. Other dims should be + # flattened. + data = np.arange(24).reshape(2, 3, 4) + axis = 1 + self.stat_func.return_value = np.empty(8) + expected = np.moveaxis(data, 1, 2).reshape(8, 3) + self.check_input(data, axis, expected) + + def test_3d_input_leading_multiple(self): + # First 2 axis chosen, should be flattened and moved to end. + data = np.arange(24).reshape(2, 3, 4) + axis = (0, 1) + self.stat_func.return_value = np.empty(4) + expected = np.moveaxis(data, 2, 0).reshape(4, 6) + self.check_input(data, axis, expected) + + def test_4d_first_and_last(self): + data = np.arange(120).reshape(2, 3, 4, 5) + axis = (0, -1) + self.stat_func.return_value = np.empty(12) + expected = np.moveaxis(data, 0, 2).reshape(12, 10) + self.check_input(data, axis, expected) + + def test_3d_input_leading_multiple_lazy(self): + # First 2 axis chosen, should be flattened and moved to end. Lazy data + # should be preserved. + data = np.arange(24).reshape(2, 3, 4) + lazy_data = as_lazy_data(data) + axis = (0, 1) + self.stat_func.return_value = np.empty(4) + expected = np.moveaxis(data, 2, 0).reshape(4, 6) + + wrapped_stat_func = _axis_to_single_trailing(self.stat_func) + wrapped_stat_func(lazy_data, axis=axis) + self.assertTrue(is_lazy_data(self.stat_func.call_args.args[0])) + self.assertArrayEqual( + as_concrete_data(self.stat_func.call_args.args[0]), expected + ) + + +class TestOutputReshape(tests.IrisTest): + """Tests to make sure array from stat function is handled correctly.""" + + def setUp(self): + self.stat_func = mock.Mock() + + def test_1d_input_1d_output(self): + # If array is fully aggregated, result should be same as returned by stat + # function. + data = np.arange(3) + self.stat_func.return_value = np.arange(2) + wrapped_stat_func = _axis_to_single_trailing(self.stat_func) + result = wrapped_stat_func(data, axis=0) + self.assertArrayEqual(result, self.stat_func.return_value) + + def test_3d_input_middle_single_stat(self): + # result shape should match non-aggregated input dims. + data = np.empty((2, 3, 4)) + axis = 1 + self.stat_func.return_value = np.arange(8) + expected = np.arange(8).reshape(2, 4) + wrapped_stat_func = _axis_to_single_trailing(self.stat_func) + result = wrapped_stat_func(data, axis=axis) + self.assertArrayEqual(result, expected) + + def test_3d_input_middle_single_stat_lazy(self): + # result shape should match non-aggregated input dims. Lazy data should + # be preserved. + data = np.empty((2, 3, 4)) + axis = 1 + self.stat_func.return_value = da.arange(8) + expected = np.arange(8).reshape(2, 4) + wrapped_stat_func = _axis_to_single_trailing(self.stat_func) + result = wrapped_stat_func(data, axis=axis) + self.assertTrue(is_lazy_data(result)) + self.assertArrayEqual(as_concrete_data(result), expected) + + def test_3d_input_middle_multiple_stat(self): + # result shape should match non-aggregated input dims, plus trailing dim + # with size determined by the stat function. + data = np.empty((2, 3, 4)) + axis = 1 + self.stat_func.return_value = np.arange(8 * 5).reshape(8, 5) + expected = np.arange(40).reshape(2, 4, 5) + wrapped_stat_func = _axis_to_single_trailing(self.stat_func) + result = wrapped_stat_func(data, axis=axis) + self.assertArrayEqual(result, expected) + + +if __name__ == "__main__": + tests.main() diff --git a/lib/iris/tests/unit/lazy_data/test_map_complete_blocks.py b/lib/iris/tests/unit/lazy_data/test_map_complete_blocks.py index e7f3adad76..66c03d04c8 100644 --- a/lib/iris/tests/unit/lazy_data/test_map_complete_blocks.py +++ b/lib/iris/tests/unit/lazy_data/test_map_complete_blocks.py @@ -25,6 +25,8 @@ def create_mock_cube(array): cube.has_lazy_data = unittest.mock.Mock(return_value=is_lazy_data(array)) cube.lazy_data = unittest.mock.Mock(return_value=array) cube.shape = array.shape + # Remove compute so cube is not interpreted as dask array. + del cube.compute return cube, cube_data @@ -58,6 +60,14 @@ def test_lazy_input(self): cube.lazy_data.assert_called_once() cube_data.assert_not_called() + def test_dask_array_input(self): + lazy_array = da.asarray(self.array, chunks=((1, 1), (4,))) + result = map_complete_blocks( + lazy_array, self.func, dims=(1,), out_sizes=(4,) + ) + self.assertTrue(is_lazy_data(result)) + self.assertArrayEqual(result.compute(), self.func_result) + def test_rechunk(self): lazy_array = da.asarray(self.array, chunks=((1, 1), (2, 2))) cube, _ = create_mock_cube(lazy_array) From 7be9dcad065a106e35995fb9e4baeebea81a55d4 Mon Sep 17 00:00:00 2001 From: Ruth Comer <10599679+rcomer@users.noreply.github.com> Date: Fri, 25 Mar 2022 12:19:49 +0000 Subject: [PATCH 03/15] Fix nearest_neighbour_index for edge case where requested point is float and bounds are int (#4245) * add failing test * pass test * add whatsnew --- docs/src/whatsnew/dev.rst | 4 ++++ lib/iris/coords.py | 4 +++- lib/iris/tests/unit/coords/test_Coord.py | 5 +++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/src/whatsnew/dev.rst b/docs/src/whatsnew/dev.rst index 9ed3cb23c2..0ef6c7f65e 100644 --- a/docs/src/whatsnew/dev.rst +++ b/docs/src/whatsnew/dev.rst @@ -54,6 +54,10 @@ This document explains the changes made to Iris for this release #. `@rcomer`_ ensured that a :class:`matplotlib.axes.Axes`'s position is preserved when Iris replaces it with a :class:`cartopy.mpl.geoaxes.GeoAxes`, fixing :issue:`1157`. (:pull:`4273`) + +#. `@rcomer`_ fixed :meth:`~iris.coords.Coord.nearest_neighbour_index` for edge + cases where the requested point is float and the coordinate has integer + bounds, reported at :issue:`2969`. (:pull:`4245`) 💣 Incompatible Changes diff --git a/lib/iris/coords.py b/lib/iris/coords.py index b236d407da..8bcba776fd 100644 --- a/lib/iris/coords.py +++ b/lib/iris/coords.py @@ -2440,7 +2440,9 @@ def nearest_neighbour_index(self, point): if self.has_bounds(): # make bounds ranges complete+separate, so point is in at least one increasing = self.bounds[0, 1] > self.bounds[0, 0] - bounds = bounds.copy() + # identify data type that bounds and point can safely cast to + dtype = np.result_type(bounds, point) + bounds = bounds.astype(dtype) # sort the bounds cells by their centre values sort_inds = np.argsort(np.mean(bounds, axis=1)) bounds = bounds[sort_inds] diff --git a/lib/iris/tests/unit/coords/test_Coord.py b/lib/iris/tests/unit/coords/test_Coord.py index 43170b6c4e..5f707f91db 100644 --- a/lib/iris/tests/unit/coords/test_Coord.py +++ b/lib/iris/tests/unit/coords/test_Coord.py @@ -74,6 +74,11 @@ def test_scalar(self): target = [0, 0, 0, 0, 0] self._test_nearest_neighbour_index(target) + def test_bounded_float_point(self): + coord = DimCoord(1, bounds=[0, 2]) + result = coord.nearest_neighbour_index(2.5) + self.assertEqual(result, 0) + class Test_nearest_neighbour_index__descending(tests.IrisTest): def setUp(self): From 57c0bff107921ea36bd564aaaa71c91ca36bafce Mon Sep 17 00:00:00 2001 From: Will Benfold <69585101+wjbenfold@users.noreply.github.com> Date: Tue, 29 Mar 2022 15:25:17 +0100 Subject: [PATCH 04/15] Include note to strip out Artifactory (#4666) * Include note to strip out Artifactory * What's new --- docs/src/developers_guide/contributing_ci_tests.rst | 7 +++++++ docs/src/whatsnew/dev.rst | 2 ++ 2 files changed, 9 insertions(+) diff --git a/docs/src/developers_guide/contributing_ci_tests.rst b/docs/src/developers_guide/contributing_ci_tests.rst index 46848166b3..87a3054605 100644 --- a/docs/src/developers_guide/contributing_ci_tests.rst +++ b/docs/src/developers_guide/contributing_ci_tests.rst @@ -68,6 +68,13 @@ or simply:: and add the changed lockfiles to your pull request. +.. note:: + + If your installation of conda runs through Artifactory or another similar + proxy then you will need to amend that lockfile to use URLs that Github + Actions can access. A utility to strip out Artifactory exists in the + ``ssstack`` tool. + New lockfiles are generated automatically each week to ensure that Iris continues to be tested against the latest available version of its dependencies. Each week the yaml files in ``requirements/ci`` are resolved by a GitHub Action. diff --git a/docs/src/whatsnew/dev.rst b/docs/src/whatsnew/dev.rst index 0ef6c7f65e..85b997125a 100644 --- a/docs/src/whatsnew/dev.rst +++ b/docs/src/whatsnew/dev.rst @@ -91,6 +91,8 @@ This document explains the changes made to Iris for this release #. `@tkknight`_ added a page to show the issues that have been voted for. See :ref:`voted_issues`. (:issue:`3307`, :pull:`4617`) +#. `@wjbenfold`_ added a note about fixing proxy URLs in lockfiles generated + because dependencies have changed. (:pull:`4666`) 💼 Internal From 62047f7fa9df9cf817067cb595127f0fc3c2b421 Mon Sep 17 00:00:00 2001 From: Ruth Comer <10599679+rcomer@users.noreply.github.com> Date: Tue, 29 Mar 2022 16:39:30 +0100 Subject: [PATCH 05/15] update black (#4668) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ee036038e4..8ce1a3cd28 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.1.0 + rev: 22.3.0 hooks: - id: black pass_filenames: false From e4246e1d1934f21e257258d868cc2936b465b0ce Mon Sep 17 00:00:00 2001 From: Patrick Peglar Date: Wed, 30 Mar 2022 18:07:20 +0100 Subject: [PATCH 06/15] Fixed cube arithmetic for cubes with meshes. (#4651) * Use separate cml files for tests/unit/analysis/maths/test_slice multiple passes. * Roughly functional, but lots of tests to fix. * Fix * Fix mockist test for new-style Resolve. Mesh support not yet tested. * Fix _create_prepared_item when points/bounds passed in. * --no-edit * Simplify prepared-item usage. * Added tests * Added whatsnew. * Reference Iris issues for outstanding problems. * Small simplifications. * Updated statement on cube arithmetic with meshes. * Fix use of 'MathsAddOperationMixin'; add coord-mismatch tests on meshcubes. * Review changes. * Hack test which was failing. --- docs/src/further_topics/ugrid/operations.rst | 21 +- docs/src/whatsnew/dev.rst | 4 + lib/iris/common/resolve.py | 231 ++++++-- .../collapse_all_dims.cml | 507 ++++++++++++++++++ .../collapse_last_dims.cml | 507 ++++++++++++++++++ .../collapse_middle_dim.cml | 507 ++++++++++++++++++ .../collapse_zeroth_dim.cml | 507 ++++++++++++++++++ .../TestBroadcastingDerived/slice.cml | 507 ++++++++++++++++++ .../TestBroadcastingDerived/transposed.cml | 507 ++++++++++++++++++ .../collapse_all_dims.cml | 122 +++++ .../collapse_last_dims.cml | 122 +++++ .../collapse_middle_dim.cml | 111 ++++ .../collapse_zeroth_dim.cml | 111 ++++ .../TestBroadcastingWithMesh/slice.cml | 111 ++++ .../TestBroadcastingWithMesh/transposed.cml | 111 ++++ .../collapse_all_dims.cml | 122 +++++ .../collapse_last_dims.cml | 122 +++++ .../collapse_middle_dim.cml | 111 ++++ .../collapse_zeroth_dim.cml | 111 ++++ .../slice.cml | 111 ++++ .../transposed.cml | 111 ++++ .../tests/unit/analysis/maths/__init__.py | 94 +++- .../maths/test__arith__derived_coords.py | 40 ++ .../analysis/maths/test__arith__meshcoords.py | 186 +++++++ .../tests/unit/common/resolve/test_Resolve.py | 67 ++- 25 files changed, 4979 insertions(+), 82 deletions(-) create mode 100644 lib/iris/tests/results/unit/analysis/maths/_arith__derived_coords/TestBroadcastingDerived/collapse_all_dims.cml create mode 100644 lib/iris/tests/results/unit/analysis/maths/_arith__derived_coords/TestBroadcastingDerived/collapse_last_dims.cml create mode 100644 lib/iris/tests/results/unit/analysis/maths/_arith__derived_coords/TestBroadcastingDerived/collapse_middle_dim.cml create mode 100644 lib/iris/tests/results/unit/analysis/maths/_arith__derived_coords/TestBroadcastingDerived/collapse_zeroth_dim.cml create mode 100644 lib/iris/tests/results/unit/analysis/maths/_arith__derived_coords/TestBroadcastingDerived/slice.cml create mode 100644 lib/iris/tests/results/unit/analysis/maths/_arith__derived_coords/TestBroadcastingDerived/transposed.cml create mode 100644 lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/collapse_all_dims.cml create mode 100644 lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/collapse_last_dims.cml create mode 100644 lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/collapse_middle_dim.cml create mode 100644 lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/collapse_zeroth_dim.cml create mode 100644 lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/slice.cml create mode 100644 lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/transposed.cml create mode 100644 lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/collapse_all_dims.cml create mode 100644 lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/collapse_last_dims.cml create mode 100644 lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/collapse_middle_dim.cml create mode 100644 lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/collapse_zeroth_dim.cml create mode 100644 lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/slice.cml create mode 100644 lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/transposed.cml create mode 100644 lib/iris/tests/unit/analysis/maths/test__arith__derived_coords.py create mode 100644 lib/iris/tests/unit/analysis/maths/test__arith__meshcoords.py diff --git a/docs/src/further_topics/ugrid/operations.rst b/docs/src/further_topics/ugrid/operations.rst index f96e3e406c..c636043640 100644 --- a/docs/src/further_topics/ugrid/operations.rst +++ b/docs/src/further_topics/ugrid/operations.rst @@ -976,13 +976,26 @@ on dimensions other than the :meth:`~iris.cube.Cube.mesh_dim`, since such Arithmetic ---------- -.. |tagline: arithmetic| replace:: |pending| +.. |tagline: arithmetic| replace:: |unchanged| .. rubric:: |tagline: arithmetic| -:class:`~iris.cube.Cube` Arithmetic (described in :doc:`/userguide/cube_maths`) -has not yet been adapted to handle :class:`~iris.cube.Cube`\s that include -:class:`~iris.experimental.ugrid.MeshCoord`\s. +Cube Arithmetic (described in :doc:`/userguide/cube_maths`) +has been extended to handle :class:`~iris.cube.Cube`\s that include +:class:`~iris.experimental.ugrid.MeshCoord`\s, and hence have a ``cube.mesh``. + +Cubes with meshes can be combined in arithmetic operations like +"ordinary" cubes. They can combine with other cubes without that mesh +(and its dimension); or with a matching mesh, which may be on a different +dimension. +Arithmetic can also be performed between a cube with a mesh and a mesh +coordinate with a matching mesh. + +In all cases, the result will have the same mesh as the input cubes. + +Meshes only match if they are fully equal -- i.e. they contain all the same +coordinates and connectivities, with identical names, units, attributes and +data content. .. todo: diff --git a/docs/src/whatsnew/dev.rst b/docs/src/whatsnew/dev.rst index 85b997125a..7d4a190ac9 100644 --- a/docs/src/whatsnew/dev.rst +++ b/docs/src/whatsnew/dev.rst @@ -37,6 +37,10 @@ This document explains the changes made to Iris for this release #. `@rcomer`_ implemented lazy aggregation for the :obj:`iris.analysis.PERCENTILE` aggregator. (:pull:`3901`) +#. `@pp-mo`_ fixed cube arithmetic operation for cubes with meshes. + (:issue:`4454`, :pull:`4651`) + + 🐛 Bugs Fixed ============= diff --git a/lib/iris/common/resolve.py b/lib/iris/common/resolve.py index 12db64cafe..a0c97dfc00 100644 --- a/lib/iris/common/resolve.py +++ b/lib/iris/common/resolve.py @@ -13,7 +13,9 @@ from collections import namedtuple from collections.abc import Iterable +from dataclasses import dataclass import logging +from typing import Any from dask.array.core import broadcast_shapes import numpy as np @@ -56,10 +58,42 @@ _PreparedFactory = namedtuple("PreparedFactory", ["container", "dependencies"]) -_PreparedItem = namedtuple( - "PreparedItem", - ["metadata", "points", "bounds", "dims", "container"], -) + +@dataclass +class _PreparedItem: + metadata: Any + points: Any + bounds: Any + dims: Any + container: Any + mesh: Any = None + location: Any = None + axis: Any = None + + def create_coord(self, metadata): + from iris.experimental.ugrid.mesh import MeshCoord + + if issubclass(self.container, MeshCoord): + # Make a MeshCoord, for which we have mesh/location/axis. + result = MeshCoord( + mesh=self.mesh, + location=self.location, + axis=self.axis, + ) + # Note: in this case we do also have "prepared metadata", but we + # do *not* assign it as we do for an 'ordinary' Coord. + # Instead, MeshCoord name/units/attributes are immutable, and set at + # create time to those of the underlying mesh node coordinate. + # cf https://github.com/SciTools/iris/issues/4670 + + else: + # make a regular coord, for which we have points/bounds/metadata. + result = self.container(self.points, bounds=self.bounds) + # Also assign prepared metadata. + result.metadata = metadata + + return result + _PreparedMetadata = namedtuple("PreparedMetadata", ["combined", "src", "tgt"]) @@ -646,7 +680,13 @@ def _categorise_items(cube): @staticmethod def _create_prepared_item( - coord, dims, src_metadata=None, tgt_metadata=None + coord, + dims, + src_metadata=None, + tgt_metadata=None, + points=None, + bounds=None, + container=None, ): """ Convenience method that creates a :class:`~iris.common.resolve._PreparedItem` @@ -658,8 +698,10 @@ def _create_prepared_item( * coord: The coordinate with the ``points`` and ``bounds`` to be extracted. - * dims: - The dimensions that the ``coord`` spans on the resulting resolved :class:`~iris.cube.Cube`. + * dims (int or tuple): + The dimensions that the ``coord`` spans on the resulting resolved + :class:`~iris.cube.Cube`. + (Can also be a single dimension number). * src_metadata: The coordinate metadata from the ``src`` :class:`~iris.cube.Cube`. @@ -667,26 +709,85 @@ def _create_prepared_item( * tgt_metadata: The coordinate metadata from the ``tgt`` :class:`~iris.cube.Cube`. + * points: + Override points array. When not given, use coord.points. + + * bounds: + Override bounds array. When not given, use coord.bounds. + + * container: + Override coord type (class constructor). + When not given, use type(coord). + Returns: The :class:`~iris.common.resolve._PreparedItem`. + .. note:: + + If container or type(coord) is DimCoord/AuxCoord (i.e. not + MeshCoord), then points+bounds define the built AuxCoord/DimCoord. + Theses points+bounds come either from those args, or the 'coord'. + Alternatively, when container or type(coord) is MeshCoord, then + points==bounds==None and the preparted item contains + mesh/location/axis properties for the resulting MeshCoord. + These don't have override args: they *always* come from 'coord'. + """ + if not isinstance(dims, Iterable): + dims = (dims,) + if src_metadata is not None and tgt_metadata is not None: combined = src_metadata.combine(tgt_metadata) else: combined = src_metadata or tgt_metadata - if not isinstance(dims, Iterable): - dims = (dims,) prepared_metadata = _PreparedMetadata( combined=combined, src=src_metadata, tgt=tgt_metadata ) - bounds = coord.bounds + + if container is None: + container = type(coord) + + from iris.experimental.ugrid.mesh import MeshCoord + + if issubclass(container, MeshCoord): + # Build a prepared-item to make a MeshCoord. + # This case does *NOT* use points + bounds, so alternatives to the + # coord content should not have been specified by the caller. + assert points is None and bounds is None + mesh = coord.mesh + location = coord.location + axis = coord.axis + + else: + # Build a prepared-item to make a DimCoord or AuxCoord. + + # mesh/location/axis are not used. + mesh = None + location = None + axis = None + + # points + bounds default to those from the coordinate, but + # alternative values may be specified. + if points is None: + points = coord.points + bounds = coord.bounds + # 'ELSE' points was passed: both points+bounds come from the args + + # Always *copy* points+bounds, to avoid any possible direct (shared) + # references to existing coord arrays. + points = points.copy() + if bounds is not None: + bounds = bounds.copy() + result = _PreparedItem( metadata=prepared_metadata, - points=coord.points.copy(), - bounds=bounds if bounds is None else bounds.copy(), dims=dims, - container=type(coord), + points=points, + bounds=bounds, + mesh=mesh, + location=location, + axis=axis, + container=container, ) return result @@ -1422,30 +1523,64 @@ def _prepare_common_aux_payload( (tgt_item,) = tgt_items src_coord = src_item.coord tgt_coord = tgt_item.coord - points, bounds = self._prepare_points_and_bounds( - src_coord, - tgt_coord, - src_item.dims, - tgt_item.dims, - ignore_mismatch=ignore_mismatch, - ) - if points is not None: - src_type = type(src_coord) - tgt_type = type(tgt_coord) - # Downcast to aux if there are mixed container types. - container = src_type if src_type is tgt_type else AuxCoord - prepared_metadata = _PreparedMetadata( - combined=src_metadata.combine(tgt_item.metadata), - src=src_metadata, - tgt=tgt_item.metadata, - ) - prepared_item = _PreparedItem( - metadata=prepared_metadata, - points=points.copy(), - bounds=bounds if bounds is None else bounds.copy(), - dims=tgt_item.dims, - container=container, + + prepared_item = None + src_is_mesh, tgt_is_mesh = [ + hasattr(coord, "mesh") for coord in (src_coord, tgt_coord) + ] + if src_is_mesh and tgt_is_mesh: + # MeshCoords are a bit "special" ... + # In this case, we may need to produce an alternative form + # to the 'ordinary' _PreparedItem + # However, this only works if they have identical meshes.. + if src_coord == tgt_coord: + prepared_item = self._create_prepared_item( + src_coord, + tgt_item.dims, + src_metadata=src_metadata, + tgt_metadata=tgt_item.metadata, + ) + else: + emsg = ( + f"Mesh coordinate {src_coord.name()!r} does not match between the " + f"LHS cube {self.lhs_cube.name()!r} and " + f"RHS cube {self.rhs_cube.name()!r}." + ) + raise ValueError(emsg) + + if prepared_item is None: + # Make a "normal" _PreparedItem, which is specified using + # points + bounds arrays. + # First, convert any un-matching MeshCoords to AuxCoord + if src_is_mesh: + src_coord = AuxCoord.from_coord(src_coord) + if tgt_is_mesh: + tgt_coord = AuxCoord.from_coord(tgt_coord) + points, bounds = self._prepare_points_and_bounds( + src_coord, + tgt_coord, + src_item.dims, + tgt_item.dims, + ignore_mismatch=ignore_mismatch, ) + if points is not None: + src_type = type(src_coord) + tgt_type = type(tgt_coord) + # Downcast to aux if there are mixed container types. + container = ( + src_type if src_type is tgt_type else AuxCoord + ) + prepared_item = self._create_prepared_item( + src_coord, + tgt_item.dims, + src_metadata=src_metadata, + tgt_metadata=tgt_item.metadata, + points=points, + bounds=bounds, + container=container, + ) + + if prepared_item is not None: prepared_items.append(prepared_item) def _prepare_common_dim_payload( @@ -1499,16 +1634,13 @@ def _prepare_common_dim_payload( ) if points is not None: - prepared_metadata = _PreparedMetadata( - combined=src_metadata.combine(tgt_metadata), - src=src_metadata, - tgt=tgt_metadata, - ) - prepared_item = _PreparedItem( - metadata=prepared_metadata, - points=points.copy(), - bounds=bounds if bounds is None else bounds.copy(), - dims=(tgt_dim,), + prepared_item = self._create_prepared_item( + src_coord, + tgt_dim, + src_metadata=src_metadata, + tgt_metadata=tgt_metadata, + points=points, + bounds=bounds, container=DimCoord, ) self.prepared_category.items_dim.append(prepared_item) @@ -2333,8 +2465,7 @@ def cube(self, data, in_place=False): # Add the prepared dim coordinates. for item in self.prepared_category.items_dim: - coord = item.container(item.points, bounds=item.bounds) - coord.metadata = item.metadata.combined + coord = item.create_coord(metadata=item.metadata.combined) result.add_dim_coord(coord, item.dims) # Add the prepared aux and scalar coordinates. @@ -2343,8 +2474,8 @@ def cube(self, data, in_place=False): + self.prepared_category.items_scalar ) for item in prepared_aux_coords: - coord = item.container(item.points, bounds=item.bounds) - coord.metadata = item.metadata.combined + # These items are "special" + coord = item.create_coord(metadata=item.metadata.combined) try: result.add_aux_coord(coord, item.dims) except ValueError as err: diff --git a/lib/iris/tests/results/unit/analysis/maths/_arith__derived_coords/TestBroadcastingDerived/collapse_all_dims.cml b/lib/iris/tests/results/unit/analysis/maths/_arith__derived_coords/TestBroadcastingDerived/collapse_all_dims.cml new file mode 100644 index 0000000000..9a522e5167 --- /dev/null +++ b/lib/iris/tests/results/unit/analysis/maths/_arith__derived_coords/TestBroadcastingDerived/collapse_all_dims.cml @@ -0,0 +1,507 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/iris/tests/results/unit/analysis/maths/_arith__derived_coords/TestBroadcastingDerived/collapse_last_dims.cml b/lib/iris/tests/results/unit/analysis/maths/_arith__derived_coords/TestBroadcastingDerived/collapse_last_dims.cml new file mode 100644 index 0000000000..9a522e5167 --- /dev/null +++ b/lib/iris/tests/results/unit/analysis/maths/_arith__derived_coords/TestBroadcastingDerived/collapse_last_dims.cml @@ -0,0 +1,507 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/iris/tests/results/unit/analysis/maths/_arith__derived_coords/TestBroadcastingDerived/collapse_middle_dim.cml b/lib/iris/tests/results/unit/analysis/maths/_arith__derived_coords/TestBroadcastingDerived/collapse_middle_dim.cml new file mode 100644 index 0000000000..9a522e5167 --- /dev/null +++ b/lib/iris/tests/results/unit/analysis/maths/_arith__derived_coords/TestBroadcastingDerived/collapse_middle_dim.cml @@ -0,0 +1,507 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/iris/tests/results/unit/analysis/maths/_arith__derived_coords/TestBroadcastingDerived/collapse_zeroth_dim.cml b/lib/iris/tests/results/unit/analysis/maths/_arith__derived_coords/TestBroadcastingDerived/collapse_zeroth_dim.cml new file mode 100644 index 0000000000..9a522e5167 --- /dev/null +++ b/lib/iris/tests/results/unit/analysis/maths/_arith__derived_coords/TestBroadcastingDerived/collapse_zeroth_dim.cml @@ -0,0 +1,507 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/iris/tests/results/unit/analysis/maths/_arith__derived_coords/TestBroadcastingDerived/slice.cml b/lib/iris/tests/results/unit/analysis/maths/_arith__derived_coords/TestBroadcastingDerived/slice.cml new file mode 100644 index 0000000000..9a522e5167 --- /dev/null +++ b/lib/iris/tests/results/unit/analysis/maths/_arith__derived_coords/TestBroadcastingDerived/slice.cml @@ -0,0 +1,507 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/iris/tests/results/unit/analysis/maths/_arith__derived_coords/TestBroadcastingDerived/transposed.cml b/lib/iris/tests/results/unit/analysis/maths/_arith__derived_coords/TestBroadcastingDerived/transposed.cml new file mode 100644 index 0000000000..9a522e5167 --- /dev/null +++ b/lib/iris/tests/results/unit/analysis/maths/_arith__derived_coords/TestBroadcastingDerived/transposed.cml @@ -0,0 +1,507 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 0000000000..2db0cc598f --- /dev/null +++ b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/collapse_all_dims.cml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 0000000000..2db0cc598f --- /dev/null +++ b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/collapse_last_dims.cml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 0000000000..359656f25a --- /dev/null +++ b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/collapse_middle_dim.cml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 0000000000..359656f25a --- /dev/null +++ b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/collapse_zeroth_dim.cml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 0000000000..359656f25a --- /dev/null +++ b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/slice.cml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 0000000000..359656f25a --- /dev/null +++ b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMesh/transposed.cml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 0000000000..2db0cc598f --- /dev/null +++ b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/collapse_all_dims.cml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 0000000000..2db0cc598f --- /dev/null +++ b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/collapse_last_dims.cml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 0000000000..359656f25a --- /dev/null +++ b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/collapse_middle_dim.cml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 0000000000..359656f25a --- /dev/null +++ b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/collapse_zeroth_dim.cml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 0000000000..359656f25a --- /dev/null +++ b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/slice.cml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 0000000000..359656f25a --- /dev/null +++ b/lib/iris/tests/results/unit/analysis/maths/_arith__meshcoords/TestBroadcastingWithMeshAndDerived/transposed.cml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/iris/tests/unit/analysis/maths/__init__.py b/lib/iris/tests/unit/analysis/maths/__init__.py index 7d11c54660..521c65a7eb 100644 --- a/lib/iris/tests/unit/analysis/maths/__init__.py +++ b/lib/iris/tests/unit/analysis/maths/__init__.py @@ -10,11 +10,13 @@ import iris.tests as tests # isort:skip from abc import ABCMeta, abstractmethod +import operator import numpy as np from numpy import ma from iris.analysis import MEAN +from iris.analysis.maths import add from iris.coords import DimCoord from iris.cube import Cube import iris.tests.stock as stock @@ -36,8 +38,46 @@ def cube_func(self): # I.E. 'iris.analysis.maths.xx'. pass + def _base_testcube(self, include_derived=False): + if include_derived: + self.cube = stock.realistic_4d() + else: + self.cube = stock.realistic_4d_no_derived() + self.cube_xy_dimcoords = ["grid_latitude", "grid_longitude"] + return self.cube + + def _meshcube_collapsesafe(self, cube, coords): + # Return the cube, or if need be a modified copy, which can be safely + # collapsed over the given coords. + # This is needed for mesh-cubes, because the mesh coords have + # bounds which are not understood by the standard 'collapse' operation. + # TODO: possibly replace with a future 'safe mesh collapse' operation. + # cf. https://github.com/SciTools/iris/issues/4672 + result = cube + if cube.mesh is not None: + collapse_dims = set() + for co in coords: + # Each must produce a single coord, with a single dim + (dim,) = cube.coord_dims(co) + collapse_dims.add(dim) + i_meshdim = cube.mesh_dim() + if i_meshdim in collapse_dims: + # Make a copy with all mesh coords replaced by their AuxCoord + # equivalents. A simple slicing will do that. + slices = [slice(None)] * cube.ndim + slices[i_meshdim] = slice(0, None) + result = cube[tuple(slices)] + # Finally, **remove bounds** from all the former AuxCoords. + # This is what enables them to be successfully collapsed. + for meshco in cube.coords(mesh_coords=True): + # Note: select new coord by name, as getting the AuxCoord + # which "matches" a MeshCoord is not possible. + result.coord(meshco.name()).bounds = None + + return result + def test_transposed(self): - cube = stock.realistic_4d_no_derived() + cube = self._base_testcube() other = cube.copy() other.transpose() res = self.cube_func(cube, other) @@ -46,7 +86,7 @@ def test_transposed(self): self.assertArrayEqual(res.data, expected_data) def test_collapse_zeroth_dim(self): - cube = stock.realistic_4d_no_derived() + cube = self._base_testcube() other = cube.collapsed("time", MEAN) res = self.cube_func(cube, other) self.assertCML(res, checksum=False) @@ -58,8 +98,10 @@ def test_collapse_zeroth_dim(self): self.assertMaskedArrayEqual(res.data, expected_data) def test_collapse_all_dims(self): - cube = stock.realistic_4d_no_derived() - other = cube.collapsed(cube.coords(dim_coords=True), MEAN) + cube = self._base_testcube() + collapse_coords = cube.coords(dim_coords=True) + other = self._meshcube_collapsesafe(cube, collapse_coords) + other = other.collapsed(collapse_coords, MEAN) res = self.cube_func(cube, other) self.assertCML(res, checksum=False) # No modification to other.data is needed as numpy broadcasting @@ -70,21 +112,28 @@ def test_collapse_all_dims(self): self.assertArrayEqual(res.data, expected_data) def test_collapse_last_dims(self): - cube = stock.realistic_4d_no_derived() - other = cube.collapsed(["grid_latitude", "grid_longitude"], MEAN) + cube = self._base_testcube() + # Collapse : by 'last' we mean the X+Y ones... + other = self._meshcube_collapsesafe(cube, self.cube_xy_dimcoords) + other = other.collapsed(self.cube_xy_dimcoords, MEAN) res = self.cube_func(cube, other) self.assertCML(res, checksum=False) # Transpose the dimensions in self.cube that have been collapsed in # other to lie at the front, thereby enabling numpy broadcasting to # function when applying data operator. Finish by transposing back # again to restore order. + n_xydims = len(self.cube_xy_dimcoords) + cube_dims = tuple(np.arange(cube.ndim)) + transpose_xy_back2front = cube_dims[-n_xydims:] + cube_dims[:-n_xydims] + transpose_xy_front2back = cube_dims[n_xydims:] + cube_dims[:n_xydims] expected_data = self.data_op( - cube.data.transpose((2, 3, 0, 1)), other.data - ).transpose(2, 3, 0, 1) + cube.data.transpose(transpose_xy_back2front), other.data + ).transpose(transpose_xy_front2back) + # Confirm result content is as expected self.assertMaskedArrayEqual(res.data, expected_data) def test_collapse_middle_dim(self): - cube = stock.realistic_4d_no_derived() + cube = self._base_testcube() other = cube.collapsed(["model_level_number"], MEAN) res = self.cube_func(cube, other) self.assertCML(res, checksum=False) @@ -94,12 +143,26 @@ def test_collapse_middle_dim(self): self.assertMaskedArrayEqual(res.data, expected_data) def test_slice(self): - cube = stock.realistic_4d_no_derived() + cube = self._base_testcube() for dim in range(cube.ndim): keys = [slice(None)] * cube.ndim keys[dim] = 3 other = cube[tuple(keys)] + + # A special "cheat" for mesh cases... + # When a mesh dimension is indexed, this produces scalar versions + # of the mesh-coords, which don't match to the originals. + # FOR NOW: remove those, for a result matching the other ones. + # TODO: coord equivalence may need reviewing, either for cube + # maths or for coord equivalance generally. + # cf. https://github.com/SciTools/iris/issues/4671 + if cube.mesh and dim == cube.mesh_dim(): + for co in cube.coords(mesh_coords=True): + other.remove_coord(co.name()) + res = self.cube_func(cube, other) + + # NOTE: only one testfile : any dim collapsed gives SAME result self.assertCML(res, checksum=False) # Add the collapsed dimension back in via np.newaxis to enable # numpy broadcasting to function. @@ -111,6 +174,17 @@ def test_slice(self): ) +class MathsAddOperationMixin: + # Test everything with the 'add' operation. + @property + def data_op(self): + return operator.add + + @property + def cube_func(self): + return add + + class CubeArithmeticMaskingTestMixin(metaclass=ABCMeta): # A framework for testing the mask handling behaviour of the various cube # arithmetic operations. (A test for each operation inherits this). diff --git a/lib/iris/tests/unit/analysis/maths/test__arith__derived_coords.py b/lib/iris/tests/unit/analysis/maths/test__arith__derived_coords.py new file mode 100644 index 0000000000..51f71affb0 --- /dev/null +++ b/lib/iris/tests/unit/analysis/maths/test__arith__derived_coords.py @@ -0,0 +1,40 @@ +# 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 cube arithmetic involving derived (i.e. factory) coords.""" + +# Import iris.tests first so that some things can be initialised before +# importing anything else. +import iris.tests as tests # isort:skip + +from iris.tests.unit.analysis.maths import ( + CubeArithmeticBroadcastingTestMixin, + MathsAddOperationMixin, +) + + +@tests.skip_data +@tests.iristest_timing_decorator +class TestBroadcastingDerived( + tests.IrisTest_nometa, + MathsAddOperationMixin, + CubeArithmeticBroadcastingTestMixin, +): + """ + Repeat the broadcasting tests while retaining derived coordinates. + + NOTE: apart from showing that these operations do succeed, this mostly + produces a new set of CML result files, + in "lib/iris/tests/results/unit/analysis/maths/_arith__derived_coords" . + See there to confirm that the results preserve the derived coordinates. + + """ + + def _base_testcube(self): + return super()._base_testcube(include_derived=True) + + +if __name__ == "__main__": + tests.main() diff --git a/lib/iris/tests/unit/analysis/maths/test__arith__meshcoords.py b/lib/iris/tests/unit/analysis/maths/test__arith__meshcoords.py new file mode 100644 index 0000000000..1d81e7b480 --- /dev/null +++ b/lib/iris/tests/unit/analysis/maths/test__arith__meshcoords.py @@ -0,0 +1,186 @@ +# 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 cube arithmetic involving MeshCoords.""" + +# Import iris.tests first so that some things can be initialised before +# importing anything else. +import iris.tests as tests # isort:skip + +import numpy as np + +from iris.analysis.maths import add +from iris.coords import AuxCoord, DimCoord +from iris.tests.stock.mesh import sample_mesh, sample_mesh_cube +from iris.tests.unit.analysis.maths import ( + CubeArithmeticBroadcastingTestMixin, + CubeArithmeticCoordsTest, + MathsAddOperationMixin, +) + + +def _convert_to_meshcube(cube): + """Convert a cube based on stock.realistic_4d into a "meshcube".""" + # Replace lat+lon with a small mesh + cube = cube[..., -1] # remove final (X) dim + for name in ("grid_longitude", "grid_latitude"): + cube.remove_coord(name) + i_meshdim = len(cube.shape) - 1 + n_meshpoints = cube.shape[i_meshdim] + mesh = sample_mesh(n_nodes=n_meshpoints, n_faces=n_meshpoints, n_edges=0) + for co in mesh.to_MeshCoords(location="face"): + cube.add_aux_coord(co, i_meshdim) + # also add a dim-coord for the mesh dim, mainly so that + # the 'xxBroadcastingxx.test_collapse_all_dims' tests can do what they say. + mesh_dimcoord = DimCoord(np.arange(n_meshpoints), long_name="i_mesh_face") + cube.add_dim_coord(mesh_dimcoord, i_meshdim) + return cube + + +class MeshLocationsMixin: + # Control allowing us to also include test with derived coordinates. + use_derived_coords = False + + # Modify the inherited data operation, to test with a mesh-cube. + # Also, optionally, test with derived coordinates. + def _base_testcube(self): + cube = super()._base_testcube(include_derived=self.use_derived_coords) + cube = _convert_to_meshcube(cube) + self.cube_xy_dimcoords = ["i_mesh_face"] + self.cube = cube + return self.cube + + +@tests.skip_data +@tests.iristest_timing_decorator +class TestBroadcastingWithMesh( + tests.IrisTest_nometa, + MeshLocationsMixin, + MathsAddOperationMixin, + CubeArithmeticBroadcastingTestMixin, +): + """ + Run all the broadcasting tests on cubes with meshes. + + NOTE: there is a fair amount of special-case code to support this, built + into the CubeArithmeticBroadcastingTestMixin baseclass. + + """ + + +@tests.skip_data +@tests.iristest_timing_decorator +class TestBroadcastingWithMeshAndDerived( + tests.IrisTest_nometa, + MeshLocationsMixin, + MathsAddOperationMixin, + CubeArithmeticBroadcastingTestMixin, +): + """Run broadcasting tests with meshes *and* derived coords.""" + + use_derived = True + + +class TestCoordMatchWithMesh(CubeArithmeticCoordsTest): + """Run the coordinate-mismatch tests with meshcubes.""" + + def _convert_to_meshcubes(self, cubes, i_dim): + """Add a mesh to one dim of the 'normal case' test-cubes.""" + for cube in cubes: + n_size = cube.shape[i_dim] + mesh = sample_mesh(n_nodes=n_size, n_faces=n_size, n_edges=0) + for co in mesh.to_MeshCoords("face"): + cube.add_aux_coord(co, i_dim) + assert cube.mesh is not None + + def _check_no_match(self, dim): + # Duplicate the basic operation, but convert cubes to meshcubes. + cube1, cube2 = self.SetUpNonMatching() + self._convert_to_meshcubes([cube1, cube2], dim) + with self.assertRaises(ValueError): + add(cube1, cube2) + + def test_no_match_dim0(self): + self._check_no_match(0) + + def test_no_match_dim1(self): + self._check_no_match(1) + + def _check_reversed_points(self, dim): + # Duplicate the basic operation, but convert cubes to meshcubes. + cube1, cube2 = self.SetUpReversed() + self._convert_to_meshcubes([cube1, cube2], dim) + with self.assertRaises(ValueError): + add(cube1, cube2) + + def test_reversed_points_dim0(self): + self._check_reversed_points(0) + + def test_reversed_points_dim1(self): + self._check_reversed_points(1) + + +class TestBasicMeshOperation(tests.IrisTest): + """Some very basic standalone tests, in an easier-to-comprehend form.""" + + def test_meshcube_same_mesh(self): + # Two similar cubes on a common mesh add to a third on the same mesh. + mesh = sample_mesh() + cube1 = sample_mesh_cube(mesh=mesh) + cube2 = sample_mesh_cube(mesh=mesh) + self.assertIs(cube1.mesh, mesh) + self.assertIs(cube2.mesh, mesh) + + result = cube1 + cube2 + self.assertEqual(result.shape, cube1.shape) + self.assertIs(result.mesh, mesh) + + def test_meshcube_different_equal_mesh(self): + # Two similar cubes on identical but different meshes. + cube1 = sample_mesh_cube() + cube2 = sample_mesh_cube() + self.assertEqual(cube1.mesh, cube2.mesh) + self.assertIsNot(cube1.mesh, cube2.mesh) + + result = cube1 + cube2 + self.assertEqual(result.shape, cube1.shape) + self.assertEqual(result.mesh, cube1.mesh) + self.assertTrue(result.mesh is cube1.mesh or result.mesh is cube2.mesh) + + def test_fail_meshcube_nonequal_mesh(self): + # Cubes on similar but different meshes -- should *not* combine. + mesh1 = sample_mesh() + mesh2 = sample_mesh(n_edges=0) + self.assertNotEqual(mesh1, mesh2) + cube1 = sample_mesh_cube(mesh=mesh1) + cube2 = sample_mesh_cube(mesh=mesh2) + + msg = "Mesh coordinate.* does not match" + with self.assertRaisesRegex(ValueError, msg): + cube1 + cube2 + + def test_meshcube_meshcoord(self): + # Combining a meshcube and meshcoord. + cube = sample_mesh_cube() + cube.coord("latitude").units = "s" + cube.units = "m" + + # A separately derived, but matching 'latitude' MeshCoord. + coord = sample_mesh_cube().coord("latitude") + coord.units = "s" # N.B. the units **must also match** + + result = cube / coord + self.assertEqual(result.name(), "unknown") + self.assertEqual(result.units, "m s-1") + + # Moreover : *cannot* do this with the 'equivalent' AuxCoord + # cf. https://github.com/SciTools/iris/issues/4671 + coord = AuxCoord.from_coord(coord) + with self.assertRaises(ValueError): + cube / coord + + +if __name__ == "__main__": + tests.main() diff --git a/lib/iris/tests/unit/common/resolve/test_Resolve.py b/lib/iris/tests/unit/common/resolve/test_Resolve.py index 98643c8f10..840f65db01 100644 --- a/lib/iris/tests/unit/common/resolve/test_Resolve.py +++ b/lib/iris/tests/unit/common/resolve/test_Resolve.py @@ -15,7 +15,7 @@ from collections import namedtuple from copy import deepcopy import unittest.mock as mock -from unittest.mock import sentinel +from unittest.mock import Mock, sentinel from cf_units import Unit import numpy as np @@ -2086,8 +2086,13 @@ def setUp(self): # # src-to-tgt mapping: # 0->1, 1->2, 2->3 - self.points = (sentinel.points_0, sentinel.points_1, sentinel.points_2) - self.bounds = (sentinel.bounds_0, sentinel.bounds_1, sentinel.bounds_2) + self.points = ( + sentinel.points_0, + sentinel.points_1, + sentinel.points_2, + sentinel.points_3, + ) + self.bounds = sentinel.bounds_0, sentinel.bounds_1, sentinel.bounds_2 self.pb_0 = ( mock.Mock(copy=mock.Mock(return_value=self.points[0])), mock.Mock(copy=mock.Mock(return_value=self.bounds[0])), @@ -2121,9 +2126,13 @@ def setUp(self): ) metadata = [self.src_metadata] * len(self.mapping) self.src_coords = [ - sentinel.src_coord_0, - sentinel.src_coord_1, - sentinel.src_coord_2, + # N.B. these need to mimic a Coord with points and bounds, and + # be of a class which is not-a-MeshCoord. + # NOTE: strictly, bounds should =above values, and support .copy(). + # For these tests, just omitting them works + is simpler. + Mock(spec=DimCoord, points=self.points[0], bounds=None), + Mock(spec=DimCoord, points=self.points[1], bounds=None), + Mock(spec=DimCoord, points=self.points[2], bounds=None), ] self.src_dims_common = [0, 1, 2] self.container = DimCoord @@ -2142,10 +2151,14 @@ def setUp(self): sentinel.tgt_metadata_3, ] self.tgt_coords = [ - sentinel.tgt_coord_0, - sentinel.tgt_coord_1, - sentinel.tgt_coord_2, - sentinel.tgt_coord_3, + # N.B. these need to mimic a Coord with points and bounds, and + # be of a class which is not-a-MeshCoord. + # NOTE: strictly, bounds should =above values, and support .copy(). + # For these tests, just omitting them works + is simpler. + Mock(spec=DimCoord, points=self.points[0], bounds=None), + Mock(spec=DimCoord, points=self.points[1], bounds=None), + Mock(spec=DimCoord, points=self.points[2], bounds=None), + Mock(spec=DimCoord, points=self.points[3], bounds=None), ] self.tgt_dims_common = [1, 2, 3] self.tgt_dim_coverage = _DimCoverage( @@ -2275,7 +2288,12 @@ def setUp(self): # # src-to-tgt mapping: # 0->1, 1->2, 2->3 - self.points = (sentinel.points_0, sentinel.points_1, sentinel.points_2) + self.points = ( + sentinel.points_0, + sentinel.points_1, + sentinel.points_2, + sentinel.points_3, + ) self.bounds = (sentinel.bounds_0, sentinel.bounds_1, sentinel.bounds_2) self.pb_0 = ( mock.Mock(copy=mock.Mock(return_value=self.points[0])), @@ -2318,9 +2336,13 @@ def setUp(self): ), ] self.src_coords = [ - sentinel.src_coord_0, - sentinel.src_coord_1, - sentinel.src_coord_2, + # N.B. these need to mimic a Coord with points and bounds, but also + # the type() defines the 'container' property of a prepared item. + # It seems that 'type()' is not fake-able in Python, so we need to + # provide *real* DimCoords, to match "self.container" below. + DimCoord(points=[0], bounds=None), + DimCoord(points=[1], bounds=None), + DimCoord(points=[2], bounds=None), ] self.src_dims = [(dim,) for dim in self.mapping.keys()] self.src_common_items = [ @@ -2329,10 +2351,14 @@ def setUp(self): ] self.tgt_metadata = [sentinel.tgt_metadata_0] + self.src_metadata self.tgt_coords = [ - sentinel.tgt_coord_0, - sentinel.tgt_coord_1, - sentinel.tgt_coord_2, - sentinel.tgt_coord_3, + # N.B. these need to mimic a Coord with points and bounds, but also + # the type() defines the 'container' property of a prepared item. + # It seems that 'type()' is not fake-able in Python, so we need to + # provide *real* DimCoords, to match "self.container" below. + DimCoord(points=[0], bounds=None), + DimCoord(points=[1], bounds=None), + DimCoord(points=[2], bounds=None), + DimCoord(points=[3], bounds=None), ] self.tgt_dims = [None] + [(dim,) for dim in self.mapping.values()] self.tgt_common_items = [ @@ -4624,6 +4650,11 @@ def setUp(self): self.resolve.prepared_category = prepared_category self.resolve.prepared_factories = prepared_factories + # Required to stop mock 'containers' failing in an 'issubclass' call. + self.patch( + "iris.common.resolve.issubclass", mock.Mock(return_value=False) + ) + def test_no_resolved_shape(self): self.resolve._broadcast_shape = None data = None From c9972cefb7c1e2e2db5c874af0ce2dae958858ae Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 31 Mar 2022 09:36:25 +0100 Subject: [PATCH 07/15] Updated environment lockfiles (#4655) Co-authored-by: Lockfile bot --- requirements/ci/nox.lock/py38-linux-64.lock | 396 ++++++++++---------- 1 file changed, 198 insertions(+), 198 deletions(-) diff --git a/requirements/ci/nox.lock/py38-linux-64.lock b/requirements/ci/nox.lock/py38-linux-64.lock index a612138dfa..ae6c224e8a 100644 --- a/requirements/ci/nox.lock/py38-linux-64.lock +++ b/requirements/ci/nox.lock/py38-linux-64.lock @@ -1,225 +1,225 @@ # Generated by conda-lock. # platform: linux-64 -# input_hash: fc890d56b881193a2422ceb96d07b1b2bb857890e1d48fb24a765ec2f886d4d2 +# input_hash: d7bddc89ba289d4c1b48871b1289eb0bf45715ef2e274de01e245547fb6a8df6 @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-2021.10.8-ha878542_0.tar.bz2#575611b8a84f45960e87722eeb51fa26 -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/sphinxcontrib-qthelp-1.0.3-py_0.tar.bz2#d01180388e6d1838c3e1ad029590aa7a +https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-applehelp-1.0.2-py_0.tar.bz2#20b2eaeaeea4ef9a9a0d99770620fd09 +https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.0.1-py38h1fd1430_1.tar.bz2#c494f75082f9c052944fda1b22c83336 +https://conda.anaconda.org/conda-forge/noarch/fsspec-2022.2.0-pyhd8ed1ab_0.tar.bz2#f31e31092035d427b05233ab924c7613 +https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-13_linux64_openblas.tar.bz2#018b80e8f21d8560ae4961567e3e00c9 +https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.47.0-h727a467_0.tar.bz2#a22567abfea169ff8048506b1ca9b230 +https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.3.0-h542a066_3.tar.bz2#1a0efb4dfd880b0376da8e1ba39fa838 +https://conda.anaconda.org/conda-forge/linux-64/gettext-0.19.8.1-h73d1719_1008.tar.bz2#af49250eca8e139378f8ff0ae9e57251 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/linux-64/expat-2.4.7-h27087fc_0.tar.bz2#b7fcfe2c2e21e5b696a98d774ac3f701 +https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.4-pyh9f0ad1d_0.tar.bz2#c08b4c1326b880ed44f3ffb04803332f +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/brotli-1.0.9-h7f98852_6.tar.bz2#612385c4a83edb0619fe911d9da317f4 +https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.0.3-he3ba5ed_0.tar.bz2#f9dbabc7e01c459ed7a1d1d64b206e9b +https://conda.anaconda.org/conda-forge/noarch/imagehash-4.2.1-pyhd8ed1ab_0.tar.bz2#01cc8698b6e1a124dc4f585516c27643 +https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.1-py38h0a891b7_0.tar.bz2#0d1a1c4a830d2bc306af194f4e4896e7 +https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81 +https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-jsmath-1.0.1-py_0.tar.bz2#67cd9d9c0382d37479b4d306c369a2d4 +https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-13_linux64_openblas.tar.bz2#8a4038563ed92dfa622bd72c0d8f31d3 https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-hab24e00_0.tar.bz2#19410c3df09dfb12d1206132a1d357c5 +https://conda.anaconda.org/conda-forge/noarch/identify-2.4.12-pyhd8ed1ab_0.tar.bz2#9f7b07b3f40905fcf600aacf63ae0c41 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-11.2.0-h5c6108e_13.tar.bz2#b62e87134ec17e1180cfcb3951624db4 -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-11.2.0-he4da1e4_13.tar.bz2#573a74710fad22a27da784cc238150b9 -https://conda.anaconda.org/conda-forge/linux-64/mpi-1.0-mpich.tar.bz2#c1fcff3417b5a22bbc4cf6e8c23648cf -https://conda.anaconda.org/conda-forge/linux-64/mysql-common-8.0.28-ha770c72_0.tar.bz2#56594fdd5a80774a80d546fbbccf2c03 -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-11.2.0-h69a702a_13.tar.bz2#a3a07a89af69d1eada078695b42e4961 -https://conda.anaconda.org/conda-forge/linux-64/libgomp-11.2.0-h1d223b6_13.tar.bz2#8e91f1f21417c9ab1265240ee4f9db1e -https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-1_gnu.tar.bz2#561e277319a41d4f24f5c05a9ef63c04 -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-11.2.0-h1d223b6_13.tar.bz2#63eaf0f146cc80abd84743d48d667da4 -https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.3-h516909a_0.tar.bz2#1378b88874f42ac31b2f8e4f6975cb7b -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.6-h27087fc_0.tar.bz2#90dec9e76bc164857cc200f81e981dab -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.10.2-h9c3ff4c_0.tar.bz2#fe9a66a351bfa7a84c3108304c7bcba5 -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-69.1-h9c3ff4c_0.tar.bz2#e0773c9556d588b062a4e1424a6a02fa -https://conda.anaconda.org/conda-forge/linux-64/jbig-2.1-h7f98852_2003.tar.bz2#1aa0cee79792fa97b7ff4545110b60bf -https://conda.anaconda.org/conda-forge/linux-64/jpeg-9e-h7f98852_0.tar.bz2#5c214edc675a7fb7cbb34b1d854e5141 -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/lerc-3.0-h9c3ff4c_0.tar.bz2#7fcefde484980d23f0ec24c11e314d2e -https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.0.9-h7f98852_6.tar.bz2#b0f44f63f7d771d7670747a1dd5d5ac1 +https://conda.anaconda.org/conda-forge/linux-64/graphviz-3.0.0-h5abf519_1.tar.bz2#fcaf13b2713335ff871ba551d5bda679 +https://conda.anaconda.org/conda-forge/linux-64/libgomp-11.2.0-h1d223b6_14.tar.bz2#a77fb1a92411cb8d979de1c2d81dd210 +https://conda.anaconda.org/conda-forge/linux-64/xorg-renderproto-0.11.1-h7f98852_1002.tar.bz2#06feff3d2634e3097ce2fe681474b534 +https://conda.anaconda.org/conda-forge/linux-64/importlib-metadata-4.11.3-py38h578d9bd_0.tar.bz2#00bdc412edc320d1717d1f7156d8edfd +https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.37.1-h4ff8645_0.tar.bz2#8057ac02d6d10a162d7eb4b0ca7ed291 +https://conda.anaconda.org/conda-forge/linux-64/qt-5.12.9-h1304e3e_6.tar.bz2#f2985d160b8c43dd427923c04cd732fe +https://conda.anaconda.org/conda-forge/noarch/pip-22.0.4-pyhd8ed1ab_0.tar.bz2#b1239ce8ef2a1eec485c398a683c5bff +https://conda.anaconda.org/conda-forge/noarch/imagesize-1.3.0-pyhd8ed1ab_0.tar.bz2#be807e7606fff9436e5e700f6bffb7c6 +https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.5.8-nompi_py38h2823cc8_101.tar.bz2#1dfe1cdee4532c72f893955259eb3de9 https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.10-h7f98852_0.tar.bz2#ffa3a757a97e851293909b49f49f28fb +https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.31.2-py38h0a891b7_0.tar.bz2#381ffd61d2617af9bddb1cee60352480 +https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-14.0.0-py38h497a2fe_0.tar.bz2#8da7787169411910df2a62dc8ef533e0 +https://conda.anaconda.org/conda-forge/linux-64/virtualenv-20.13.4-py38h578d9bd_0.tar.bz2#ff64ee0548fdd8de60b0a6a0356e0f0d 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/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.18-pthreads_h8fe5266_0.tar.bz2#41532e4448c0cce086d6570f95e4e12e +https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.12.1-mpi_mpich_h08b82f9_4.tar.bz2#975d5635b158c1b3c5c795f9d0a430a1 +https://conda.anaconda.org/conda-forge/linux-64/scipy-1.8.0-py38h56a6a73_1.tar.bz2#86073932d9e675c5929376f6f8b79b97 +https://conda.anaconda.org/conda-forge/linux-64/netcdf-fortran-4.5.4-mpi_mpich_h1364a43_0.tar.bz2#b6ba4f487ef9fd5d353ff277df06d133 +https://conda.anaconda.org/conda-forge/linux-64/libclang-13.0.1-default_hc23dcda_0.tar.bz2#8cebb0736cba83485b13dc10d242d96d +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/libcblas-3.9.0-13_linux64_openblas.tar.bz2#b17676dbd6688396c3a3076259fb7907 +https://conda.anaconda.org/conda-forge/linux-64/libpq-14.2-hd57d9b9_0.tar.bz2#91b38e297e1cc79f88f7cbf7bdb248e0 +https://conda.anaconda.org/conda-forge/noarch/distlib-0.3.4-pyhd8ed1ab_0.tar.bz2#7b50d840543d9cdae100e91582c33035 +https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-2.2.0-pyhd8ed1ab_0.tar.bz2#4d22a9315e78c6827f806065957d566e 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/libuuid-2.32.1-h7f98852_1000.tar.bz2#772d69f030955d9646d3d0eaf21d859d -https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.2.2-h7f98852_1.tar.bz2#46cf26ecc8775a0aab300ea1821aaa3c -https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.2.11-h36c2ea0_1013.tar.bz2#dcddf696ff5dfcab567100d691678e18 -https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.9.3-h9c3ff4c_1.tar.bz2#fbe97e8fa6f275d7c76a09e795adc3e6 -https://conda.anaconda.org/conda-forge/linux-64/mpich-4.0.1-h846660c_100.tar.bz2#4b85205b094808088bb0862e08251653 -https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.3-h9c3ff4c_0.tar.bz2#fb31bcb7af058244479ca635d20f0f4a -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.1l-h7f98852_0.tar.bz2#de7b38a1542dbe6f41653a8ae71adc53 -https://conda.anaconda.org/conda-forge/linux-64/pcre-8.45-h9c3ff4c_0.tar.bz2#c05d1820a6d34ff07aaaab7a9b7eddaa -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 +https://conda.anaconda.org/conda-forge/linux-64/curl-7.82.0-h7bff187_0.tar.bz2#631777dcd00a2714cf0c415ffe40c480 +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/xorg-libsm-1.2.3-hd9c2040_1000.tar.bz2#9e856f78d5c80d5a78f61e72d1d473a3 +https://conda.anaconda.org/conda-forge/linux-64/pre-commit-2.17.0-py38h578d9bd_0.tar.bz2#839ac9dba9a6126c9532781a9ea4506b +https://conda.anaconda.org/conda-forge/noarch/packaging-21.3-pyhd8ed1ab_0.tar.bz2#71f1ab2de48613876becddd496371c85 https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.0.10-h7f98852_0.tar.bz2#d6b0b50b49eccfe0be0373be628be0f3 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.9-h7f98852_0.tar.bz2#bf6f803a544f26ebbdc3bfff272eb179 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.3-h7f98852_0.tar.bz2#be93aabceefa2fac576e971aef407908 -https://conda.anaconda.org/conda-forge/linux-64/xorg-renderproto-0.11.1-h7f98852_1002.tar.bz2#06feff3d2634e3097ce2fe681474b534 -https://conda.anaconda.org/conda-forge/linux-64/xorg-xextproto-7.3.0-h7f98852_1002.tar.bz2#1e15f6ad85a7d743a2ac68dae6c82b98 -https://conda.anaconda.org/conda-forge/linux-64/xorg-xproto-7.0.31-h7f98852_1007.tar.bz2#b4a4381d54784606820704f7b5f05a15 -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.5-h516909a_1.tar.bz2#33f601066901f3e1a85af3522a8113f9 -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/libblas-3.9.0-13_linux64_openblas.tar.bz2#8a4038563ed92dfa622bd72c0d8f31d3 -https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.0.9-h7f98852_6.tar.bz2#c7c03a2592cac92246a13a0732bd1573 -https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.0.9-h7f98852_6.tar.bz2#28bfe0a70154e6881da7bae97517c948 -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/libllvm13-13.0.1-hf817b99_2.tar.bz2#47da3ce0d8b2e65ccb226c186dd91eba -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/setuptools-61.0.0-py38h578d9bd_0.tar.bz2#f22d6162248bb633b199c495268d8fc4 https://conda.anaconda.org/conda-forge/linux-64/readline-8.1-h46c0cb4_0.tar.bz2#5788de3c8d7a7d64ac56c784c4ef48e6 -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.11-h36c2ea0_1013.tar.bz2#cf7190238072a41e9579e4476a6a60b8 -https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.2-ha95c52a_0.tar.bz2#5222b231b1ef49a7f60d40b363469b70 -https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.0.9-h7f98852_6.tar.bz2#9e94bf16f14c78a36561d5019f490d22 -https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h10796ff_3.tar.bz2#21a8d66dc17f065023b33145c42652fe -https://conda.anaconda.org/conda-forge/linux-64/krb5-1.19.2-h3790be6_4.tar.bz2#dbbd32092ee31aab0f2d213e8f9f1b40 -https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-13_linux64_openblas.tar.bz2#b17676dbd6688396c3a3076259fb7907 -https://conda.anaconda.org/conda-forge/linux-64/libclang-13.0.1-default_hc23dcda_0.tar.bz2#8cebb0736cba83485b13dc10d242d96d -https://conda.anaconda.org/conda-forge/linux-64/libglib-2.70.2-h174f98d_4.tar.bz2#d44314ffae96b17657fbf3f8e47b04fc -https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-13_linux64_openblas.tar.bz2#018b80e8f21d8560ae4961567e3e00c9 -https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.47.0-h727a467_0.tar.bz2#a22567abfea169ff8048506b1ca9b230 -https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.37-h21135ba_2.tar.bz2#b6acf807307d033d4b7e758b4f44b036 -https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.10.0-ha56f1ee_2.tar.bz2#6ab4eaa11ff01801cffca0a27489dc04 -https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.3.0-h542a066_3.tar.bz2#1a0efb4dfd880b0376da8e1ba39fa838 -https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.9.12-h885dcf4_1.tar.bz2#d1355eaa48f465782f228275a0a69771 -https://conda.anaconda.org/conda-forge/linux-64/libzip-1.8.0-h4de3113_1.tar.bz2#175a746a43d42c053b91aa765fbc197d -https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-8.0.28-hfa10184_0.tar.bz2#aac17542e50a474e2e632878dc696d50 -https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.37.0-h9cd32fc_0.tar.bz2#eb66fc098824d25518a79e83d12a81d6 -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-h7f98852_6.tar.bz2#612385c4a83edb0619fe911d9da317f4 -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/freetype-2.10.4-h0708190_1.tar.bz2#4a06f2ac2e5bfae7b6b245171c3f07aa -https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.42.6-h04a7f16_0.tar.bz2#b24a1e18325a6e8f8b6b4a2ec5860ce2 -https://conda.anaconda.org/conda-forge/linux-64/gstreamer-1.18.5-h9f60fe5_3.tar.bz2#511aa83cdfcc0132380db5daf2f15f27 -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/libcurl-7.81.0-h2574ce0_0.tar.bz2#1f8655741d0269ca6756f131522da1e8 -https://conda.anaconda.org/conda-forge/linux-64/libpq-14.2-hd57d9b9_0.tar.bz2#91b38e297e1cc79f88f7cbf7bdb248e0 -https://conda.anaconda.org/conda-forge/linux-64/libwebp-1.2.2-h3452ae3_0.tar.bz2#c363665b4aabe56aae4f8981cff5b153 -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/nss-3.74-hb5efdd6_0.tar.bz2#136876ca50177058594f6c2944e95c40 -https://conda.anaconda.org/conda-forge/linux-64/python-3.8.12-ha38a3c6_3_cpython.tar.bz2#bed445cebcd8f97dce76dc06201928ee -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/alabaster-0.7.12-py_0.tar.bz2#2489a97287f90176ecdc3ca982b4b0a0 -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.0.12-pyhd8ed1ab_0.tar.bz2#1f5b32dabae0f1893ae3283dac7f799e -https://conda.anaconda.org/conda-forge/noarch/cloudpickle-2.0.0-pyhd8ed1ab_0.tar.bz2#3a8fc8b627d5fb6af827e126a10a86c6 -https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.4-pyh9f0ad1d_0.tar.bz2#c08b4c1326b880ed44f3ffb04803332f -https://conda.anaconda.org/conda-forge/linux-64/curl-7.81.0-h2574ce0_0.tar.bz2#3a95d393b490f82aa406f1892fad84d9 -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.4-pyhd8ed1ab_0.tar.bz2#7b50d840543d9cdae100e91582c33035 -https://conda.anaconda.org/conda-forge/noarch/filelock-3.6.0-pyhd8ed1ab_0.tar.bz2#6e03ca6c7b47a4152a2b12c6eee3bd32 -https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.13.96-ha180cfb_0.tar.bz2#d190a1c55c84ba1c9a33484a38ece029 -https://conda.anaconda.org/conda-forge/noarch/fsspec-2022.2.0-pyhd8ed1ab_0.tar.bz2#f31e31092035d427b05233ab924c7613 -https://conda.anaconda.org/conda-forge/linux-64/gst-plugins-base-1.18.5-hf529b03_3.tar.bz2#524a9f1718bac53a6cf4906bcc51d044 -https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.12.1-mpi_mpich_h08b82f9_4.tar.bz2#975d5635b158c1b3c5c795f9d0a430a1 -https://conda.anaconda.org/conda-forge/noarch/idna-3.3-pyhd8ed1ab_0.tar.bz2#40b50b8b030f5f2f22085c062ed013dd -https://conda.anaconda.org/conda-forge/noarch/imagesize-1.3.0-pyhd8ed1ab_0.tar.bz2#be807e7606fff9436e5e700f6bffb7c6 -https://conda.anaconda.org/conda-forge/noarch/iris-sample-data-2.4.0-pyhd8ed1ab_0.tar.bz2#18ee9c07cf945a33f92caf1ee3d23ad9 -https://conda.anaconda.org/conda-forge/noarch/locket-0.2.0-py_2.tar.bz2#709e8671651c7ec3d1ad07800339ff1d -https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2#2ba8498c1018c1e9c61eb99b973dfe19 -https://conda.anaconda.org/conda-forge/noarch/nose-1.3.7-py_1006.tar.bz2#382019d5f8e9362ef6f60a8d4e7bce8f -https://conda.anaconda.org/conda-forge/noarch/olefile-0.46-pyh9f0ad1d_1.tar.bz2#0b2e68acc8c78c8cc392b90983481f58 -https://conda.anaconda.org/conda-forge/noarch/platformdirs-2.5.1-pyhd8ed1ab_0.tar.bz2#d5df87964a39f67c46a5448f4e78d9b6 -https://conda.anaconda.org/conda-forge/linux-64/proj-8.2.1-h277dcde_0.tar.bz2#f2ceb1be6565c35e2db0ac948754751d -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.7-pyhd8ed1ab_0.tar.bz2#727e2216d9c47455d8ddc060eb2caad9 -https://conda.anaconda.org/conda-forge/noarch/pyshp-2.2.0-pyhd8ed1ab_0.tar.bz2#2aa546be05be34b8e1744afd327b623f -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-2021.3-pyhd8ed1ab_0.tar.bz2#7e4f811bff46a5a6a7e0094921389395 +https://conda.anaconda.org/conda-forge/linux-64/libcurl-7.82.0-h7bff187_0.tar.bz2#fa26f833ca8796ad44f9561c5402013d +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/xorg-libx11-1.7.2-h7f98852_0.tar.bz2#12a61e640b8894504326aadafccbb790 +https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.0.9-h7f98852_6.tar.bz2#b0f44f63f7d771d7670747a1dd5d5ac1 +https://conda.anaconda.org/conda-forge/linux-64/mpi-1.0-mpich.tar.bz2#c1fcff3417b5a22bbc4cf6e8c23648cf +https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.32.1-h7f98852_1000.tar.bz2#772d69f030955d9646d3d0eaf21d859d 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/sphinxcontrib-applehelp-1.0.2-py_0.tar.bz2#20b2eaeaeea4ef9a9a0d99770620fd09 -https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-devhelp-1.0.2-py_0.tar.bz2#68e01cac9d38d0e717cd5c87bc3d2cc9 -https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-htmlhelp-2.0.0-pyhd8ed1ab_0.tar.bz2#77dad82eb9c8c1525ff7953e0756d708 -https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-jsmath-1.0.1-py_0.tar.bz2#67cd9d9c0382d37479b4d306c369a2d4 -https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-qthelp-1.0.3-py_0.tar.bz2#d01180388e6d1838c3e1ad029590aa7a -https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_0.tar.bz2#f832c45a477c78bebd107098db465095 -https://conda.anaconda.org/conda-forge/noarch/toolz-0.11.2-pyhd8ed1ab_0.tar.bz2#f348d1590550371edfac5ed3c1d44f7e +https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.0.9-h7f98852_6.tar.bz2#9e94bf16f14c78a36561d5019f490d22 +https://conda.anaconda.org/conda-forge/linux-64/proj-9.0.0-h93bde94_1.tar.bz2#cf908994f24ea526afc59f295d5b07c1 +https://conda.anaconda.org/conda-forge/linux-64/pysocks-1.7.1-py38h578d9bd_4.tar.bz2#9c4bbee6f682f2fc7d7803df3996e77e +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/esmf-8.2.0-mpi_mpich_h4975321_100.tar.bz2#56f5c650937b1667ad0a557a0dff3bc4 +https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab +https://conda.anaconda.org/conda-forge/linux-64/libglib-2.70.2-h174f98d_4.tar.bz2#d44314ffae96b17657fbf3f8e47b04fc +https://conda.anaconda.org/conda-forge/linux-64/pixman-0.40.0-h36c2ea0_0.tar.bz2#660e72c82f2e75a6b3fe6a6e75c79f19 +https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.6.0-pyhd8ed1ab_0.tar.bz2#0941325bf48969e2b3b19d0951740950 +https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.9.3-h9c3ff4c_1.tar.bz2#fbe97e8fa6f275d7c76a09e795adc3e6 +https://conda.anaconda.org/conda-forge/linux-64/xz-5.2.5-h516909a_1.tar.bz2#33f601066901f3e1a85af3522a8113f9 +https://conda.anaconda.org/conda-forge/linux-64/libllvm13-13.0.1-hf817b99_2.tar.bz2#47da3ce0d8b2e65ccb226c186dd91eba +https://conda.anaconda.org/conda-forge/noarch/sphinx-4.4.0-pyh6c4a22f_1.tar.bz2#a9025d14c2a609e0d895ad3e75b5369c 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.7.0-pyhd8ed1ab_1.tar.bz2#b689b2cbc8481b224777415e1a193170 -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.9.1-pyh44b312d_0.tar.bz2#74136ed39bfea0832d338df1e58d013e -https://conda.anaconda.org/conda-forge/linux-64/cairo-1.16.0-ha00ac49_1009.tar.bz2#d1dff57b8731c245d3247b46d002e1c9 +https://conda.anaconda.org/conda-forge/noarch/partd-1.2.0-pyhd8ed1ab_0.tar.bz2#0c32f563d7f22e3a34c95cad8cc95651 +https://conda.anaconda.org/conda-forge/noarch/requests-2.27.1-pyhd8ed1ab_0.tar.bz2#7c1c427246b057b8fa97200ecdb2ed62 https://conda.anaconda.org/conda-forge/linux-64/certifi-2021.10.8-py38h578d9bd_1.tar.bz2#52a6cee65a5d10ed1c3f0af24fb48dd3 -https://conda.anaconda.org/conda-forge/linux-64/cffi-1.15.0-py38h3931269_0.tar.bz2#9c491a90ae11d08ca97326a0ed876f3a -https://conda.anaconda.org/conda-forge/linux-64/docutils-0.16-py38h578d9bd_3.tar.bz2#a7866449fb9e5e4008a02df276549d34 -https://conda.anaconda.org/conda-forge/linux-64/importlib-metadata-4.11.2-py38h578d9bd_0.tar.bz2#0c1ffd6807cbf6c15456c49ca9baa668 -https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.3.2-py38h1fd1430_1.tar.bz2#085365abfe53d5d13bb68b1dda0b439e -https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-h3cfcdeb_1.tar.bz2#37d7568c595f0cfcd0c493f5ca0344ab https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.8.1-mpi_mpich_h319fa22_1.tar.bz2#7583fbaea3648f692c0c019254bc196c -https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.0-py38h0a891b7_1.tar.bz2#60eff55f2a845f35e58bd0be235fe4b7 -https://conda.anaconda.org/conda-forge/linux-64/mpi4py-3.1.3-py38he865349_0.tar.bz2#b1b3d6847a68251a1465206ab466b475 -https://conda.anaconda.org/conda-forge/linux-64/numpy-1.22.3-py38h05e7239_0.tar.bz2#90b4ee61abb81fb3f3995ec9d4c734f0 -https://conda.anaconda.org/conda-forge/noarch/packaging-21.3-pyhd8ed1ab_0.tar.bz2#71f1ab2de48613876becddd496371c85 -https://conda.anaconda.org/conda-forge/noarch/partd-1.2.0-pyhd8ed1ab_0.tar.bz2#0c32f563d7f22e3a34c95cad8cc95651 +https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.3.0-py38hbc0797c_2.tar.bz2#7e4c695d10aa5e4576e87fb00a9d2511 +https://conda.anaconda.org/conda-forge/noarch/sphinx-gallery-0.10.1-pyhd8ed1ab_0.tar.bz2#4918585fe5e5341740f7e63c61743efb +https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2#f766549260d6815b0c52253f1fb1bb29 +https://conda.anaconda.org/conda-forge/noarch/nc-time-axis-1.4.0-pyhd8ed1ab_0.tar.bz2#9113b4e4fa2fa4a7f129c71a6f319475 +https://conda.anaconda.org/conda-forge/linux-64/pango-1.50.6-hbd2fdc8_0.tar.bz2#129c70116050ccf66b362ca3203fb660 +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/pillow-6.2.1-py38hd70f55b_1.tar.bz2#80d719bee2b77a106b199150c0829107 -https://conda.anaconda.org/conda-forge/noarch/pockets-0.9.1-py_0.tar.bz2#1b52f0c42e8077e5a33e00fe72269364 -https://conda.anaconda.org/conda-forge/linux-64/pyqt5-sip-4.19.18-py38h709712a_8.tar.bz2#11b72f5b1cc15427c89232321172a0bc -https://conda.anaconda.org/conda-forge/linux-64/pysocks-1.7.1-py38h578d9bd_4.tar.bz2#9c4bbee6f682f2fc7d7803df3996e77e -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_0.tar.bz2#12eaa8cbfedfbf7879e5653467b03c94 -https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0-py38h497a2fe_3.tar.bz2#131de7d638aa59fb8afbce59f1a8aa98 -https://conda.anaconda.org/conda-forge/linux-64/qt-5.12.9-ha98a1a1_5.tar.bz2#9b27fa0b1044a2119fb1b290617fe06f -https://conda.anaconda.org/conda-forge/linux-64/setuptools-60.9.3-py38h578d9bd_0.tar.bz2#864b832ea94d9c0b37ddfbbb8adb42f1 -https://conda.anaconda.org/conda-forge/linux-64/tornado-6.1-py38h497a2fe_2.tar.bz2#63b3b55c98b4239134e0be080f448944 -https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-14.0.0-py38h497a2fe_0.tar.bz2#8da7787169411910df2a62dc8ef533e0 -https://conda.anaconda.org/conda-forge/linux-64/virtualenv-20.13.3-py38h578d9bd_0.tar.bz2#4f2dd671de7a8666acdc51a9dd6d4324 -https://conda.anaconda.org/conda-forge/linux-64/brotlipy-0.7.0-py38h497a2fe_1003.tar.bz2#9189b42c42b9c87b2b2068cbe31901a8 -https://conda.anaconda.org/conda-forge/linux-64/cftime-1.6.0-py38h3ec907f_0.tar.bz2#35411e5fc8dd523f9e68316847e6a25b -https://conda.anaconda.org/conda-forge/linux-64/cryptography-36.0.1-py38h3e25421_0.tar.bz2#acc14d0d71dbf74f6a15f2456951b6cf -https://conda.anaconda.org/conda-forge/noarch/dask-core-2022.2.1-pyhd8ed1ab_0.tar.bz2#0cb751f07e68fda1d631a02faa66f0de -https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.29.1-py38h497a2fe_0.tar.bz2#121e02be214af4980911bb2cbd5b2742 -https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-3.4.0-hb4a5f5f_0.tar.bz2#42190c4597593e9742513d7b39b02c49 -https://conda.anaconda.org/conda-forge/noarch/jinja2-3.0.3-pyhd8ed1ab_0.tar.bz2#036d872c653780cb26e797e2e2f61b4c +https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.0.9-h7f98852_6.tar.bz2#28bfe0a70154e6881da7bae97517c948 +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/linux-64/zlib-1.2.11-h36c2ea0_1013.tar.bz2#cf7190238072a41e9579e4476a6a60b8 +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/mo_pack-0.2.0-py38h6c62de6_1006.tar.bz2#829b1209dfadd431a11048d6eeaf5bef -https://conda.anaconda.org/conda-forge/linux-64/netcdf-fortran-4.5.4-mpi_mpich_h1364a43_0.tar.bz2#b6ba4f487ef9fd5d353ff277df06d133 -https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.6.0-pyhd8ed1ab_0.tar.bz2#0941325bf48969e2b3b19d0951740950 -https://conda.anaconda.org/conda-forge/linux-64/pandas-1.4.1-py38h43a58ef_0.tar.bz2#1083ebe2edc30e4fb9568d1f66e3588b -https://conda.anaconda.org/conda-forge/noarch/pip-22.0.4-pyhd8ed1ab_0.tar.bz2#b1239ce8ef2a1eec485c398a683c5bff +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/libxcb-1.13-h7f98852_1004.tar.bz2#b3653fdc58d03face9724f602218a904 +https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.0.7-pyhd8ed1ab_0.tar.bz2#727e2216d9c47455d8ddc060eb2caad9 +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/geos-3.10.2-h9c3ff4c_0.tar.bz2#fe9a66a351bfa7a84c3108304c7bcba5 +https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-h36c2ea0_1001.tar.bz2#22dad4df6e8630e8dff2428f6f6a7036 +https://conda.anaconda.org/conda-forge/noarch/zipp-3.7.0-pyhd8ed1ab_1.tar.bz2#b689b2cbc8481b224777415e1a193170 +https://conda.anaconda.org/conda-forge/linux-64/cartopy-0.20.2-py38h51d8e34_4.tar.bz2#9f23c80d08456c02ab284f974719b013 +https://conda.anaconda.org/conda-forge/linux-64/cftime-1.6.0-py38h3ec907f_0.tar.bz2#35411e5fc8dd523f9e68316847e6a25b https://conda.anaconda.org/conda-forge/noarch/pygments-2.11.2-pyhd8ed1ab_0.tar.bz2#caef60540e2239e27bf62569a5015e3b -https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.3.0-py38h5383654_1.tar.bz2#5b600e019fa7c33be73bdb626236936b +https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.2-ha95c52a_0.tar.bz2#5222b231b1ef49a7f60d40b363469b70 +https://conda.anaconda.org/conda-forge/linux-64/libwebp-1.2.2-h3452ae3_0.tar.bz2#c363665b4aabe56aae4f8981cff5b153 +https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2#2ba8498c1018c1e9c61eb99b973dfe19 +https://conda.anaconda.org/conda-forge/linux-64/jpeg-9e-h7f98852_0.tar.bz2#5c214edc675a7fb7cbb34b1d854e5141 +https://conda.anaconda.org/conda-forge/linux-64/gstreamer-1.20.1-hd4edc92_1.tar.bz2#ebebb56f78dd7518dcc94d6c26aa2249 +https://conda.anaconda.org/conda-forge/noarch/sphinx-copybutton-0.5.0-pyhd8ed1ab_0.tar.bz2#4c969cdd5191306c269490f7ff236d9c +https://conda.anaconda.org/conda-forge/noarch/idna-3.3-pyhd8ed1ab_0.tar.bz2#40b50b8b030f5f2f22085c062ed013dd +https://conda.anaconda.org/conda-forge/linux-64/cairo-1.16.0-ha12eb4b_1010.tar.bz2#e15c0969bf37df9dae513a48ac871a7d +https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0-py38h497a2fe_3.tar.bz2#131de7d638aa59fb8afbce59f1a8aa98 +https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.37-h21135ba_2.tar.bz2#b6acf807307d033d4b7e758b4f44b036 +https://conda.anaconda.org/conda-forge/linux-64/pywavelets-1.3.0-py38h3ec907f_0.tar.bz2#3562860c28f129d73c4431cb69a13ce1 +https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2#4d331e44109e3f0e19b4cb8f9b82f3e1 +https://conda.anaconda.org/conda-forge/noarch/olefile-0.46-pyh9f0ad1d_1.tar.bz2#0b2e68acc8c78c8cc392b90983481f58 +https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.0-py38h43d8883_0.tar.bz2#e1d977faa8e5cec62eaf960cdf83d2b5 https://conda.anaconda.org/conda-forge/linux-64/pyqt-impl-5.12.3-py38h0ffb2e6_8.tar.bz2#acfc7625a212c27f7decdca86fdb2aba -https://conda.anaconda.org/conda-forge/linux-64/python-stratify-0.2.post0-py38h6c62de6_1.tar.bz2#a350e3f4ca899e95122f66806e048858 -https://conda.anaconda.org/conda-forge/linux-64/pywavelets-1.2.0-py38h6c62de6_1.tar.bz2#2953d3fc0113fc6ffb955a5b72811fb0 -https://conda.anaconda.org/conda-forge/linux-64/scipy-1.8.0-py38h56a6a73_1.tar.bz2#86073932d9e675c5929376f6f8b79b97 +https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-h283352f_2.tar.bz2#2b0d39005a2e8347f329fe578bd6488a +https://conda.anaconda.org/conda-forge/linux-64/python-stratify-0.2.post0-py38h3ec907f_1.tar.bz2#4cf16ae4d975b6935582ab87dba69f0e +https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-1_gnu.tar.bz2#561e277319a41d4f24f5c05a9ef63c04 +https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-devhelp-1.0.2-py_0.tar.bz2#68e01cac9d38d0e717cd5c87bc3d2cc9 +https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.0.0-py38h0a891b7_0.tar.bz2#12eaa8cbfedfbf7879e5653467b03c94 +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/python-3.8.13-h582c2e5_0_cpython.tar.bz2#8ec74710472994e2411a8020fa8589ce +https://conda.anaconda.org/conda-forge/noarch/urllib3-1.26.9-pyhd8ed1ab_0.tar.bz2#0ea179ee251aa7100807c35bc0252693 +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/nss-3.76-h2350873_0.tar.bz2#0ecbc685e45dfa8874a68f2cc832d9d1 +https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.3-h9c3ff4c_0.tar.bz2#fb31bcb7af058244479ca635d20f0f4a +https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.0.9-h7f98852_6.tar.bz2#c7c03a2592cac92246a13a0732bd1573 +https://conda.anaconda.org/conda-forge/noarch/dask-core-2022.3.0-pyhd8ed1ab_0.tar.bz2#f428eca4a3c0f1bc39ca6c8f2ae53611 +https://conda.anaconda.org/conda-forge/linux-64/gst-plugins-base-1.20.1-hcf0ee16_1.tar.bz2#4c41fc47db7d631862f12e5e5c885cb9 +https://conda.anaconda.org/conda-forge/linux-64/cf-units-3.0.1-py38h6c62de6_2.tar.bz2#350322b046c129e5802b79358a1343f7 +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/gts-0.7.6-h64030ff_2.tar.bz2#112eb9b5b93f0c02e59aea4fd1967363 +https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2#4cb3ad778ec2d5a7acbdf254eb1c42ae +https://conda.anaconda.org/conda-forge/noarch/platformdirs-2.5.1-pyhd8ed1ab_0.tar.bz2#d5df87964a39f67c46a5448f4e78d9b6 +https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2021.10.8-ha878542_0.tar.bz2#575611b8a84f45960e87722eeb51fa26 https://conda.anaconda.org/conda-forge/linux-64/shapely-1.8.0-py38h596eeab_5.tar.bz2#ec3b783081e14a9dc0eb5ce609649728 +https://conda.anaconda.org/conda-forge/linux-64/openssl-1.1.1n-h166bdaf_0.tar.bz2#cf0ddbd911fa547e6bc4291ca5e17379 +https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h10796ff_3.tar.bz2#21a8d66dc17f065023b33145c42652fe +https://conda.anaconda.org/conda-forge/noarch/sphinx_rtd_theme-1.0.0-pyhd8ed1ab_0.tar.bz2#9f633f2f2869184e31acfeae95b24345 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-py38h1fd1430_1.tar.bz2#c494f75082f9c052944fda1b22c83336 -https://conda.anaconda.org/conda-forge/linux-64/cf-units-3.0.1-py38h6c62de6_2.tar.bz2#350322b046c129e5802b79358a1343f7 -https://conda.anaconda.org/conda-forge/linux-64/esmf-8.2.0-mpi_mpich_h4975321_100.tar.bz2#56f5c650937b1667ad0a557a0dff3bc4 -https://conda.anaconda.org/conda-forge/noarch/identify-2.4.11-pyhd8ed1ab_0.tar.bz2#979d7dfda4d04702391e80158c322039 -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.1-py38hf4fb855_0.tar.bz2#47cf0cab2ae368e1062e75cfbc4277af -https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.5.8-nompi_py38h2823cc8_101.tar.bz2#1dfe1cdee4532c72f893955259eb3de9 -https://conda.anaconda.org/conda-forge/linux-64/pango-1.50.5-h4dcc4a0_0.tar.bz2#56ce3e3bec0d5c9e6db22083a3ef5e13 -https://conda.anaconda.org/conda-forge/noarch/pyopenssl-22.0.0-pyhd8ed1ab_0.tar.bz2#1d7e241dfaf5475e893d4b824bb71b44 -https://conda.anaconda.org/conda-forge/linux-64/pyqtchart-5.12-py38h7400c14_8.tar.bz2#78a2a6cb4ef31f997c1bee8223a9e579 +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-11.2.0-he4da1e4_14.tar.bz2#c3b55849172e4bfa01d3349ea8e73a3a +https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-4.1.0-h40b6f09_0.tar.bz2#567de1f208bd0547b37d4ea9760606c0 +https://conda.anaconda.org/conda-forge/linux-64/docutils-0.16-py38h578d9bd_3.tar.bz2#a7866449fb9e5e4008a02df276549d34 +https://conda.anaconda.org/conda-forge/linux-64/libzip-1.8.0-h4de3113_1.tar.bz2#175a746a43d42c053b91aa765fbc197d +https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.13.96-h8e229c2_2.tar.bz2#70d2ba376dff51a33343169642aebb44 https://conda.anaconda.org/conda-forge/linux-64/pyqtwebengine-5.12.1-py38h7400c14_8.tar.bz2#857894ea9c5e53c962c3a0932efa71ea -https://conda.anaconda.org/conda-forge/linux-64/cartopy-0.20.2-py38ha217159_3.tar.bz2#d7461e191f7a0522e4709612786bdf4e -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/gtk2-2.24.33-h90689f9_2.tar.bz2#957a0255ab58aaf394a91725d73ab422 +https://conda.anaconda.org/conda-forge/noarch/filelock-3.6.0-pyhd8ed1ab_0.tar.bz2#6e03ca6c7b47a4152a2b12c6eee3bd32 +https://conda.anaconda.org/conda-forge/noarch/sphinx-panels-0.6.0-pyhd8ed1ab_0.tar.bz2#6eec6480601f5d15babf9c3b3987f34a +https://conda.anaconda.org/conda-forge/linux-64/cryptography-36.0.2-py38h2b5fc30_0.tar.bz2#22274a82cf664d94a9e7c8f9deddf52a +https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.18-pthreads_h8fe5266_0.tar.bz2#41532e4448c0cce086d6570f95e4e12e +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/xorg-kbproto-1.0.7-h7f98852_1002.tar.bz2#4b230e8381279d76131116660f5a241a +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/pandas-1.4.1-py38h43a58ef_0.tar.bz2#1083ebe2edc30e4fb9568d1f66e3588b +https://conda.anaconda.org/conda-forge/noarch/pockets-0.9.1-py_0.tar.bz2#1b52f0c42e8077e5a33e00fe72269364 +https://conda.anaconda.org/conda-forge/linux-64/xxhash-0.8.0-h7f98852_3.tar.bz2#52402c791f35e414e704b7a113f99605 +https://conda.anaconda.org/conda-forge/noarch/cloudpickle-2.0.0-pyhd8ed1ab_0.tar.bz2#3a8fc8b627d5fb6af827e126a10a86c6 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.9-h7f98852_0.tar.bz2#bf6f803a544f26ebbdc3bfff272eb179 +https://conda.anaconda.org/conda-forge/linux-64/libvorbis-1.3.7-h9c3ff4c_0.tar.bz2#309dec04b70a3cc0f1e84a4013683bc0 +https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-11.2.0-h5c6108e_14.tar.bz2#f8b51fb7bccd48f959982973df578165 +https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.2.11-h36c2ea0_1013.tar.bz2#dcddf696ff5dfcab567100d691678e18 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.3-h7f98852_0.tar.bz2#be93aabceefa2fac576e971aef407908 +https://conda.anaconda.org/conda-forge/linux-64/xorg-xextproto-7.3.0-h7f98852_1002.tar.bz2#1e15f6ad85a7d743a2ac68dae6c82b98 +https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-11.2.0-h1d223b6_14.tar.bz2#47e6c01d149b26090748d9d1ac32491b +https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.42.8-hff1cb4f_0.tar.bz2#908fc30f89e27817d835b45f865536d7 +https://conda.anaconda.org/conda-forge/noarch/pyopenssl-22.0.0-pyhd8ed1ab_0.tar.bz2#1d7e241dfaf5475e893d4b824bb71b44 +https://conda.anaconda.org/conda-forge/linux-64/jbig-2.1-h7f98852_2003.tar.bz2#1aa0cee79792fa97b7ff4545110b60bf +https://conda.anaconda.org/conda-forge/linux-64/brotlipy-0.7.0-py38h497a2fe_1003.tar.bz2#9189b42c42b9c87b2b2068cbe31901a8 +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/nspr-4.32-h9c3ff4c_1.tar.bz2#29ded371806431b0499aaee146abfc3e https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.52.5-h0a9e6e8_2.tar.bz2#aa768fdaad03509a97df37f81163346b -https://conda.anaconda.org/conda-forge/noarch/nc-time-axis-1.4.0-pyhd8ed1ab_0.tar.bz2#9113b4e4fa2fa4a7f129c71a6f319475 -https://conda.anaconda.org/conda-forge/linux-64/pre-commit-2.17.0-py38h578d9bd_0.tar.bz2#839ac9dba9a6126c9532781a9ea4506b -https://conda.anaconda.org/conda-forge/linux-64/pyqt-5.12.3-py38h578d9bd_8.tar.bz2#88368a5889f31dff922a2d57bbfc3f5b -https://conda.anaconda.org/conda-forge/noarch/urllib3-1.26.8-pyhd8ed1ab_1.tar.bz2#53f1387c68c21cecb386e2cde51b3f7c -https://conda.anaconda.org/conda-forge/linux-64/graphviz-3.0.0-h5abf519_0.tar.bz2#e5521af56c6e927397ca9851eecb2f48 +https://conda.anaconda.org/conda-forge/linux-64/mysql-common-8.0.28-ha770c72_0.tar.bz2#56594fdd5a80774a80d546fbbccf2c03 +https://conda.anaconda.org/conda-forge/linux-64/pcre-8.45-h9c3ff4c_0.tar.bz2#c05d1820a6d34ff07aaaab7a9b7eddaa +https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.9.12-h885dcf4_1.tar.bz2#d1355eaa48f465782f228275a0a69771 +https://conda.anaconda.org/conda-forge/noarch/cycler-0.11.0-pyhd8ed1ab_0.tar.bz2#a50559fad0affdbb33729a68669ca1cb +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/antlr-python-runtime-4.7.2-py38h578d9bd_1003.tar.bz2#db8b471d9a764f561a129f94ea215c0a +https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_0.tar.bz2#f832c45a477c78bebd107098db465095 +https://conda.anaconda.org/conda-forge/linux-64/tornado-6.1-py38h497a2fe_2.tar.bz2#63b3b55c98b4239134e0be080f448944 https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.5.1-py38h578d9bd_0.tar.bz2#0d78be9cf1c400ba8e3077cf060492f1 -https://conda.anaconda.org/conda-forge/noarch/requests-2.27.1-pyhd8ed1ab_0.tar.bz2#7c1c427246b057b8fa97200ecdb2ed62 -https://conda.anaconda.org/conda-forge/noarch/sphinx-4.4.0-pyh6c4a22f_1.tar.bz2#a9025d14c2a609e0d895ad3e75b5369c -https://conda.anaconda.org/conda-forge/noarch/sphinx-copybutton-0.5.0-pyhd8ed1ab_0.tar.bz2#4c969cdd5191306c269490f7ff236d9c -https://conda.anaconda.org/conda-forge/noarch/sphinx-gallery-0.10.1-pyhd8ed1ab_0.tar.bz2#4918585fe5e5341740f7e63c61743efb -https://conda.anaconda.org/conda-forge/noarch/sphinx-panels-0.6.0-pyhd8ed1ab_0.tar.bz2#6eec6480601f5d15babf9c3b3987f34a -https://conda.anaconda.org/conda-forge/noarch/sphinx_rtd_theme-1.0.0-pyhd8ed1ab_0.tar.bz2#9f633f2f2869184e31acfeae95b24345 +https://conda.anaconda.org/conda-forge/linux-64/icu-69.1-h9c3ff4c_0.tar.bz2#e0773c9556d588b062a4e1424a6a02fa +https://conda.anaconda.org/conda-forge/linux-64/lerc-3.0-h9c3ff4c_0.tar.bz2#7fcefde484980d23f0ec24c11e314d2e +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/mpich-4.0.1-h846660c_100.tar.bz2#4b85205b094808088bb0862e08251653 +https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-2.0.12-pyhd8ed1ab_0.tar.bz2#1f5b32dabae0f1893ae3283dac7f799e +https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.5.1-py38hf4fb855_0.tar.bz2#47cf0cab2ae368e1062e75cfbc4277af +https://conda.anaconda.org/conda-forge/linux-64/numpy-1.22.3-py38h05e7239_0.tar.bz2#90b4ee61abb81fb3f3995ec9d4c734f0 +https://conda.anaconda.org/conda-forge/linux-64/udunits2-2.2.28-hc3e0081_0.tar.bz2#d4c341e0379c31e9e781d4f204726867 +https://conda.anaconda.org/conda-forge/noarch/babel-2.9.1-pyh44b312d_0.tar.bz2#74136ed39bfea0832d338df1e58d013e +https://conda.anaconda.org/conda-forge/linux-64/pyqt5-sip-4.19.18-py38h709712a_8.tar.bz2#11b72f5b1cc15427c89232321172a0bc +https://conda.anaconda.org/conda-forge/linux-64/mpi4py-3.1.3-py38he865349_0.tar.bz2#b1b3d6847a68251a1465206ab466b475 +https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.16-h516909a_0.tar.bz2#5c0f338a513a2943c659ae619fca9211 +https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.2.2-h7f98852_1.tar.bz2#46cf26ecc8775a0aab300ea1821aaa3c +https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-8.0.28-hfa10184_0.tar.bz2#aac17542e50a474e2e632878dc696d50 +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/xorg-libxrender-0.9.10-h7f98852_1003.tar.bz2#f59c1242cc1dd93e72c2ee2b360979eb +https://conda.anaconda.org/conda-forge/noarch/pytz-2022.1-pyhd8ed1ab_0.tar.bz2#b87d66d6d3991d988fb31510c95a9267 +https://conda.anaconda.org/conda-forge/noarch/pycparser-2.21-pyhd8ed1ab_0.tar.bz2#076becd9e05608f8dc72757d5f3a91ff +https://conda.anaconda.org/conda-forge/linux-64/cffi-1.15.0-py38h3931269_0.tar.bz2#9c491a90ae11d08ca97326a0ed876f3a +https://conda.anaconda.org/conda-forge/linux-64/pyqt-5.12.3-py38h578d9bd_8.tar.bz2#88368a5889f31dff922a2d57bbfc3f5b +https://conda.anaconda.org/conda-forge/noarch/nose-1.3.7-py_1006.tar.bz2#382019d5f8e9362ef6f60a8d4e7bce8f +https://conda.anaconda.org/conda-forge/linux-64/libtool-2.4.6-h9c3ff4c_1008.tar.bz2#16e143a1ed4b4fd169536373957f6fee +https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.0-pyhd8ed1ab_0.tar.bz2#57701269794b583dc907037bc66ae6ec +https://conda.anaconda.org/conda-forge/linux-64/xorg-xproto-7.0.31-h7f98852_1007.tar.bz2#b4a4381d54784606820704f7b5f05a15 +https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.3-h516909a_0.tar.bz2#1378b88874f42ac31b2f8e4f6975cb7b +https://conda.anaconda.org/conda-forge/linux-64/pyqtchart-5.12-py38h7400c14_8.tar.bz2#78a2a6cb4ef31f997c1bee8223a9e579 +https://conda.anaconda.org/conda-forge/noarch/cfgv-3.3.1-pyhd8ed1ab_0.tar.bz2#ebb5f5f7dc4f1a3780ef7ea7738db08c +https://conda.anaconda.org/conda-forge/noarch/pyshp-2.2.0-pyhd8ed1ab_0.tar.bz2#2aa546be05be34b8e1744afd327b623f +https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.10.0-ha56f1ee_2.tar.bz2#6ab4eaa11ff01801cffca0a27489dc04 +https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-htmlhelp-2.0.0-pyhd8ed1ab_0.tar.bz2#77dad82eb9c8c1525ff7953e0756d708 +https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2#d645c6d2ac96843a2bfaccd2d62b3ac3 https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-serializinghtml-1.1.5-pyhd8ed1ab_1.tar.bz2#63d2f874f990fdcab47c822b608d6ade +https://conda.anaconda.org/conda-forge/noarch/locket-0.2.0-py_2.tar.bz2#709e8671651c7ec3d1ad07800339ff1d +https://conda.anaconda.org/conda-forge/noarch/toolz-0.11.2-pyhd8ed1ab_0.tar.bz2#f348d1590550371edfac5ed3c1d44f7e +https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-11.2.0-h69a702a_14.tar.bz2#67328431d4aeab35e5a2c6c22669c22b +https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.8-2_cp38.tar.bz2#bfbb29d517281e78ac53e48d21e6e860 +https://conda.anaconda.org/conda-forge/noarch/iris-sample-data-2.4.0-pyhd8ed1ab_0.tar.bz2#18ee9c07cf945a33f92caf1ee3d23ad9 From 3ab9eb80acd1d49ba95ab8a104cd808c382b634c Mon Sep 17 00:00:00 2001 From: Will Benfold <69585101+wjbenfold@users.noreply.github.com> Date: Thu, 31 Mar 2022 11:58:53 +0100 Subject: [PATCH 08/15] 'Deep' benchmarks to catch scaling with field count (#4654) * 'Deep' benchmarks to catch scaling with field count * Better sized benchmarks * What's new * Update benchmarks/benchmarks/load/__init__.py Co-authored-by: Martin Yeo <40734014+trexfeathers@users.noreply.github.com> Co-authored-by: Martin Yeo <40734014+trexfeathers@users.noreply.github.com> --- benchmarks/benchmarks/load/__init__.py | 8 +++++--- docs/src/whatsnew/dev.rst | 3 +++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/benchmarks/benchmarks/load/__init__.py b/benchmarks/benchmarks/load/__init__.py index 74a751a46b..1b0ea696f6 100644 --- a/benchmarks/benchmarks/load/__init__.py +++ b/benchmarks/benchmarks/load/__init__.py @@ -24,8 +24,10 @@ class LoadAndRealise: + # For data generation + timeout = 600.0 params = [ - [(2, 2, 2), (1280, 960, 5)], + [(2, 2, 2), (1280, 960, 5), (2, 2, 1000)], [False, True], ["FF", "PP", "NetCDF"], ] @@ -68,7 +70,7 @@ def time_realise(self, _, __, ___, ____) -> None: class STASHConstraint: # xyz sizes mimic LoadAndRealise to maximise file re-use. - params = [[(2, 2, 2), (1280, 960, 5)], ["FF", "PP"]] + params = [[(2, 2, 2), (1280, 960, 5), (2, 2, 1000)], ["FF", "PP"]] param_names = ["xyz", "file_format"] def setup_cache(self) -> dict: @@ -155,7 +157,7 @@ class StructuredFF: avoiding the cost of merging. """ - params = [[(2, 2, 2), (1280, 960, 5)], [False, True]] + params = [[(2, 2, 2), (1280, 960, 5), (2, 2, 1000)], [False, True]] param_names = ["xyz", "structured_loading"] def setup_cache(self) -> dict: diff --git a/docs/src/whatsnew/dev.rst b/docs/src/whatsnew/dev.rst index 7d4a190ac9..8cf0ac31cf 100644 --- a/docs/src/whatsnew/dev.rst +++ b/docs/src/whatsnew/dev.rst @@ -106,6 +106,9 @@ This document explains the changes made to Iris for this release infrastructure (see :ref:`contributing.benchmarks`), building on 2 hard years of lessons learned 🎉. (:pull:`4477`, :pull:`4562`, :pull:`4571`, :pull:`4583`, :pull:`4621`) +#. `@wjbenfold`_ used the aforementioned benchmarking infrastructure to + introduce deep (large 3rd dimension) loading and realisation benchmarks. + (:pull:`4654`) #. `@wjbenfold`_ made :func:`iris.tests.stock.simple_1d` respect the ``with_bounds`` argument. (:pull:`4658`) From 07991fd33bdd8e762579caafaef37a74890253fb Mon Sep 17 00:00:00 2001 From: Ruth Comer <10599679+rcomer@users.noreply.github.com> Date: Thu, 31 Mar 2022 15:55:05 +0100 Subject: [PATCH 09/15] Automatic reversal of DimCoord bounds (#4466) * first pass at ensuring contiguous bounds * reduce scope and fix old tests * bounds setter tests * test contiguity preserved * check bounds within reverse tests * add comment; merge test * simpler approach * revert now redundant changes * update cml * cell equality to ignore bound order * update cml * move merge test to integration * review: simple actions * review: split up cube reverse tests * review: check bounds in all cube reverse tests * whatsnew * review: reduce test repetition * tweak whatsnew * blank line --- docs/src/whatsnew/dev.rst | 10 +- lib/iris/coords.py | 23 +++- lib/iris/tests/integration/merge/__init__.py | 6 + .../tests/integration/merge/test_merge.py | 37 +++++++ .../coord_api/intersection_reversed.xml | 16 +-- .../cube_slice/2d_intersect_and_reverse.cml | 18 +-- .../results/cube_slice/2d_to_2d_revesed.cml | 20 ++-- lib/iris/tests/test_coord_api.py | 6 +- lib/iris/tests/unit/coords/test_DimCoord.py | 12 ++ lib/iris/tests/unit/util/test_reverse.py | 104 ++++++++++-------- 10 files changed, 176 insertions(+), 76 deletions(-) create mode 100644 lib/iris/tests/integration/merge/__init__.py create mode 100644 lib/iris/tests/integration/merge/test_merge.py diff --git a/docs/src/whatsnew/dev.rst b/docs/src/whatsnew/dev.rst index 8cf0ac31cf..0307a8d82f 100644 --- a/docs/src/whatsnew/dev.rst +++ b/docs/src/whatsnew/dev.rst @@ -63,6 +63,14 @@ This document explains the changes made to Iris for this release cases where the requested point is float and the coordinate has integer bounds, reported at :issue:`2969`. (:pull:`4245`) +#. `@rcomer`_ modified bounds setting on :obj:`~iris.coords.DimCoord` instances + so that the order of the cell bounds is automatically reversed + to match the coordinate's direction if necessary. This is consistent with + the `Bounds for 1-D coordinate variables` subsection of the `Cell Boundaries`_ + section of the CF Conventions and ensures that contiguity is preserved if a + coordinate's direction is reversed. (:issue:`3249`, :issue:`423`, + :issue:`4078`, :issue:`3756`, :pull:`4466`) + 💣 Incompatible Changes ======================= @@ -123,4 +131,4 @@ This document explains the changes made to Iris for this release .. comment Whatsnew resources in alphabetical order: - +.. _Cell Boundaries: https://cfconventions.org/Data/cf-conventions/cf-conventions-1.9/cf-conventions.html#cell-boundaries diff --git a/lib/iris/coords.py b/lib/iris/coords.py index 8bcba776fd..3862c9057b 100644 --- a/lib/iris/coords.py +++ b/lib/iris/coords.py @@ -1360,7 +1360,9 @@ def __eq__(self, other): else: return self.point == other elif isinstance(other, Cell): - return (self.point == other.point) and (self.bound == other.bound) + return (self.point == other.point) and ( + self.bound == other.bound or self.bound == other.bound[::-1] + ) elif ( isinstance(other, str) and self.bound is None @@ -2805,6 +2807,10 @@ def _new_bounds_requirements(self, bounds): * bounds are not masked, and * bounds are monotonic in the first dimension. + Also reverse the order of the second dimension if necessary to match the + first dimension's direction. I.e. both should increase or both should + decrease. + """ # Ensure the bounds are a compatible shape. if self.shape != bounds.shape[:-1] and not ( @@ -2854,6 +2860,16 @@ def _new_bounds_requirements(self, bounds): emsg.format(self.name(), self.__class__.__name__) ) + if n_bounds == 2: + # Make ordering of bounds consistent with coord's direction + # if possible. + (direction,) = directions + diffs = bounds[:, 0] - bounds[:, 1] + if np.all(np.sign(diffs) == direction): + bounds = np.flip(bounds, axis=1) + + return bounds + @Coord.bounds.setter def bounds(self, bounds): if bounds is not None: @@ -2862,8 +2878,9 @@ def bounds(self, bounds): # Make sure we have an array (any type of array). bounds = np.asanyarray(bounds) - # Check validity requirements for dimension-coordinate bounds. - self._new_bounds_requirements(bounds) + # Check validity requirements for dimension-coordinate bounds and reverse + # trailing dimension if necessary. + bounds = self._new_bounds_requirements(bounds) # Cast to a numpy array for masked arrays with no mask. bounds = np.array(bounds) diff --git a/lib/iris/tests/integration/merge/__init__.py b/lib/iris/tests/integration/merge/__init__.py new file mode 100644 index 0000000000..9374976532 --- /dev/null +++ b/lib/iris/tests/integration/merge/__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. +"""Integration tests for the :mod:`iris._merge` package.""" diff --git a/lib/iris/tests/integration/merge/test_merge.py b/lib/iris/tests/integration/merge/test_merge.py new file mode 100644 index 0000000000..f5f92a7a7d --- /dev/null +++ b/lib/iris/tests/integration/merge/test_merge.py @@ -0,0 +1,37 @@ +# 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. +""" +Integration tests for merging cubes. + +""" + +# import iris tests first so that some things can be initialised +# before importing anything else. +import iris.tests as tests # isort:skip + +from iris.coords import DimCoord +from iris.cube import Cube, CubeList + + +class TestContiguous(tests.IrisTest): + def test_form_contiguous_dimcoord(self): + # Test that cube sliced up and remerged in the opposite order maintains + # contiguity. + cube1 = Cube([1, 2, 3], "air_temperature", units="K") + coord1 = DimCoord([3, 2, 1], long_name="spam") + coord1.guess_bounds() + cube1.add_dim_coord(coord1, 0) + cubes = CubeList(cube1.slices_over("spam")) + cube2 = cubes.merge_cube() + coord2 = cube2.coord("spam") + + self.assertTrue(coord2.is_contiguous()) + self.assertArrayEqual(coord2.points, [1, 2, 3]) + self.assertArrayEqual(coord2.bounds, coord1.bounds[::-1, ::-1]) + + +if __name__ == "__main__": + tests.main() diff --git a/lib/iris/tests/results/coord_api/intersection_reversed.xml b/lib/iris/tests/results/coord_api/intersection_reversed.xml index b966a09b54..b489f95451 100644 --- a/lib/iris/tests/results/coord_api/intersection_reversed.xml +++ b/lib/iris/tests/results/coord_api/intersection_reversed.xml @@ -1,9 +1,9 @@ - + diff --git a/lib/iris/tests/results/cube_slice/2d_intersect_and_reverse.cml b/lib/iris/tests/results/cube_slice/2d_intersect_and_reverse.cml index 3f9e5fef9e..f272cebeb1 100644 --- a/lib/iris/tests/results/cube_slice/2d_intersect_and_reverse.cml +++ b/lib/iris/tests/results/cube_slice/2d_intersect_and_reverse.cml @@ -9,15 +9,15 @@ - + - + Date: Sat, 2 Apr 2022 10:09:04 +0100 Subject: [PATCH 10/15] Updated environment lockfiles (#4677) Co-authored-by: Lockfile bot --- requirements/ci/nox.lock/py38-linux-64.lock | 392 ++++++++++---------- 1 file changed, 196 insertions(+), 196 deletions(-) diff --git a/requirements/ci/nox.lock/py38-linux-64.lock b/requirements/ci/nox.lock/py38-linux-64.lock index ae6c224e8a..b9a064b2d3 100644 --- a/requirements/ci/nox.lock/py38-linux-64.lock +++ b/requirements/ci/nox.lock/py38-linux-64.lock @@ -2,224 +2,224 @@ # platform: linux-64 # input_hash: d7bddc89ba289d4c1b48871b1289eb0bf45715ef2e274de01e245547fb6a8df6 @EXPLICIT -https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-qthelp-1.0.3-py_0.tar.bz2#d01180388e6d1838c3e1ad029590aa7a -https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-applehelp-1.0.2-py_0.tar.bz2#20b2eaeaeea4ef9a9a0d99770620fd09 -https://conda.anaconda.org/conda-forge/linux-64/ukkonen-1.0.1-py38h1fd1430_1.tar.bz2#c494f75082f9c052944fda1b22c83336 -https://conda.anaconda.org/conda-forge/noarch/fsspec-2022.2.0-pyhd8ed1ab_0.tar.bz2#f31e31092035d427b05233ab924c7613 -https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-13_linux64_openblas.tar.bz2#018b80e8f21d8560ae4961567e3e00c9 -https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.47.0-h727a467_0.tar.bz2#a22567abfea169ff8048506b1ca9b230 -https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.3.0-h542a066_3.tar.bz2#1a0efb4dfd880b0376da8e1ba39fa838 -https://conda.anaconda.org/conda-forge/linux-64/gettext-0.19.8.1-h73d1719_1008.tar.bz2#af49250eca8e139378f8ff0ae9e57251 -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/linux-64/expat-2.4.7-h27087fc_0.tar.bz2#b7fcfe2c2e21e5b696a98d774ac3f701 -https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.4-pyh9f0ad1d_0.tar.bz2#c08b4c1326b880ed44f3ffb04803332f -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/brotli-1.0.9-h7f98852_6.tar.bz2#612385c4a83edb0619fe911d9da317f4 -https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.0.3-he3ba5ed_0.tar.bz2#f9dbabc7e01c459ed7a1d1d64b206e9b -https://conda.anaconda.org/conda-forge/noarch/imagehash-4.2.1-pyhd8ed1ab_0.tar.bz2#01cc8698b6e1a124dc4f585516c27643 -https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.1-py38h0a891b7_0.tar.bz2#0d1a1c4a830d2bc306af194f4e4896e7 https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2#d7c89558ba9fa0495403155b64376d81 -https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-jsmath-1.0.1-py_0.tar.bz2#67cd9d9c0382d37479b4d306c369a2d4 -https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-13_linux64_openblas.tar.bz2#8a4038563ed92dfa622bd72c0d8f31d3 +https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2021.10.8-ha878542_0.tar.bz2#575611b8a84f45960e87722eeb51fa26 +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/noarch/identify-2.4.12-pyhd8ed1ab_0.tar.bz2#9f7b07b3f40905fcf600aacf63ae0c41 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/graphviz-3.0.0-h5abf519_1.tar.bz2#fcaf13b2713335ff871ba551d5bda679 +https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-11.2.0-h5c6108e_14.tar.bz2#f8b51fb7bccd48f959982973df578165 +https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-11.2.0-he4da1e4_14.tar.bz2#c3b55849172e4bfa01d3349ea8e73a3a +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-11.2.0-h69a702a_14.tar.bz2#67328431d4aeab35e5a2c6c22669c22b https://conda.anaconda.org/conda-forge/linux-64/libgomp-11.2.0-h1d223b6_14.tar.bz2#a77fb1a92411cb8d979de1c2d81dd210 -https://conda.anaconda.org/conda-forge/linux-64/xorg-renderproto-0.11.1-h7f98852_1002.tar.bz2#06feff3d2634e3097ce2fe681474b534 -https://conda.anaconda.org/conda-forge/linux-64/importlib-metadata-4.11.3-py38h578d9bd_0.tar.bz2#00bdc412edc320d1717d1f7156d8edfd -https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.37.1-h4ff8645_0.tar.bz2#8057ac02d6d10a162d7eb4b0ca7ed291 -https://conda.anaconda.org/conda-forge/linux-64/qt-5.12.9-h1304e3e_6.tar.bz2#f2985d160b8c43dd427923c04cd732fe -https://conda.anaconda.org/conda-forge/noarch/pip-22.0.4-pyhd8ed1ab_0.tar.bz2#b1239ce8ef2a1eec485c398a683c5bff -https://conda.anaconda.org/conda-forge/noarch/imagesize-1.3.0-pyhd8ed1ab_0.tar.bz2#be807e7606fff9436e5e700f6bffb7c6 -https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.5.8-nompi_py38h2823cc8_101.tar.bz2#1dfe1cdee4532c72f893955259eb3de9 +https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-1_gnu.tar.bz2#561e277319a41d4f24f5c05a9ef63c04 +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-11.2.0-h1d223b6_14.tar.bz2#47e6c01d149b26090748d9d1ac32491b +https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.3-h516909a_0.tar.bz2#1378b88874f42ac31b2f8e4f6975cb7b +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/fribidi-1.0.10-h36c2ea0_0.tar.bz2#ac7bc6a654f8f41b352b38f4051135f8 +https://conda.anaconda.org/conda-forge/linux-64/geos-3.10.2-h9c3ff4c_0.tar.bz2#fe9a66a351bfa7a84c3108304c7bcba5 +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-69.1-h9c3ff4c_0.tar.bz2#e0773c9556d588b062a4e1424a6a02fa +https://conda.anaconda.org/conda-forge/linux-64/jbig-2.1-h7f98852_2003.tar.bz2#1aa0cee79792fa97b7ff4545110b60bf +https://conda.anaconda.org/conda-forge/linux-64/jpeg-9e-h7f98852_0.tar.bz2#5c214edc675a7fb7cbb34b1d854e5141 +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/lerc-3.0-h9c3ff4c_0.tar.bz2#7fcefde484980d23f0ec24c11e314d2e +https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.0.9-h7f98852_6.tar.bz2#b0f44f63f7d771d7670747a1dd5d5ac1 https://conda.anaconda.org/conda-forge/linux-64/libdeflate-1.10-h7f98852_0.tar.bz2#ffa3a757a97e851293909b49f49f28fb -https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.31.2-py38h0a891b7_0.tar.bz2#381ffd61d2617af9bddb1cee60352480 -https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-14.0.0-py38h497a2fe_0.tar.bz2#8da7787169411910df2a62dc8ef533e0 -https://conda.anaconda.org/conda-forge/linux-64/virtualenv-20.13.4-py38h578d9bd_0.tar.bz2#ff64ee0548fdd8de60b0a6a0356e0f0d https://conda.anaconda.org/conda-forge/linux-64/libev-4.33-h516909a_1.tar.bz2#6f8720dff19e17ce5d48cfe7f3d2f0a3 -https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.12.1-mpi_mpich_h08b82f9_4.tar.bz2#975d5635b158c1b3c5c795f9d0a430a1 -https://conda.anaconda.org/conda-forge/linux-64/scipy-1.8.0-py38h56a6a73_1.tar.bz2#86073932d9e675c5929376f6f8b79b97 -https://conda.anaconda.org/conda-forge/linux-64/netcdf-fortran-4.5.4-mpi_mpich_h1364a43_0.tar.bz2#b6ba4f487ef9fd5d353ff277df06d133 -https://conda.anaconda.org/conda-forge/linux-64/libclang-13.0.1-default_hc23dcda_0.tar.bz2#8cebb0736cba83485b13dc10d242d96d -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/libcblas-3.9.0-13_linux64_openblas.tar.bz2#b17676dbd6688396c3a3076259fb7907 -https://conda.anaconda.org/conda-forge/linux-64/libpq-14.2-hd57d9b9_0.tar.bz2#91b38e297e1cc79f88f7cbf7bdb248e0 -https://conda.anaconda.org/conda-forge/noarch/distlib-0.3.4-pyhd8ed1ab_0.tar.bz2#7b50d840543d9cdae100e91582c33035 -https://conda.anaconda.org/conda-forge/noarch/snowballstemmer-2.2.0-pyhd8ed1ab_0.tar.bz2#4d22a9315e78c6827f806065957d566e +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/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.18-pthreads_h8fe5266_0.tar.bz2#41532e4448c0cce086d6570f95e4e12e 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/curl-7.82.0-h7bff187_0.tar.bz2#631777dcd00a2714cf0c415ffe40c480 -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/xorg-libsm-1.2.3-hd9c2040_1000.tar.bz2#9e856f78d5c80d5a78f61e72d1d473a3 -https://conda.anaconda.org/conda-forge/linux-64/pre-commit-2.17.0-py38h578d9bd_0.tar.bz2#839ac9dba9a6126c9532781a9ea4506b -https://conda.anaconda.org/conda-forge/noarch/packaging-21.3-pyhd8ed1ab_0.tar.bz2#71f1ab2de48613876becddd496371c85 +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/libuuid-2.32.1-h7f98852_1000.tar.bz2#772d69f030955d9646d3d0eaf21d859d +https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.2.2-h7f98852_1.tar.bz2#46cf26ecc8775a0aab300ea1821aaa3c +https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.2.11-h166bdaf_1014.tar.bz2#757138ba3ddc6777b82e91d9ff62e7b9 +https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.9.3-h9c3ff4c_1.tar.bz2#fbe97e8fa6f275d7c76a09e795adc3e6 +https://conda.anaconda.org/conda-forge/linux-64/mpich-4.0.1-h846660c_100.tar.bz2#4b85205b094808088bb0862e08251653 +https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.3-h9c3ff4c_0.tar.bz2#fb31bcb7af058244479ca635d20f0f4a +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.1n-h166bdaf_0.tar.bz2#cf0ddbd911fa547e6bc4291ca5e17379 +https://conda.anaconda.org/conda-forge/linux-64/pcre-8.45-h9c3ff4c_0.tar.bz2#c05d1820a6d34ff07aaaab7a9b7eddaa +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 https://conda.anaconda.org/conda-forge/linux-64/xorg-libice-1.0.10-h7f98852_0.tar.bz2#d6b0b50b49eccfe0be0373be628be0f3 -https://conda.anaconda.org/conda-forge/linux-64/setuptools-61.0.0-py38h578d9bd_0.tar.bz2#f22d6162248bb633b199c495268d8fc4 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.9-h7f98852_0.tar.bz2#bf6f803a544f26ebbdc3bfff272eb179 +https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.3-h7f98852_0.tar.bz2#be93aabceefa2fac576e971aef407908 +https://conda.anaconda.org/conda-forge/linux-64/xorg-renderproto-0.11.1-h7f98852_1002.tar.bz2#06feff3d2634e3097ce2fe681474b534 +https://conda.anaconda.org/conda-forge/linux-64/xorg-xextproto-7.3.0-h7f98852_1002.tar.bz2#1e15f6ad85a7d743a2ac68dae6c82b98 +https://conda.anaconda.org/conda-forge/linux-64/xorg-xproto-7.0.31-h7f98852_1007.tar.bz2#b4a4381d54784606820704f7b5f05a15 +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.5-h516909a_1.tar.bz2#33f601066901f3e1a85af3522a8113f9 +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/libblas-3.9.0-13_linux64_openblas.tar.bz2#8a4038563ed92dfa622bd72c0d8f31d3 +https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.0.9-h7f98852_6.tar.bz2#c7c03a2592cac92246a13a0732bd1573 +https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.0.9-h7f98852_6.tar.bz2#28bfe0a70154e6881da7bae97517c948 +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/libllvm13-13.0.1-hf817b99_2.tar.bz2#47da3ce0d8b2e65ccb226c186dd91eba +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/mysql-common-8.0.28-haf5c9bc_2.tar.bz2#3593de3e1062c658062a53bf3874ff41 https://conda.anaconda.org/conda-forge/linux-64/readline-8.1-h46c0cb4_0.tar.bz2#5788de3c8d7a7d64ac56c784c4ef48e6 +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.11-h166bdaf_1014.tar.bz2#def3b82d1a03aa695bb38ac1dd072ff2 +https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.2-ha95c52a_0.tar.bz2#5222b231b1ef49a7f60d40b363469b70 +https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.0.9-h7f98852_6.tar.bz2#9e94bf16f14c78a36561d5019f490d22 +https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h10796ff_3.tar.bz2#21a8d66dc17f065023b33145c42652fe +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-13_linux64_openblas.tar.bz2#b17676dbd6688396c3a3076259fb7907 +https://conda.anaconda.org/conda-forge/linux-64/libclang-13.0.1-default_hc23dcda_0.tar.bz2#8cebb0736cba83485b13dc10d242d96d +https://conda.anaconda.org/conda-forge/linux-64/libglib-2.70.2-h174f98d_4.tar.bz2#d44314ffae96b17657fbf3f8e47b04fc +https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-13_linux64_openblas.tar.bz2#018b80e8f21d8560ae4961567e3e00c9 +https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.47.0-h727a467_0.tar.bz2#a22567abfea169ff8048506b1ca9b230 +https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.37-h21135ba_2.tar.bz2#b6acf807307d033d4b7e758b4f44b036 +https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.10.0-ha56f1ee_2.tar.bz2#6ab4eaa11ff01801cffca0a27489dc04 +https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.3.0-h542a066_3.tar.bz2#1a0efb4dfd880b0376da8e1ba39fa838 +https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.9.12-h885dcf4_1.tar.bz2#d1355eaa48f465782f228275a0a69771 +https://conda.anaconda.org/conda-forge/linux-64/libzip-1.8.0-h4de3113_1.tar.bz2#175a746a43d42c053b91aa765fbc197d +https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-8.0.28-h28c427c_2.tar.bz2#bc001857cad0d2859f1507f420691bcc +https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.37.1-h4ff8645_0.tar.bz2#8057ac02d6d10a162d7eb4b0ca7ed291 +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-h7f98852_6.tar.bz2#612385c4a83edb0619fe911d9da317f4 +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/freetype-2.10.4-h0708190_1.tar.bz2#4a06f2ac2e5bfae7b6b245171c3f07aa -https://conda.anaconda.org/conda-forge/noarch/alabaster-0.7.12-py_0.tar.bz2#2489a97287f90176ecdc3ca982b4b0a0 +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/gstreamer-1.20.1-hd4edc92_1.tar.bz2#ebebb56f78dd7518dcc94d6c26aa2249 +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/libcurl-7.82.0-h7bff187_0.tar.bz2#fa26f833ca8796ad44f9561c5402013d -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/xorg-libx11-1.7.2-h7f98852_0.tar.bz2#12a61e640b8894504326aadafccbb790 -https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.0.9-h7f98852_6.tar.bz2#b0f44f63f7d771d7670747a1dd5d5ac1 -https://conda.anaconda.org/conda-forge/linux-64/mpi-1.0-mpich.tar.bz2#c1fcff3417b5a22bbc4cf6e8c23648cf -https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.32.1-h7f98852_1000.tar.bz2#772d69f030955d9646d3d0eaf21d859d -https://conda.anaconda.org/conda-forge/noarch/six-1.16.0-pyh6c4a22f_0.tar.bz2#e5f25f8dbc060e9a8d912e432202afc2 -https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.0.9-h7f98852_6.tar.bz2#9e94bf16f14c78a36561d5019f490d22 +https://conda.anaconda.org/conda-forge/linux-64/libpq-14.2-hd57d9b9_0.tar.bz2#91b38e297e1cc79f88f7cbf7bdb248e0 +https://conda.anaconda.org/conda-forge/linux-64/libwebp-1.2.2-h3452ae3_0.tar.bz2#c363665b4aabe56aae4f8981cff5b153 +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/nss-3.77-h2350873_0.tar.bz2#260617b7829b86e9e939b01c9cad1526 +https://conda.anaconda.org/conda-forge/linux-64/python-3.8.13-h582c2e5_0_cpython.tar.bz2#8ec74710472994e2411a8020fa8589ce +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/alabaster-0.7.12-py_0.tar.bz2#2489a97287f90176ecdc3ca982b4b0a0 +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.0.12-pyhd8ed1ab_0.tar.bz2#1f5b32dabae0f1893ae3283dac7f799e +https://conda.anaconda.org/conda-forge/noarch/cloudpickle-2.0.0-pyhd8ed1ab_0.tar.bz2#3a8fc8b627d5fb6af827e126a10a86c6 +https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.4-pyh9f0ad1d_0.tar.bz2#c08b4c1326b880ed44f3ffb04803332f +https://conda.anaconda.org/conda-forge/linux-64/curl-7.82.0-h7bff187_0.tar.bz2#631777dcd00a2714cf0c415ffe40c480 +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.4-pyhd8ed1ab_0.tar.bz2#7b50d840543d9cdae100e91582c33035 +https://conda.anaconda.org/conda-forge/noarch/filelock-3.6.0-pyhd8ed1ab_0.tar.bz2#6e03ca6c7b47a4152a2b12c6eee3bd32 +https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.13.96-h8e229c2_2.tar.bz2#70d2ba376dff51a33343169642aebb44 +https://conda.anaconda.org/conda-forge/noarch/fsspec-2022.3.0-pyhd8ed1ab_0.tar.bz2#960b78685ccbedb992886fc4ce37bf6e +https://conda.anaconda.org/conda-forge/linux-64/gst-plugins-base-1.20.1-hcf0ee16_1.tar.bz2#4c41fc47db7d631862f12e5e5c885cb9 +https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.12.1-mpi_mpich_h08b82f9_4.tar.bz2#975d5635b158c1b3c5c795f9d0a430a1 +https://conda.anaconda.org/conda-forge/noarch/idna-3.3-pyhd8ed1ab_0.tar.bz2#40b50b8b030f5f2f22085c062ed013dd +https://conda.anaconda.org/conda-forge/noarch/imagesize-1.3.0-pyhd8ed1ab_0.tar.bz2#be807e7606fff9436e5e700f6bffb7c6 +https://conda.anaconda.org/conda-forge/noarch/iris-sample-data-2.4.0-pyhd8ed1ab_0.tar.bz2#18ee9c07cf945a33f92caf1ee3d23ad9 +https://conda.anaconda.org/conda-forge/noarch/locket-0.2.0-py_2.tar.bz2#709e8671651c7ec3d1ad07800339ff1d +https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2#2ba8498c1018c1e9c61eb99b973dfe19 +https://conda.anaconda.org/conda-forge/noarch/nose-1.3.7-py_1006.tar.bz2#382019d5f8e9362ef6f60a8d4e7bce8f +https://conda.anaconda.org/conda-forge/noarch/olefile-0.46-pyh9f0ad1d_1.tar.bz2#0b2e68acc8c78c8cc392b90983481f58 +https://conda.anaconda.org/conda-forge/noarch/platformdirs-2.5.1-pyhd8ed1ab_0.tar.bz2#d5df87964a39f67c46a5448f4e78d9b6 https://conda.anaconda.org/conda-forge/linux-64/proj-9.0.0-h93bde94_1.tar.bz2#cf908994f24ea526afc59f295d5b07c1 -https://conda.anaconda.org/conda-forge/linux-64/pysocks-1.7.1-py38h578d9bd_4.tar.bz2#9c4bbee6f682f2fc7d7803df3996e77e -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/esmf-8.2.0-mpi_mpich_h4975321_100.tar.bz2#56f5c650937b1667ad0a557a0dff3bc4 -https://conda.anaconda.org/conda-forge/noarch/fonts-conda-ecosystem-1-0.tar.bz2#fee5683a3f04bd15cbd8318b096a27ab -https://conda.anaconda.org/conda-forge/linux-64/libglib-2.70.2-h174f98d_4.tar.bz2#d44314ffae96b17657fbf3f8e47b04fc -https://conda.anaconda.org/conda-forge/linux-64/pixman-0.40.0-h36c2ea0_0.tar.bz2#660e72c82f2e75a6b3fe6a6e75c79f19 -https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.6.0-pyhd8ed1ab_0.tar.bz2#0941325bf48969e2b3b19d0951740950 -https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.9.3-h9c3ff4c_1.tar.bz2#fbe97e8fa6f275d7c76a09e795adc3e6 -https://conda.anaconda.org/conda-forge/linux-64/xz-5.2.5-h516909a_1.tar.bz2#33f601066901f3e1a85af3522a8113f9 -https://conda.anaconda.org/conda-forge/linux-64/libllvm13-13.0.1-hf817b99_2.tar.bz2#47da3ce0d8b2e65ccb226c186dd91eba -https://conda.anaconda.org/conda-forge/noarch/sphinx-4.4.0-pyh6c4a22f_1.tar.bz2#a9025d14c2a609e0d895ad3e75b5369c +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.7-pyhd8ed1ab_0.tar.bz2#727e2216d9c47455d8ddc060eb2caad9 +https://conda.anaconda.org/conda-forge/noarch/pyshp-2.2.0-pyhd8ed1ab_0.tar.bz2#2aa546be05be34b8e1744afd327b623f +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.1-pyhd8ed1ab_0.tar.bz2#b87d66d6d3991d988fb31510c95a9267 +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/sphinxcontrib-applehelp-1.0.2-py_0.tar.bz2#20b2eaeaeea4ef9a9a0d99770620fd09 +https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-devhelp-1.0.2-py_0.tar.bz2#68e01cac9d38d0e717cd5c87bc3d2cc9 +https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-htmlhelp-2.0.0-pyhd8ed1ab_0.tar.bz2#77dad82eb9c8c1525ff7953e0756d708 +https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-jsmath-1.0.1-py_0.tar.bz2#67cd9d9c0382d37479b4d306c369a2d4 +https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-qthelp-1.0.3-py_0.tar.bz2#d01180388e6d1838c3e1ad029590aa7a +https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_0.tar.bz2#f832c45a477c78bebd107098db465095 +https://conda.anaconda.org/conda-forge/noarch/toolz-0.11.2-pyhd8ed1ab_0.tar.bz2#f348d1590550371edfac5ed3c1d44f7e https://conda.anaconda.org/conda-forge/noarch/wheel-0.37.1-pyhd8ed1ab_0.tar.bz2#1ca02aaf78d9c70d9a81a3bed5752022 -https://conda.anaconda.org/conda-forge/noarch/partd-1.2.0-pyhd8ed1ab_0.tar.bz2#0c32f563d7f22e3a34c95cad8cc95651 -https://conda.anaconda.org/conda-forge/noarch/requests-2.27.1-pyhd8ed1ab_0.tar.bz2#7c1c427246b057b8fa97200ecdb2ed62 -https://conda.anaconda.org/conda-forge/linux-64/certifi-2021.10.8-py38h578d9bd_1.tar.bz2#52a6cee65a5d10ed1c3f0af24fb48dd3 +https://conda.anaconda.org/conda-forge/noarch/zipp-3.7.0-pyhd8ed1ab_1.tar.bz2#b689b2cbc8481b224777415e1a193170 +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.9.1-pyh44b312d_0.tar.bz2#74136ed39bfea0832d338df1e58d013e +https://conda.anaconda.org/conda-forge/linux-64/cairo-1.16.0-ha12eb4b_1010.tar.bz2#e15c0969bf37df9dae513a48ac871a7d +https://conda.anaconda.org/conda-forge/linux-64/certifi-2021.10.8-py38h578d9bd_2.tar.bz2#63a01bce71bc3e8c8e0510ed997d1458 +https://conda.anaconda.org/conda-forge/linux-64/cffi-1.15.0-py38h3931269_0.tar.bz2#9c491a90ae11d08ca97326a0ed876f3a +https://conda.anaconda.org/conda-forge/linux-64/docutils-0.16-py38h578d9bd_3.tar.bz2#a7866449fb9e5e4008a02df276549d34 +https://conda.anaconda.org/conda-forge/linux-64/importlib-metadata-4.11.3-py38h578d9bd_1.tar.bz2#21862e22238907d485e460f9c2e9ae4d +https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.2-py38h43d8883_0.tar.bz2#f6962263e6503aed8b6ea087d8cc892e +https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-h283352f_2.tar.bz2#2b0d39005a2e8347f329fe578bd6488a https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.8.1-mpi_mpich_h319fa22_1.tar.bz2#7583fbaea3648f692c0c019254bc196c -https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.3.0-py38hbc0797c_2.tar.bz2#7e4c695d10aa5e4576e87fb00a9d2511 -https://conda.anaconda.org/conda-forge/noarch/sphinx-gallery-0.10.1-pyhd8ed1ab_0.tar.bz2#4918585fe5e5341740f7e63c61743efb -https://conda.anaconda.org/conda-forge/noarch/fonts-conda-forge-1-0.tar.bz2#f766549260d6815b0c52253f1fb1bb29 -https://conda.anaconda.org/conda-forge/noarch/nc-time-axis-1.4.0-pyhd8ed1ab_0.tar.bz2#9113b4e4fa2fa4a7f129c71a6f319475 -https://conda.anaconda.org/conda-forge/linux-64/pango-1.50.6-hbd2fdc8_0.tar.bz2#129c70116050ccf66b362ca3203fb660 -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/markupsafe-2.1.1-py38h0a891b7_1.tar.bz2#20d003ad5f584e212c299f64cac46c05 +https://conda.anaconda.org/conda-forge/linux-64/mpi4py-3.1.3-py38he865349_0.tar.bz2#b1b3d6847a68251a1465206ab466b475 +https://conda.anaconda.org/conda-forge/linux-64/numpy-1.22.3-py38h05e7239_0.tar.bz2#90b4ee61abb81fb3f3995ec9d4c734f0 +https://conda.anaconda.org/conda-forge/noarch/packaging-21.3-pyhd8ed1ab_0.tar.bz2#71f1ab2de48613876becddd496371c85 +https://conda.anaconda.org/conda-forge/noarch/partd-1.2.0-pyhd8ed1ab_0.tar.bz2#0c32f563d7f22e3a34c95cad8cc95651 https://conda.anaconda.org/conda-forge/linux-64/pillow-6.2.1-py38hd70f55b_1.tar.bz2#80d719bee2b77a106b199150c0829107 -https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.0.9-h7f98852_6.tar.bz2#28bfe0a70154e6881da7bae97517c948 -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/linux-64/zlib-1.2.11-h36c2ea0_1013.tar.bz2#cf7190238072a41e9579e4476a6a60b8 -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/mo_pack-0.2.0-py38h6c62de6_1006.tar.bz2#829b1209dfadd431a11048d6eeaf5bef -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/libxcb-1.13-h7f98852_1004.tar.bz2#b3653fdc58d03face9724f602218a904 -https://conda.anaconda.org/conda-forge/noarch/pyparsing-3.0.7-pyhd8ed1ab_0.tar.bz2#727e2216d9c47455d8ddc060eb2caad9 -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/geos-3.10.2-h9c3ff4c_0.tar.bz2#fe9a66a351bfa7a84c3108304c7bcba5 -https://conda.anaconda.org/conda-forge/linux-64/pthread-stubs-0.4-h36c2ea0_1001.tar.bz2#22dad4df6e8630e8dff2428f6f6a7036 -https://conda.anaconda.org/conda-forge/noarch/zipp-3.7.0-pyhd8ed1ab_1.tar.bz2#b689b2cbc8481b224777415e1a193170 -https://conda.anaconda.org/conda-forge/linux-64/cartopy-0.20.2-py38h51d8e34_4.tar.bz2#9f23c80d08456c02ab284f974719b013 +https://conda.anaconda.org/conda-forge/noarch/pockets-0.9.1-py_0.tar.bz2#1b52f0c42e8077e5a33e00fe72269364 +https://conda.anaconda.org/conda-forge/linux-64/pyqt5-sip-4.19.18-py38h709712a_8.tar.bz2#11b72f5b1cc15427c89232321172a0bc +https://conda.anaconda.org/conda-forge/linux-64/pysocks-1.7.1-py38h578d9bd_4.tar.bz2#9c4bbee6f682f2fc7d7803df3996e77e +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_0.tar.bz2#12eaa8cbfedfbf7879e5653467b03c94 +https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0-py38h0a891b7_4.tar.bz2#ba24ff01bb38c5cd5be54b45ef685db3 +https://conda.anaconda.org/conda-forge/linux-64/qt-5.12.9-h1304e3e_6.tar.bz2#f2985d160b8c43dd427923c04cd732fe +https://conda.anaconda.org/conda-forge/linux-64/setuptools-61.3.0-py38h578d9bd_0.tar.bz2#9d042d3e103851e2f59433313ff84df4 +https://conda.anaconda.org/conda-forge/linux-64/tornado-6.1-py38h0a891b7_3.tar.bz2#d9e2836a4a46935f84b858462d54a7c3 +https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-14.0.0-py38h497a2fe_0.tar.bz2#8da7787169411910df2a62dc8ef533e0 +https://conda.anaconda.org/conda-forge/linux-64/virtualenv-20.14.0-py38h578d9bd_1.tar.bz2#6c6a5da4d7e2af1dbbfb1e21daa559cc +https://conda.anaconda.org/conda-forge/linux-64/brotlipy-0.7.0-py38h497a2fe_1003.tar.bz2#9189b42c42b9c87b2b2068cbe31901a8 https://conda.anaconda.org/conda-forge/linux-64/cftime-1.6.0-py38h3ec907f_0.tar.bz2#35411e5fc8dd523f9e68316847e6a25b +https://conda.anaconda.org/conda-forge/linux-64/cryptography-36.0.2-py38h2b5fc30_0.tar.bz2#22274a82cf664d94a9e7c8f9deddf52a +https://conda.anaconda.org/conda-forge/noarch/dask-core-2022.4.0-pyhd8ed1ab_0.tar.bz2#a47051950a34bef40d8369a5f320b78d +https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.31.2-py38h0a891b7_0.tar.bz2#381ffd61d2617af9bddb1cee60352480 +https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-4.2.0-h40b6f09_0.tar.bz2#017b20e7e98860f0bfa7492ce16390a7 +https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.1-pyhd8ed1ab_0.tar.bz2#40b3f446c61729cdd21ed9d85627df6e +https://conda.anaconda.org/conda-forge/linux-64/mo_pack-0.2.0-py38h6c62de6_1006.tar.bz2#829b1209dfadd431a11048d6eeaf5bef +https://conda.anaconda.org/conda-forge/linux-64/netcdf-fortran-4.5.4-mpi_mpich_h1364a43_0.tar.bz2#b6ba4f487ef9fd5d353ff277df06d133 +https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.6.0-pyhd8ed1ab_0.tar.bz2#0941325bf48969e2b3b19d0951740950 +https://conda.anaconda.org/conda-forge/linux-64/pandas-1.4.1-py38h43a58ef_0.tar.bz2#1083ebe2edc30e4fb9568d1f66e3588b +https://conda.anaconda.org/conda-forge/noarch/pip-22.0.4-pyhd8ed1ab_0.tar.bz2#b1239ce8ef2a1eec485c398a683c5bff https://conda.anaconda.org/conda-forge/noarch/pygments-2.11.2-pyhd8ed1ab_0.tar.bz2#caef60540e2239e27bf62569a5015e3b -https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.2-ha95c52a_0.tar.bz2#5222b231b1ef49a7f60d40b363469b70 -https://conda.anaconda.org/conda-forge/linux-64/libwebp-1.2.2-h3452ae3_0.tar.bz2#c363665b4aabe56aae4f8981cff5b153 -https://conda.anaconda.org/conda-forge/noarch/munkres-1.1.4-pyh9f0ad1d_0.tar.bz2#2ba8498c1018c1e9c61eb99b973dfe19 -https://conda.anaconda.org/conda-forge/linux-64/jpeg-9e-h7f98852_0.tar.bz2#5c214edc675a7fb7cbb34b1d854e5141 -https://conda.anaconda.org/conda-forge/linux-64/gstreamer-1.20.1-hd4edc92_1.tar.bz2#ebebb56f78dd7518dcc94d6c26aa2249 -https://conda.anaconda.org/conda-forge/noarch/sphinx-copybutton-0.5.0-pyhd8ed1ab_0.tar.bz2#4c969cdd5191306c269490f7ff236d9c -https://conda.anaconda.org/conda-forge/noarch/idna-3.3-pyhd8ed1ab_0.tar.bz2#40b50b8b030f5f2f22085c062ed013dd -https://conda.anaconda.org/conda-forge/linux-64/cairo-1.16.0-ha12eb4b_1010.tar.bz2#e15c0969bf37df9dae513a48ac871a7d -https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0-py38h497a2fe_3.tar.bz2#131de7d638aa59fb8afbce59f1a8aa98 -https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.37-h21135ba_2.tar.bz2#b6acf807307d033d4b7e758b4f44b036 -https://conda.anaconda.org/conda-forge/linux-64/pywavelets-1.3.0-py38h3ec907f_0.tar.bz2#3562860c28f129d73c4431cb69a13ce1 -https://conda.anaconda.org/conda-forge/linux-64/libedit-3.1.20191231-he28a2e2_2.tar.bz2#4d331e44109e3f0e19b4cb8f9b82f3e1 -https://conda.anaconda.org/conda-forge/noarch/olefile-0.46-pyh9f0ad1d_1.tar.bz2#0b2e68acc8c78c8cc392b90983481f58 -https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.0-py38h43d8883_0.tar.bz2#e1d977faa8e5cec62eaf960cdf83d2b5 +https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.3.0-py38hbc0797c_2.tar.bz2#7e4c695d10aa5e4576e87fb00a9d2511 https://conda.anaconda.org/conda-forge/linux-64/pyqt-impl-5.12.3-py38h0ffb2e6_8.tar.bz2#acfc7625a212c27f7decdca86fdb2aba -https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-h283352f_2.tar.bz2#2b0d39005a2e8347f329fe578bd6488a https://conda.anaconda.org/conda-forge/linux-64/python-stratify-0.2.post0-py38h3ec907f_1.tar.bz2#4cf16ae4d975b6935582ab87dba69f0e -https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-1_gnu.tar.bz2#561e277319a41d4f24f5c05a9ef63c04 -https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-devhelp-1.0.2-py_0.tar.bz2#68e01cac9d38d0e717cd5c87bc3d2cc9 -https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.0.0-py38h0a891b7_0.tar.bz2#12eaa8cbfedfbf7879e5653467b03c94 -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/python-3.8.13-h582c2e5_0_cpython.tar.bz2#8ec74710472994e2411a8020fa8589ce -https://conda.anaconda.org/conda-forge/noarch/urllib3-1.26.9-pyhd8ed1ab_0.tar.bz2#0ea179ee251aa7100807c35bc0252693 -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/nss-3.76-h2350873_0.tar.bz2#0ecbc685e45dfa8874a68f2cc832d9d1 -https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.3-h9c3ff4c_0.tar.bz2#fb31bcb7af058244479ca635d20f0f4a -https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.0.9-h7f98852_6.tar.bz2#c7c03a2592cac92246a13a0732bd1573 -https://conda.anaconda.org/conda-forge/noarch/dask-core-2022.3.0-pyhd8ed1ab_0.tar.bz2#f428eca4a3c0f1bc39ca6c8f2ae53611 -https://conda.anaconda.org/conda-forge/linux-64/gst-plugins-base-1.20.1-hcf0ee16_1.tar.bz2#4c41fc47db7d631862f12e5e5c885cb9 -https://conda.anaconda.org/conda-forge/linux-64/cf-units-3.0.1-py38h6c62de6_2.tar.bz2#350322b046c129e5802b79358a1343f7 -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/gts-0.7.6-h64030ff_2.tar.bz2#112eb9b5b93f0c02e59aea4fd1967363 -https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h7f98852_2.tar.bz2#4cb3ad778ec2d5a7acbdf254eb1c42ae -https://conda.anaconda.org/conda-forge/noarch/platformdirs-2.5.1-pyhd8ed1ab_0.tar.bz2#d5df87964a39f67c46a5448f4e78d9b6 -https://conda.anaconda.org/conda-forge/linux-64/ca-certificates-2021.10.8-ha878542_0.tar.bz2#575611b8a84f45960e87722eeb51fa26 +https://conda.anaconda.org/conda-forge/linux-64/pywavelets-1.3.0-py38h3ec907f_0.tar.bz2#3562860c28f129d73c4431cb69a13ce1 +https://conda.anaconda.org/conda-forge/linux-64/scipy-1.8.0-py38h56a6a73_1.tar.bz2#86073932d9e675c5929376f6f8b79b97 https://conda.anaconda.org/conda-forge/linux-64/shapely-1.8.0-py38h596eeab_5.tar.bz2#ec3b783081e14a9dc0eb5ce609649728 -https://conda.anaconda.org/conda-forge/linux-64/openssl-1.1.1n-h166bdaf_0.tar.bz2#cf0ddbd911fa547e6bc4291ca5e17379 -https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h10796ff_3.tar.bz2#21a8d66dc17f065023b33145c42652fe -https://conda.anaconda.org/conda-forge/noarch/sphinx_rtd_theme-1.0.0-pyhd8ed1ab_0.tar.bz2#9f633f2f2869184e31acfeae95b24345 https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-napoleon-0.7-py_0.tar.bz2#0bc25ff6f2e34af63ded59692df5f749 -https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-11.2.0-he4da1e4_14.tar.bz2#c3b55849172e4bfa01d3349ea8e73a3a -https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-4.1.0-h40b6f09_0.tar.bz2#567de1f208bd0547b37d4ea9760606c0 -https://conda.anaconda.org/conda-forge/linux-64/docutils-0.16-py38h578d9bd_3.tar.bz2#a7866449fb9e5e4008a02df276549d34 -https://conda.anaconda.org/conda-forge/linux-64/libzip-1.8.0-h4de3113_1.tar.bz2#175a746a43d42c053b91aa765fbc197d -https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.13.96-h8e229c2_2.tar.bz2#70d2ba376dff51a33343169642aebb44 -https://conda.anaconda.org/conda-forge/linux-64/pyqtwebengine-5.12.1-py38h7400c14_8.tar.bz2#857894ea9c5e53c962c3a0932efa71ea -https://conda.anaconda.org/conda-forge/noarch/filelock-3.6.0-pyhd8ed1ab_0.tar.bz2#6e03ca6c7b47a4152a2b12c6eee3bd32 -https://conda.anaconda.org/conda-forge/noarch/sphinx-panels-0.6.0-pyhd8ed1ab_0.tar.bz2#6eec6480601f5d15babf9c3b3987f34a -https://conda.anaconda.org/conda-forge/linux-64/cryptography-36.0.2-py38h2b5fc30_0.tar.bz2#22274a82cf664d94a9e7c8f9deddf52a -https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.18-pthreads_h8fe5266_0.tar.bz2#41532e4448c0cce086d6570f95e4e12e -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/xorg-kbproto-1.0.7-h7f98852_1002.tar.bz2#4b230e8381279d76131116660f5a241a -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/pandas-1.4.1-py38h43a58ef_0.tar.bz2#1083ebe2edc30e4fb9568d1f66e3588b -https://conda.anaconda.org/conda-forge/noarch/pockets-0.9.1-py_0.tar.bz2#1b52f0c42e8077e5a33e00fe72269364 -https://conda.anaconda.org/conda-forge/linux-64/xxhash-0.8.0-h7f98852_3.tar.bz2#52402c791f35e414e704b7a113f99605 -https://conda.anaconda.org/conda-forge/noarch/cloudpickle-2.0.0-pyhd8ed1ab_0.tar.bz2#3a8fc8b627d5fb6af827e126a10a86c6 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxau-1.0.9-h7f98852_0.tar.bz2#bf6f803a544f26ebbdc3bfff272eb179 -https://conda.anaconda.org/conda-forge/linux-64/libvorbis-1.3.7-h9c3ff4c_0.tar.bz2#309dec04b70a3cc0f1e84a4013683bc0 -https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6 -https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-11.2.0-h5c6108e_14.tar.bz2#f8b51fb7bccd48f959982973df578165 -https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.2.11-h36c2ea0_1013.tar.bz2#dcddf696ff5dfcab567100d691678e18 -https://conda.anaconda.org/conda-forge/linux-64/xorg-libxdmcp-1.1.3-h7f98852_0.tar.bz2#be93aabceefa2fac576e971aef407908 -https://conda.anaconda.org/conda-forge/linux-64/xorg-xextproto-7.3.0-h7f98852_1002.tar.bz2#1e15f6ad85a7d743a2ac68dae6c82b98 -https://conda.anaconda.org/conda-forge/linux-64/libgcc-ng-11.2.0-h1d223b6_14.tar.bz2#47e6c01d149b26090748d9d1ac32491b -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/ukkonen-1.0.1-py38h1fd1430_1.tar.bz2#c494f75082f9c052944fda1b22c83336 +https://conda.anaconda.org/conda-forge/linux-64/cf-units-3.0.1-py38h6c62de6_2.tar.bz2#350322b046c129e5802b79358a1343f7 +https://conda.anaconda.org/conda-forge/linux-64/esmf-8.2.0-mpi_mpich_h4975321_100.tar.bz2#56f5c650937b1667ad0a557a0dff3bc4 +https://conda.anaconda.org/conda-forge/noarch/identify-2.4.12-pyhd8ed1ab_0.tar.bz2#9f7b07b3f40905fcf600aacf63ae0c41 +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.1-py38hf4fb855_0.tar.bz2#47cf0cab2ae368e1062e75cfbc4277af +https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.5.8-nompi_py38h2823cc8_101.tar.bz2#1dfe1cdee4532c72f893955259eb3de9 +https://conda.anaconda.org/conda-forge/linux-64/pango-1.50.6-hbd2fdc8_0.tar.bz2#129c70116050ccf66b362ca3203fb660 https://conda.anaconda.org/conda-forge/noarch/pyopenssl-22.0.0-pyhd8ed1ab_0.tar.bz2#1d7e241dfaf5475e893d4b824bb71b44 -https://conda.anaconda.org/conda-forge/linux-64/jbig-2.1-h7f98852_2003.tar.bz2#1aa0cee79792fa97b7ff4545110b60bf -https://conda.anaconda.org/conda-forge/linux-64/brotlipy-0.7.0-py38h497a2fe_1003.tar.bz2#9189b42c42b9c87b2b2068cbe31901a8 -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/nspr-4.32-h9c3ff4c_1.tar.bz2#29ded371806431b0499aaee146abfc3e +https://conda.anaconda.org/conda-forge/linux-64/pyqtchart-5.12-py38h7400c14_8.tar.bz2#78a2a6cb4ef31f997c1bee8223a9e579 +https://conda.anaconda.org/conda-forge/linux-64/pyqtwebengine-5.12.1-py38h7400c14_8.tar.bz2#857894ea9c5e53c962c3a0932efa71ea +https://conda.anaconda.org/conda-forge/linux-64/cartopy-0.20.2-py38h51d8e34_4.tar.bz2#9f23c80d08456c02ab284f974719b013 +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/gtk2-2.24.33-h90689f9_2.tar.bz2#957a0255ab58aaf394a91725d73ab422 https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.52.5-h0a9e6e8_2.tar.bz2#aa768fdaad03509a97df37f81163346b -https://conda.anaconda.org/conda-forge/linux-64/mysql-common-8.0.28-ha770c72_0.tar.bz2#56594fdd5a80774a80d546fbbccf2c03 -https://conda.anaconda.org/conda-forge/linux-64/pcre-8.45-h9c3ff4c_0.tar.bz2#c05d1820a6d34ff07aaaab7a9b7eddaa -https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.9.12-h885dcf4_1.tar.bz2#d1355eaa48f465782f228275a0a69771 -https://conda.anaconda.org/conda-forge/noarch/cycler-0.11.0-pyhd8ed1ab_0.tar.bz2#a50559fad0affdbb33729a68669ca1cb -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/antlr-python-runtime-4.7.2-py38h578d9bd_1003.tar.bz2#db8b471d9a764f561a129f94ea215c0a -https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_0.tar.bz2#f832c45a477c78bebd107098db465095 -https://conda.anaconda.org/conda-forge/linux-64/tornado-6.1-py38h497a2fe_2.tar.bz2#63b3b55c98b4239134e0be080f448944 -https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.5.1-py38h578d9bd_0.tar.bz2#0d78be9cf1c400ba8e3077cf060492f1 -https://conda.anaconda.org/conda-forge/linux-64/icu-69.1-h9c3ff4c_0.tar.bz2#e0773c9556d588b062a4e1424a6a02fa -https://conda.anaconda.org/conda-forge/linux-64/lerc-3.0-h9c3ff4c_0.tar.bz2#7fcefde484980d23f0ec24c11e314d2e -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/mpich-4.0.1-h846660c_100.tar.bz2#4b85205b094808088bb0862e08251653 -https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-2.0.12-pyhd8ed1ab_0.tar.bz2#1f5b32dabae0f1893ae3283dac7f799e -https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.5.1-py38hf4fb855_0.tar.bz2#47cf0cab2ae368e1062e75cfbc4277af -https://conda.anaconda.org/conda-forge/linux-64/numpy-1.22.3-py38h05e7239_0.tar.bz2#90b4ee61abb81fb3f3995ec9d4c734f0 -https://conda.anaconda.org/conda-forge/linux-64/udunits2-2.2.28-hc3e0081_0.tar.bz2#d4c341e0379c31e9e781d4f204726867 -https://conda.anaconda.org/conda-forge/noarch/babel-2.9.1-pyh44b312d_0.tar.bz2#74136ed39bfea0832d338df1e58d013e -https://conda.anaconda.org/conda-forge/linux-64/pyqt5-sip-4.19.18-py38h709712a_8.tar.bz2#11b72f5b1cc15427c89232321172a0bc -https://conda.anaconda.org/conda-forge/linux-64/mpi4py-3.1.3-py38he865349_0.tar.bz2#b1b3d6847a68251a1465206ab466b475 -https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.16-h516909a_0.tar.bz2#5c0f338a513a2943c659ae619fca9211 -https://conda.anaconda.org/conda-forge/linux-64/libwebp-base-1.2.2-h7f98852_1.tar.bz2#46cf26ecc8775a0aab300ea1821aaa3c -https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-8.0.28-hfa10184_0.tar.bz2#aac17542e50a474e2e632878dc696d50 -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/xorg-libxrender-0.9.10-h7f98852_1003.tar.bz2#f59c1242cc1dd93e72c2ee2b360979eb -https://conda.anaconda.org/conda-forge/noarch/pytz-2022.1-pyhd8ed1ab_0.tar.bz2#b87d66d6d3991d988fb31510c95a9267 -https://conda.anaconda.org/conda-forge/noarch/pycparser-2.21-pyhd8ed1ab_0.tar.bz2#076becd9e05608f8dc72757d5f3a91ff -https://conda.anaconda.org/conda-forge/linux-64/cffi-1.15.0-py38h3931269_0.tar.bz2#9c491a90ae11d08ca97326a0ed876f3a +https://conda.anaconda.org/conda-forge/noarch/nc-time-axis-1.4.0-pyhd8ed1ab_0.tar.bz2#9113b4e4fa2fa4a7f129c71a6f319475 +https://conda.anaconda.org/conda-forge/linux-64/pre-commit-2.17.0-py38h578d9bd_0.tar.bz2#839ac9dba9a6126c9532781a9ea4506b https://conda.anaconda.org/conda-forge/linux-64/pyqt-5.12.3-py38h578d9bd_8.tar.bz2#88368a5889f31dff922a2d57bbfc3f5b -https://conda.anaconda.org/conda-forge/noarch/nose-1.3.7-py_1006.tar.bz2#382019d5f8e9362ef6f60a8d4e7bce8f -https://conda.anaconda.org/conda-forge/linux-64/libtool-2.4.6-h9c3ff4c_1008.tar.bz2#16e143a1ed4b4fd169536373957f6fee -https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.0-pyhd8ed1ab_0.tar.bz2#57701269794b583dc907037bc66ae6ec -https://conda.anaconda.org/conda-forge/linux-64/xorg-xproto-7.0.31-h7f98852_1007.tar.bz2#b4a4381d54784606820704f7b5f05a15 -https://conda.anaconda.org/conda-forge/linux-64/alsa-lib-1.2.3-h516909a_0.tar.bz2#1378b88874f42ac31b2f8e4f6975cb7b -https://conda.anaconda.org/conda-forge/linux-64/pyqtchart-5.12-py38h7400c14_8.tar.bz2#78a2a6cb4ef31f997c1bee8223a9e579 -https://conda.anaconda.org/conda-forge/noarch/cfgv-3.3.1-pyhd8ed1ab_0.tar.bz2#ebb5f5f7dc4f1a3780ef7ea7738db08c -https://conda.anaconda.org/conda-forge/noarch/pyshp-2.2.0-pyhd8ed1ab_0.tar.bz2#2aa546be05be34b8e1744afd327b623f -https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.10.0-ha56f1ee_2.tar.bz2#6ab4eaa11ff01801cffca0a27489dc04 -https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-htmlhelp-2.0.0-pyhd8ed1ab_0.tar.bz2#77dad82eb9c8c1525ff7953e0756d708 -https://conda.anaconda.org/conda-forge/linux-64/libffi-3.4.2-h7f98852_5.tar.bz2#d645c6d2ac96843a2bfaccd2d62b3ac3 +https://conda.anaconda.org/conda-forge/noarch/urllib3-1.26.9-pyhd8ed1ab_0.tar.bz2#0ea179ee251aa7100807c35bc0252693 +https://conda.anaconda.org/conda-forge/linux-64/graphviz-3.0.0-h5abf519_1.tar.bz2#fcaf13b2713335ff871ba551d5bda679 +https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.5.1-py38h578d9bd_0.tar.bz2#0d78be9cf1c400ba8e3077cf060492f1 +https://conda.anaconda.org/conda-forge/noarch/requests-2.27.1-pyhd8ed1ab_0.tar.bz2#7c1c427246b057b8fa97200ecdb2ed62 +https://conda.anaconda.org/conda-forge/noarch/sphinx-4.5.0-pyh6c4a22f_0.tar.bz2#46b38d88c4270ff9ba78a89c83c66345 +https://conda.anaconda.org/conda-forge/noarch/sphinx-copybutton-0.5.0-pyhd8ed1ab_0.tar.bz2#4c969cdd5191306c269490f7ff236d9c +https://conda.anaconda.org/conda-forge/noarch/sphinx-gallery-0.10.1-pyhd8ed1ab_0.tar.bz2#4918585fe5e5341740f7e63c61743efb +https://conda.anaconda.org/conda-forge/noarch/sphinx-panels-0.6.0-pyhd8ed1ab_0.tar.bz2#6eec6480601f5d15babf9c3b3987f34a +https://conda.anaconda.org/conda-forge/noarch/sphinx_rtd_theme-1.0.0-pyhd8ed1ab_0.tar.bz2#9f633f2f2869184e31acfeae95b24345 https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-serializinghtml-1.1.5-pyhd8ed1ab_1.tar.bz2#63d2f874f990fdcab47c822b608d6ade -https://conda.anaconda.org/conda-forge/noarch/locket-0.2.0-py_2.tar.bz2#709e8671651c7ec3d1ad07800339ff1d -https://conda.anaconda.org/conda-forge/noarch/toolz-0.11.2-pyhd8ed1ab_0.tar.bz2#f348d1590550371edfac5ed3c1d44f7e -https://conda.anaconda.org/conda-forge/linux-64/libgfortran-ng-11.2.0-h69a702a_14.tar.bz2#67328431d4aeab35e5a2c6c22669c22b -https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.8-2_cp38.tar.bz2#bfbb29d517281e78ac53e48d21e6e860 -https://conda.anaconda.org/conda-forge/noarch/iris-sample-data-2.4.0-pyhd8ed1ab_0.tar.bz2#18ee9c07cf945a33f92caf1ee3d23ad9 From ceff5000f9a6f2099f387d9a8bc6149cb942349f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 4 Apr 2022 15:44:48 +0100 Subject: [PATCH 11/15] Updated environment lockfiles (#4678) Co-authored-by: Lockfile bot --- requirements/ci/nox.lock/py38-linux-64.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/requirements/ci/nox.lock/py38-linux-64.lock b/requirements/ci/nox.lock/py38-linux-64.lock index b9a064b2d3..7883f1deaa 100644 --- a/requirements/ci/nox.lock/py38-linux-64.lock +++ b/requirements/ci/nox.lock/py38-linux-64.lock @@ -31,7 +31,7 @@ https://conda.anaconda.org/conda-forge/linux-64/jbig-2.1-h7f98852_2003.tar.bz2#1 https://conda.anaconda.org/conda-forge/linux-64/jpeg-9e-h7f98852_0.tar.bz2#5c214edc675a7fb7cbb34b1d854e5141 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/lerc-3.0-h9c3ff4c_0.tar.bz2#7fcefde484980d23f0ec24c11e314d2e -https://conda.anaconda.org/conda-forge/linux-64/libbrotlicommon-1.0.9-h7f98852_6.tar.bz2#b0f44f63f7d771d7670747a1dd5d5ac1 +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/libdeflate-1.10-h7f98852_0.tar.bz2#ffa3a757a97e851293909b49f49f28fb 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 @@ -65,8 +65,8 @@ https://conda.anaconda.org/conda-forge/linux-64/xz-5.2.5-h516909a_1.tar.bz2#33f6 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/libblas-3.9.0-13_linux64_openblas.tar.bz2#8a4038563ed92dfa622bd72c0d8f31d3 -https://conda.anaconda.org/conda-forge/linux-64/libbrotlidec-1.0.9-h7f98852_6.tar.bz2#c7c03a2592cac92246a13a0732bd1573 -https://conda.anaconda.org/conda-forge/linux-64/libbrotlienc-1.0.9-h7f98852_6.tar.bz2#28bfe0a70154e6881da7bae97517c948 +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/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/libllvm13-13.0.1-hf817b99_2.tar.bz2#47da3ce0d8b2e65ccb226c186dd91eba @@ -79,7 +79,7 @@ https://conda.anaconda.org/conda-forge/linux-64/udunits2-2.2.28-hc3e0081_0.tar.b 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.11-h166bdaf_1014.tar.bz2#def3b82d1a03aa695bb38ac1dd072ff2 https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.2-ha95c52a_0.tar.bz2#5222b231b1ef49a7f60d40b363469b70 -https://conda.anaconda.org/conda-forge/linux-64/brotli-bin-1.0.9-h7f98852_6.tar.bz2#9e94bf16f14c78a36561d5019f490d22 +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/hdf4-4.2.15-h10796ff_3.tar.bz2#21a8d66dc17f065023b33145c42652fe 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-13_linux64_openblas.tar.bz2#b17676dbd6688396c3a3076259fb7907 @@ -96,7 +96,7 @@ https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-8.0.28-h28c427c_2.tar https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.37.1-h4ff8645_0.tar.bz2#8057ac02d6d10a162d7eb4b0ca7ed291 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-h7f98852_6.tar.bz2#612385c4a83edb0619fe911d9da317f4 +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/dbus-1.13.6-h5008d03_3.tar.bz2#ecfff944ba3960ecb334b9a2663d708d https://conda.anaconda.org/conda-forge/linux-64/freetype-2.10.4-h0708190_1.tar.bz2#4a06f2ac2e5bfae7b6b245171c3f07aa https://conda.anaconda.org/conda-forge/linux-64/gdk-pixbuf-2.42.8-hff1cb4f_0.tar.bz2#908fc30f89e27817d835b45f865536d7 @@ -155,7 +155,7 @@ https://conda.anaconda.org/conda-forge/linux-64/certifi-2021.10.8-py38h578d9bd_2 https://conda.anaconda.org/conda-forge/linux-64/cffi-1.15.0-py38h3931269_0.tar.bz2#9c491a90ae11d08ca97326a0ed876f3a https://conda.anaconda.org/conda-forge/linux-64/docutils-0.16-py38h578d9bd_3.tar.bz2#a7866449fb9e5e4008a02df276549d34 https://conda.anaconda.org/conda-forge/linux-64/importlib-metadata-4.11.3-py38h578d9bd_1.tar.bz2#21862e22238907d485e460f9c2e9ae4d -https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.2-py38h43d8883_0.tar.bz2#f6962263e6503aed8b6ea087d8cc892e +https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.2-py38h43d8883_1.tar.bz2#34c284cb94bd8c5118ccfe6d6fc3dd3f https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-h283352f_2.tar.bz2#2b0d39005a2e8347f329fe578bd6488a https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.8.1-mpi_mpich_h319fa22_1.tar.bz2#7583fbaea3648f692c0c019254bc196c https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.1.1-py38h0a891b7_1.tar.bz2#20d003ad5f584e212c299f64cac46c05 @@ -166,7 +166,7 @@ https://conda.anaconda.org/conda-forge/noarch/partd-1.2.0-pyhd8ed1ab_0.tar.bz2#0 https://conda.anaconda.org/conda-forge/linux-64/pillow-6.2.1-py38hd70f55b_1.tar.bz2#80d719bee2b77a106b199150c0829107 https://conda.anaconda.org/conda-forge/noarch/pockets-0.9.1-py_0.tar.bz2#1b52f0c42e8077e5a33e00fe72269364 https://conda.anaconda.org/conda-forge/linux-64/pyqt5-sip-4.19.18-py38h709712a_8.tar.bz2#11b72f5b1cc15427c89232321172a0bc -https://conda.anaconda.org/conda-forge/linux-64/pysocks-1.7.1-py38h578d9bd_4.tar.bz2#9c4bbee6f682f2fc7d7803df3996e77e +https://conda.anaconda.org/conda-forge/linux-64/pysocks-1.7.1-py38h578d9bd_5.tar.bz2#11113c7e50bb81f30762fe8325f305e1 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_0.tar.bz2#12eaa8cbfedfbf7879e5653467b03c94 https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0-py38h0a891b7_4.tar.bz2#ba24ff01bb38c5cd5be54b45ef685db3 @@ -175,7 +175,7 @@ https://conda.anaconda.org/conda-forge/linux-64/setuptools-61.3.0-py38h578d9bd_0 https://conda.anaconda.org/conda-forge/linux-64/tornado-6.1-py38h0a891b7_3.tar.bz2#d9e2836a4a46935f84b858462d54a7c3 https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-14.0.0-py38h497a2fe_0.tar.bz2#8da7787169411910df2a62dc8ef533e0 https://conda.anaconda.org/conda-forge/linux-64/virtualenv-20.14.0-py38h578d9bd_1.tar.bz2#6c6a5da4d7e2af1dbbfb1e21daa559cc -https://conda.anaconda.org/conda-forge/linux-64/brotlipy-0.7.0-py38h497a2fe_1003.tar.bz2#9189b42c42b9c87b2b2068cbe31901a8 +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.0-py38h3ec907f_0.tar.bz2#35411e5fc8dd523f9e68316847e6a25b https://conda.anaconda.org/conda-forge/linux-64/cryptography-36.0.2-py38h2b5fc30_0.tar.bz2#22274a82cf664d94a9e7c8f9deddf52a https://conda.anaconda.org/conda-forge/noarch/dask-core-2022.4.0-pyhd8ed1ab_0.tar.bz2#a47051950a34bef40d8369a5f320b78d From a78230e996584a998bab4007f4acf8d89fc013c0 Mon Sep 17 00:00:00 2001 From: lbdreyer Date: Mon, 4 Apr 2022 15:58:07 +0100 Subject: [PATCH 12/15] Reorder Constraints sections of user guide, add time bounds example (#4656) * move constraint detail to subsetting cube, add example of constraining bound * Add whats new --- docs/src/userguide/cube_maths.rst | 2 +- docs/src/userguide/loading_iris_cubes.rst | 237 +--------------------- docs/src/userguide/subsetting_a_cube.rst | 234 ++++++++++++++++++++- docs/src/whatsnew/dev.rst | 3 + 4 files changed, 234 insertions(+), 242 deletions(-) diff --git a/docs/src/userguide/cube_maths.rst b/docs/src/userguide/cube_maths.rst index e8a1744a44..fe9a5d63d2 100644 --- a/docs/src/userguide/cube_maths.rst +++ b/docs/src/userguide/cube_maths.rst @@ -38,7 +38,7 @@ Let's load some air temperature which runs from 1860 to 2100:: air_temp = iris.load_cube(filename, 'air_temperature') We can now get the first and last time slices using indexing -(see :ref:`subsetting_a_cube` for a reminder):: +(see :ref:`cube_indexing` for a reminder):: t_first = air_temp[0, :, :] t_last = air_temp[-1, :, :] diff --git a/docs/src/userguide/loading_iris_cubes.rst b/docs/src/userguide/loading_iris_cubes.rst index fb938975e8..9e9c343300 100644 --- a/docs/src/userguide/loading_iris_cubes.rst +++ b/docs/src/userguide/loading_iris_cubes.rst @@ -206,241 +206,8 @@ a specific ``model_level_number``:: level_10 = iris.Constraint(model_level_number=10) cubes = iris.load(filename, level_10) -Constraints can be combined using ``&`` to represent a more restrictive -constraint to ``load``:: - - filename = iris.sample_data_path('uk_hires.pp') - forecast_6 = iris.Constraint(forecast_period=6) - level_10 = iris.Constraint(model_level_number=10) - cubes = iris.load(filename, forecast_6 & level_10) - -.. note:: - - Whilst ``&`` is supported, the ``|`` that might reasonably be expected is - not. Explanation as to why is in the :class:`iris.Constraint` reference - documentation. - - For an example of constraining to multiple ranges of the same coordinate to - generate one cube, see the :class:`iris.Constraint` reference documentation. - - To generate multiple cubes, each constrained to a different range of the - same coordinate, use :py:func:`iris.load_cubes`. - -As well as being able to combine constraints using ``&``, -the :class:`iris.Constraint` class can accept multiple arguments, -and a list of values can be given to constrain a coordinate to one of -a collection of values:: - - filename = iris.sample_data_path('uk_hires.pp') - level_10_or_16_fp_6 = iris.Constraint(model_level_number=[10, 16], forecast_period=6) - cubes = iris.load(filename, level_10_or_16_fp_6) - -A common requirement is to limit the value of a coordinate to a specific range, -this can be achieved by passing the constraint a function:: - - def bottom_16_levels(cell): - # return True or False as to whether the cell in question should be kept - return cell <= 16 - - filename = iris.sample_data_path('uk_hires.pp') - level_lt_16 = iris.Constraint(model_level_number=bottom_16_levels) - cubes = iris.load(filename, level_lt_16) - -.. note:: - - As with many of the examples later in this documentation, the - simple function above can be conveniently written as a lambda function - on a single line:: - - bottom_16_levels = lambda cell: cell <= 16 - - -Note also the :ref:`warning on equality constraints with floating point coordinates `. - - -Cube attributes can also be part of the constraint criteria. Supposing a -cube attribute of ``STASH`` existed, as is the case when loading ``PP`` files, -then specific STASH codes can be filtered:: - - filename = iris.sample_data_path('uk_hires.pp') - level_10_with_stash = iris.AttributeConstraint(STASH='m01s00i004') & iris.Constraint(model_level_number=10) - cubes = iris.load(filename, level_10_with_stash) - -.. seealso:: - - For advanced usage there are further examples in the - :class:`iris.Constraint` reference documentation. - - -Constraining a Circular Coordinate Across its Boundary -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Occasionally you may need to constrain your cube with a region that crosses the -boundary of a circular coordinate (this is often the meridian or the dateline / -antimeridian). An example use-case of this is to extract the entire Pacific Ocean -from a cube whose longitudes are bounded by the dateline. - -This functionality cannot be provided reliably using constraints. Instead you should use the -functionality provided by :meth:`cube.intersection ` -to extract this region. - - -.. _using-time-constraints: - -Constraining on Time -^^^^^^^^^^^^^^^^^^^^ -Iris follows NetCDF-CF rules in representing time coordinate values as normalised, -purely numeric, values which are normalised by the calendar specified in the coordinate's -units (e.g. "days since 1970-01-01"). -However, when constraining by time we usually want to test calendar-related -aspects such as hours of the day or months of the year, so Iris -provides special features to facilitate this: - -Firstly, when Iris evaluates Constraint expressions, it will convert time-coordinate -values (points and bounds) from numbers into :class:`~datetime.datetime`-like objects -for ease of calendar-based testing. - - >>> filename = iris.sample_data_path('uk_hires.pp') - >>> cube_all = iris.load_cube(filename, 'air_potential_temperature') - >>> print('All times :\n' + str(cube_all.coord('time'))) - All times : - DimCoord : time / (hours since 1970-01-01 00:00:00, gregorian calendar) - points: [2009-11-19 10:00:00, 2009-11-19 11:00:00, 2009-11-19 12:00:00] - shape: (3,) - dtype: float64 - standard_name: 'time' - >>> # Define a function which accepts a datetime as its argument (this is simplified in later examples). - >>> hour_11 = iris.Constraint(time=lambda cell: cell.point.hour == 11) - >>> cube_11 = cube_all.extract(hour_11) - >>> print('Selected times :\n' + str(cube_11.coord('time'))) - Selected times : - DimCoord : time / (hours since 1970-01-01 00:00:00, gregorian calendar) - points: [2009-11-19 11:00:00] - shape: (1,) - dtype: float64 - standard_name: 'time' - -Secondly, the :class:`iris.time` module provides flexible time comparison -facilities. An :class:`iris.time.PartialDateTime` object can be compared to -objects such as :class:`datetime.datetime` instances, and this comparison will -then test only those 'aspects' which the PartialDateTime instance defines: - - >>> import datetime - >>> from iris.time import PartialDateTime - >>> dt = datetime.datetime(2011, 3, 7) - >>> print(dt > PartialDateTime(year=2010, month=6)) - True - >>> print(dt > PartialDateTime(month=6)) - False - >>> - -These two facilities can be combined to provide straightforward calendar-based -time selections when loading or extracting data. - -The previous constraint example can now be written as: - - >>> the_11th_hour = iris.Constraint(time=iris.time.PartialDateTime(hour=11)) - >>> print(iris.load_cube( - ... iris.sample_data_path('uk_hires.pp'), - ... 'air_potential_temperature' & the_11th_hour).coord('time')) - DimCoord : time / (hours since 1970-01-01 00:00:00, gregorian calendar) - points: [2009-11-19 11:00:00] - shape: (1,) - dtype: float64 - standard_name: 'time' - -It is common that a cube will need to be constrained between two given dates. -In the following example we construct a time sequence representing the first -day of every week for many years: - -.. testsetup:: timeseries_range - - import datetime - import numpy as np - from iris.time import PartialDateTime - long_ts = iris.cube.Cube(np.arange(150), long_name='data', units='1') - _mondays = iris.coords.DimCoord(7 * np.arange(150), standard_name='time', units='days since 2007-04-09') - long_ts.add_dim_coord(_mondays, 0) - - -.. doctest:: timeseries_range - :options: +NORMALIZE_WHITESPACE, +ELLIPSIS - - >>> print(long_ts.coord('time')) - DimCoord : time / (days since 2007-04-09, gregorian calendar) - points: [ - 2007-04-09 00:00:00, 2007-04-16 00:00:00, ..., - 2010-02-08 00:00:00, 2010-02-15 00:00:00] - shape: (150,) - dtype: int64 - standard_name: 'time' - -Given two dates in datetime format, we can select all points between them. - -.. doctest:: timeseries_range - :options: +NORMALIZE_WHITESPACE, +ELLIPSIS - - >>> d1 = datetime.datetime.strptime('20070715T0000Z', '%Y%m%dT%H%MZ') - >>> d2 = datetime.datetime.strptime('20070825T0000Z', '%Y%m%dT%H%MZ') - >>> st_swithuns_daterange_07 = iris.Constraint( - ... time=lambda cell: d1 <= cell.point < d2) - >>> within_st_swithuns_07 = long_ts.extract(st_swithuns_daterange_07) - >>> print(within_st_swithuns_07.coord('time')) - DimCoord : time / (days since 2007-04-09, gregorian calendar) - points: [ - 2007-07-16 00:00:00, 2007-07-23 00:00:00, 2007-07-30 00:00:00, - 2007-08-06 00:00:00, 2007-08-13 00:00:00, 2007-08-20 00:00:00] - shape: (6,) - dtype: int64 - standard_name: 'time' - -Alternatively, we may rewrite this using :class:`iris.time.PartialDateTime` -objects. - -.. doctest:: timeseries_range - :options: +NORMALIZE_WHITESPACE, +ELLIPSIS - - >>> pdt1 = PartialDateTime(year=2007, month=7, day=15) - >>> pdt2 = PartialDateTime(year=2007, month=8, day=25) - >>> st_swithuns_daterange_07 = iris.Constraint( - ... time=lambda cell: pdt1 <= cell.point < pdt2) - >>> within_st_swithuns_07 = long_ts.extract(st_swithuns_daterange_07) - >>> print(within_st_swithuns_07.coord('time')) - DimCoord : time / (days since 2007-04-09, gregorian calendar) - points: [ - 2007-07-16 00:00:00, 2007-07-23 00:00:00, 2007-07-30 00:00:00, - 2007-08-06 00:00:00, 2007-08-13 00:00:00, 2007-08-20 00:00:00] - shape: (6,) - dtype: int64 - standard_name: 'time' - -A more complex example might require selecting points over an annually repeating -date range. We can select points within a certain part of the year, in this case -between the 15th of July through to the 25th of August. By making use of -PartialDateTime this becomes simple: - -.. doctest:: timeseries_range - - >>> st_swithuns_daterange = iris.Constraint( - ... time=lambda cell: PartialDateTime(month=7, day=15) <= cell < PartialDateTime(month=8, day=25)) - >>> within_st_swithuns = long_ts.extract(st_swithuns_daterange) - ... - >>> # Note: using summary(max_values) to show more of the points - >>> print(within_st_swithuns.coord('time').summary(max_values=100)) - DimCoord : time / (days since 2007-04-09, gregorian calendar) - points: [ - 2007-07-16 00:00:00, 2007-07-23 00:00:00, 2007-07-30 00:00:00, - 2007-08-06 00:00:00, 2007-08-13 00:00:00, 2007-08-20 00:00:00, - 2008-07-21 00:00:00, 2008-07-28 00:00:00, 2008-08-04 00:00:00, - 2008-08-11 00:00:00, 2008-08-18 00:00:00, 2009-07-20 00:00:00, - 2009-07-27 00:00:00, 2009-08-03 00:00:00, 2009-08-10 00:00:00, - 2009-08-17 00:00:00, 2009-08-24 00:00:00] - shape: (17,) - dtype: int64 - standard_name: 'time' - -Notice how the dates printed are between the range specified in the ``st_swithuns_daterange`` -and that they span multiple years. +Further details on using :class:`iris.Constraint` are +discussed later in :ref:`cube_extraction`. .. _strict-loading: diff --git a/docs/src/userguide/subsetting_a_cube.rst b/docs/src/userguide/subsetting_a_cube.rst index 5112d9689a..6523ab1cd7 100644 --- a/docs/src/userguide/subsetting_a_cube.rst +++ b/docs/src/userguide/subsetting_a_cube.rst @@ -10,9 +10,10 @@ 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**. +.. _cube_extraction: Cube Extraction -^^^^^^^^^^^^^^^^ +--------------- A subset of a cube can be "extracted" from a multi-dimensional cube in order to reduce its dimensionality: >>> import iris @@ -34,15 +35,14 @@ A subset of a cube can be "extracted" from a multi-dimensional cube in order to In this example we start with a 3 dimensional cube, with dimensions of ``height``, ``grid_latitude`` and ``grid_longitude``, -and extract every point where the latitude is 0, resulting in a 2d cube with axes of ``height`` and ``grid_longitude``. - +and use :class:`iris.Constraint` to extract every point where the latitude is 0, resulting in a 2d cube with axes of ``height`` and ``grid_longitude``. .. _floating-point-warning: .. warning:: Caution is required when using equality constraints with floating point coordinates such as ``grid_latitude``. Printing the points of a coordinate does not necessarily show the full precision of the underlying number and it - is very easy return no matches to a constraint when one was expected. + is very easy to return no matches to a constraint when one was expected. This can be avoided by using a function as the argument to the constraint:: def near_zero(cell): @@ -68,6 +68,33 @@ The two steps required to get ``height`` of 9000 m at the equator can be simplif equator_height_9km_slice = cube.extract(iris.Constraint(grid_latitude=0, height=9000)) print(equator_height_9km_slice) +Alternatively, constraints can be combined using ``&``:: + + cube = iris.load_cube(filename, 'electron density') + equator_constraint = iris.Constraint(grid_latitude=0) + height_constraint = iris.Constraint(height=9000) + equator_height_9km_slice = cube.extract(equator_constraint & height_constraint) + +.. note:: + + Whilst ``&`` is supported, the ``|`` that might reasonably be expected is + not. Explanation as to why is in the :class:`iris.Constraint` reference + documentation. + + For an example of constraining to multiple ranges of the same coordinate to + generate one cube, see the :class:`iris.Constraint` reference documentation. + +A common requirement is to limit the value of a coordinate to a specific range, +this can be achieved by passing the constraint a function:: + + def below_9km(cell): + # return True or False as to whether the cell in question should be kept + return cell <= 9000 + + cube = iris.load_cube(filename, 'electron density') + height_below_9km = iris.Constraint(height=below_9km) + below_9km_slice = cube.extract(height_below_9km) + As we saw in :doc:`loading_iris_cubes` the result of :func:`iris.load` is a :class:`CubeList `. The ``extract`` method also exists on a :class:`CubeList ` and behaves in exactly the same way as loading with constraints: @@ -100,9 +127,203 @@ same way as loading with constraints: source 'Data from Met Office Unified Model' um_version '7.3' +Cube attributes can also be part of the constraint criteria. Supposing a +cube attribute of ``STASH`` existed, as is the case when loading ``PP`` files, +then specific STASH codes can be filtered:: + + filename = iris.sample_data_path('uk_hires.pp') + level_10_with_stash = iris.AttributeConstraint(STASH='m01s00i004') & iris.Constraint(model_level_number=10) + cubes = iris.load(filename).extract(level_10_with_stash) + +.. seealso:: + + For advanced usage there are further examples in the + :class:`iris.Constraint` reference documentation. + +Constraining a Circular Coordinate Across its Boundary +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Occasionally you may need to constrain your cube with a region that crosses the +boundary of a circular coordinate (this is often the meridian or the dateline / +antimeridian). An example use-case of this is to extract the entire Pacific Ocean +from a cube whose longitudes are bounded by the dateline. + +This functionality cannot be provided reliably using constraints. Instead you should use the +functionality provided by :meth:`cube.intersection ` +to extract this region. + + +.. _using-time-constraints: + +Constraining on Time +^^^^^^^^^^^^^^^^^^^^ +Iris follows NetCDF-CF rules in representing time coordinate values as normalised, +purely numeric, values which are normalised by the calendar specified in the coordinate's +units (e.g. "days since 1970-01-01"). +However, when constraining by time we usually want to test calendar-related +aspects such as hours of the day or months of the year, so Iris +provides special features to facilitate this. + +Firstly, when Iris evaluates :class:`iris.Constraint` expressions, it will convert +time-coordinate values (points and bounds) from numbers into :class:`~datetime.datetime`-like +objects for ease of calendar-based testing. + + >>> filename = iris.sample_data_path('uk_hires.pp') + >>> cube_all = iris.load_cube(filename, 'air_potential_temperature') + >>> print('All times :\n' + str(cube_all.coord('time'))) + All times : + DimCoord : time / (hours since 1970-01-01 00:00:00, gregorian calendar) + points: [2009-11-19 10:00:00, 2009-11-19 11:00:00, 2009-11-19 12:00:00] + shape: (3,) + dtype: float64 + standard_name: 'time' + >>> # Define a function which accepts a datetime as its argument (this is simplified in later examples). + >>> hour_11 = iris.Constraint(time=lambda cell: cell.point.hour == 11) + >>> cube_11 = cube_all.extract(hour_11) + >>> print('Selected times :\n' + str(cube_11.coord('time'))) + Selected times : + DimCoord : time / (hours since 1970-01-01 00:00:00, gregorian calendar) + points: [2009-11-19 11:00:00] + shape: (1,) + dtype: float64 + standard_name: 'time' + +Secondly, the :class:`iris.time` module provides flexible time comparison +facilities. An :class:`iris.time.PartialDateTime` object can be compared to +objects such as :class:`datetime.datetime` instances, and this comparison will +then test only those 'aspects' which the PartialDateTime instance defines: + + >>> import datetime + >>> from iris.time import PartialDateTime + >>> dt = datetime.datetime(2011, 3, 7) + >>> print(dt > PartialDateTime(year=2010, month=6)) + True + >>> print(dt > PartialDateTime(month=6)) + False + +These two facilities can be combined to provide straightforward calendar-based +time selections when loading or extracting data. + +The previous constraint example can now be written as: + + >>> the_11th_hour = iris.Constraint(time=iris.time.PartialDateTime(hour=11)) + >>> print(iris.load_cube( + ... iris.sample_data_path('uk_hires.pp'), + ... 'air_potential_temperature' & the_11th_hour).coord('time')) + DimCoord : time / (hours since 1970-01-01 00:00:00, gregorian calendar) + points: [2009-11-19 11:00:00] + shape: (1,) + dtype: float64 + standard_name: 'time' + +It is common that a cube will need to be constrained between two given dates. +In the following example we construct a time sequence representing the first +day of every week for many years: + +.. testsetup:: timeseries_range + + import datetime + import numpy as np + from iris.time import PartialDateTime + long_ts = iris.cube.Cube(np.arange(150), long_name='data', units='1') + _mondays = iris.coords.DimCoord(7 * np.arange(150), standard_name='time', units='days since 2007-04-09') + long_ts.add_dim_coord(_mondays, 0) + + +.. doctest:: timeseries_range + :options: +NORMALIZE_WHITESPACE, +ELLIPSIS + + >>> print(long_ts.coord('time')) + DimCoord : time / (days since 2007-04-09, gregorian calendar) + points: [ + 2007-04-09 00:00:00, 2007-04-16 00:00:00, ..., + 2010-02-08 00:00:00, 2010-02-15 00:00:00] + shape: (150,) + dtype: int64 + standard_name: 'time' + +Given two dates in datetime format, we can select all points between them. +Instead of constraining at loaded time, we already have the time coord so +we constrain that coord using :class:`iris.cube.Cube.extract` + +.. doctest:: timeseries_range + :options: +NORMALIZE_WHITESPACE, +ELLIPSIS + + >>> d1 = datetime.datetime.strptime('20070715T0000Z', '%Y%m%dT%H%MZ') + >>> d2 = datetime.datetime.strptime('20070825T0000Z', '%Y%m%dT%H%MZ') + >>> st_swithuns_daterange_07 = iris.Constraint( + ... time=lambda cell: d1 <= cell.point < d2) + >>> within_st_swithuns_07 = long_ts.extract(st_swithuns_daterange_07) + >>> print(within_st_swithuns_07.coord('time')) + DimCoord : time / (days since 2007-04-09, gregorian calendar) + points: [ + 2007-07-16 00:00:00, 2007-07-23 00:00:00, 2007-07-30 00:00:00, + 2007-08-06 00:00:00, 2007-08-13 00:00:00, 2007-08-20 00:00:00] + shape: (6,) + dtype: int64 + standard_name: 'time' + +Alternatively, we may rewrite this using :class:`iris.time.PartialDateTime` +objects. + +.. doctest:: timeseries_range + :options: +NORMALIZE_WHITESPACE, +ELLIPSIS + + >>> pdt1 = PartialDateTime(year=2007, month=7, day=15) + >>> pdt2 = PartialDateTime(year=2007, month=8, day=25) + >>> st_swithuns_daterange_07 = iris.Constraint( + ... time=lambda cell: pdt1 <= cell.point < pdt2) + >>> within_st_swithuns_07 = long_ts.extract(st_swithuns_daterange_07) + >>> print(within_st_swithuns_07.coord('time')) + DimCoord : time / (days since 2007-04-09, gregorian calendar) + points: [ + 2007-07-16 00:00:00, 2007-07-23 00:00:00, 2007-07-30 00:00:00, + 2007-08-06 00:00:00, 2007-08-13 00:00:00, 2007-08-20 00:00:00] + shape: (6,) + dtype: int64 + standard_name: 'time' + +A more complex example might require selecting points over an annually repeating +date range. We can select points within a certain part of the year, in this case +between the 15th of July through to the 25th of August. By making use of +PartialDateTime this becomes simple: + +.. doctest:: timeseries_range + + >>> st_swithuns_daterange = iris.Constraint( + ... time=lambda cell: PartialDateTime(month=7, day=15) <= cell.point < PartialDateTime(month=8, day=25)) + >>> within_st_swithuns = long_ts.extract(st_swithuns_daterange) + ... + >>> # Note: using summary(max_values) to show more of the points + >>> print(within_st_swithuns.coord('time').summary(max_values=100)) + DimCoord : time / (days since 2007-04-09, gregorian calendar) + points: [ + 2007-07-16 00:00:00, 2007-07-23 00:00:00, 2007-07-30 00:00:00, + 2007-08-06 00:00:00, 2007-08-13 00:00:00, 2007-08-20 00:00:00, + 2008-07-21 00:00:00, 2008-07-28 00:00:00, 2008-08-04 00:00:00, + 2008-08-11 00:00:00, 2008-08-18 00:00:00, 2009-07-20 00:00:00, + 2009-07-27 00:00:00, 2009-08-03 00:00:00, 2009-08-10 00:00:00, + 2009-08-17 00:00:00, 2009-08-24 00:00:00] + shape: (17,) + dtype: int64 + standard_name: 'time' + +Notice how the dates printed are between the range specified in the ``st_swithuns_daterange`` +and that they span multiple years. + +The above examples involve constraining on the points of the time coordinate. Constraining +on bounds can be done in the following way:: + + filename = iris.sample_data_path('ostia_monthly.nc') + cube = iris.load_cube(filename, 'surface_temperature') + dtmin = datetime.datetime(2008, 1, 1) + cube.extract(iris.Constraint(time = lambda cell: any(bound > dtmin for bound in cell.bound))) + +The above example constrains to cells where either the upper or lower bound occur +after 1st January 2008. Cube Iteration -^^^^^^^^^^^^^^^ +-------------- It is not possible to directly iterate over an Iris cube. That is, you cannot use code such as ``for x in cube:``. However, you can iterate over cube slices, as this section details. @@ -151,9 +372,10 @@ slicing the 3 dimensional cube (15, 100, 100) by longitude (i starts at 0 and 15 Once the your code can handle a 2d slice, it is then an easy step to loop over **all** 2d slices within the bigger cube using the slices method. +.. _cube_indexing: Cube Indexing -^^^^^^^^^^^^^ +------------- In the same way that you would expect a numeric multidimensional array to be **indexed** to take a subset of your original array, you can **index** a Cube for the same purpose. diff --git a/docs/src/whatsnew/dev.rst b/docs/src/whatsnew/dev.rst index 0307a8d82f..4e7a16adb2 100644 --- a/docs/src/whatsnew/dev.rst +++ b/docs/src/whatsnew/dev.rst @@ -105,6 +105,9 @@ This document explains the changes made to Iris for this release :ref:`voted_issues`. (:issue:`3307`, :pull:`4617`) #. `@wjbenfold`_ added a note about fixing proxy URLs in lockfiles generated because dependencies have changed. (:pull:`4666`) +#. `@lbdreyer`_ moved most of the User Guide's :class:`iris.Constraint` examples + from :doc:`loading_iris_cubes` to :ref:`cube_extraction` and added an + example of constraining on bounded time. (:pull:`4656`) 💼 Internal From 5eeb2c02215122c038c60e76b784cdb37f8a1d94 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Apr 2022 10:34:22 +0100 Subject: [PATCH 13/15] Bump peter-evans/create-pull-request from 4.0.0 to 4.0.1 (#4675) Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 4.0.0 to 4.0.1. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/d6d5519d05f5814158ef015b8448f2f74648c421...f1a7646cead32c950d90344a4fb5d4e926972a8f) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/refresh-lockfiles.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/refresh-lockfiles.yml b/.github/workflows/refresh-lockfiles.yml index d191910b44..c48a631bbd 100644 --- a/.github/workflows/refresh-lockfiles.yml +++ b/.github/workflows/refresh-lockfiles.yml @@ -72,7 +72,7 @@ jobs: - name: Create Pull Request id: cpr - uses: peter-evans/create-pull-request@d6d5519d05f5814158ef015b8448f2f74648c421 + uses: peter-evans/create-pull-request@f1a7646cead32c950d90344a4fb5d4e926972a8f with: commit-message: Updated environment lockfiles committer: "Lockfile bot " From 3a8eee44e1e2dfc39b9ee3ee3e4b494c44ff7c4a Mon Sep 17 00:00:00 2001 From: Ruth Comer <10599679+rcomer@users.noreply.github.com> Date: Wed, 6 Apr 2022 14:00:40 +0100 Subject: [PATCH 14/15] tweak whatsnew (#4682) --- docs/src/whatsnew/dev.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/whatsnew/dev.rst b/docs/src/whatsnew/dev.rst index 4e7a16adb2..843815b46b 100644 --- a/docs/src/whatsnew/dev.rst +++ b/docs/src/whatsnew/dev.rst @@ -106,7 +106,7 @@ This document explains the changes made to Iris for this release #. `@wjbenfold`_ added a note about fixing proxy URLs in lockfiles generated because dependencies have changed. (:pull:`4666`) #. `@lbdreyer`_ moved most of the User Guide's :class:`iris.Constraint` examples - from :doc:`loading_iris_cubes` to :ref:`cube_extraction` and added an + from :ref:`loading_iris_cubes` to :ref:`cube_extraction` and added an example of constraining on bounded time. (:pull:`4656`) From 591c9f4817e04422cd076a878704afc85482f1dc Mon Sep 17 00:00:00 2001 From: tkknight <2108488+tkknight@users.noreply.github.com> Date: Thu, 7 Apr 2022 17:11:44 +0100 Subject: [PATCH 15/15] Implement Pydata theme (#4661) * new theme baseline * update * added discussion directive and whatsnew * fixed bullet point indentation * updated NOX_CACHE_BUILD to force update the nox cache * added pr ref * Update lock-file generation in response to conda-lock change * Make lockfile shareable and bump nox cache * MAin branch updates to latest.rst (from dev.rst) * change references of dev.rst to latest.rst * corrected bullet points indentation to render correctly. * updated * update whatsnew * Review action and fixed typo for link * update lock * updated from main Co-authored-by: Will Benfold Co-authored-by: Bill Little --- .cirrus.yml | 2 +- docs/gallery_code/README.rst | 2 + docs/gallery_code/general/README.rst | 1 + docs/src/_static/icon_api.svg | 144 ++++++++++ docs/src/_static/icon_development.svg | 63 +++++ docs/src/_static/icon_instructions.svg | 162 ++++++++++++ docs/src/_static/icon_new_product.svg | 182 +++++++++++++ docs/src/_static/icon_shuttle.svg | 71 +++++ docs/src/_static/icon_support.png | Bin 0 -> 8037 bytes docs/src/_static/icon_thumb.png | Bin 0 -> 18412 bytes docs/src/_static/iris-logo-title.svg | 23 +- docs/src/_static/theme_override.css | 42 +-- docs/src/_templates/custom_footer.html | 1 + .../custom_sidebar_logo_version.html | 20 ++ docs/src/_templates/footer.html | 5 - docs/src/_templates/layout.html | 40 +-- docs/src/common_links.inc | 3 +- docs/src/conf.py | 91 ++++--- .../contributing_deprecations.rst | 72 ++--- .../contributing_getting_involved.rst | 33 ++- .../contributing_graphics_tests.rst | 70 ++--- .../contributing_pull_request_checklist.rst | 16 +- .../developers_guide/contributing_testing.rst | 4 +- .../documenting/docstrings.rst | 4 +- .../documenting/rest_guide.rst | 4 +- .../documenting/whats_new_contributions.rst | 27 +- .../gitwash/development_workflow.rst | 2 +- docs/src/developers_guide/release.rst | 34 ++- docs/src/further_topics/index.rst | 26 -- docs/src/further_topics/metadata.rst | 32 +-- docs/src/further_topics/ugrid/data_model.rst | 12 +- docs/src/getting_started.rst | 15 ++ docs/src/index.rst | 185 ++++++------- docs/src/installing.rst | 4 +- docs/src/userguide/code_maintenance.rst | 24 +- docs/src/userguide/index.rst | 62 +++-- .../interpolation_and_regridding.rst | 56 ++-- docs/src/userguide/iris_cubes.rst | 246 ++++++++++-------- docs/src/userguide/loading_iris_cubes.rst | 14 +- docs/src/userguide/merge_and_concat.rst | 16 +- docs/src/userguide/plotting_a_cube.rst | 30 +-- docs/src/userguide/real_and_lazy_data.rst | 26 +- docs/src/voted_issues.rst | 2 +- docs/src/whatsnew/3.2.rst | 2 +- docs/src/whatsnew/dev.rst | 137 ---------- docs/src/whatsnew/index.rst | 2 +- docs/src/whatsnew/latest.rst | 146 ++++++++++- .../{dev.rst.template => latest.rst.template} | 0 docs/src/why_iris.rst | 44 ++++ requirements/ci/nox.lock/py38-linux-64.lock | 40 +-- requirements/ci/py38.yml | 2 +- tools/update_lockfiles.py | 3 +- 52 files changed, 1484 insertions(+), 760 deletions(-) create mode 100644 docs/src/_static/icon_api.svg create mode 100644 docs/src/_static/icon_development.svg create mode 100644 docs/src/_static/icon_instructions.svg create mode 100644 docs/src/_static/icon_new_product.svg create mode 100644 docs/src/_static/icon_shuttle.svg create mode 100644 docs/src/_static/icon_support.png create mode 100644 docs/src/_static/icon_thumb.png create mode 100644 docs/src/_templates/custom_footer.html create mode 100644 docs/src/_templates/custom_sidebar_logo_version.html delete mode 100644 docs/src/_templates/footer.html delete mode 100644 docs/src/further_topics/index.rst create mode 100644 docs/src/getting_started.rst delete mode 100644 docs/src/whatsnew/dev.rst mode change 120000 => 100644 docs/src/whatsnew/latest.rst rename docs/src/whatsnew/{dev.rst.template => latest.rst.template} (100%) create mode 100644 docs/src/why_iris.rst diff --git a/.cirrus.yml b/.cirrus.yml index c9c1d71859..534f9f895c 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -30,7 +30,7 @@ env: # Increment the build number to force new conda cache upload. CONDA_CACHE_BUILD: "0" # Increment the build number to force new nox cache upload. - NOX_CACHE_BUILD: "0" + NOX_CACHE_BUILD: "2" # Increment the build number to force new pip cache upload. PIP_CACHE_BUILD: "0" # Pip packages to be upgraded/installed. diff --git a/docs/gallery_code/README.rst b/docs/gallery_code/README.rst index 720fd1e6f6..85bf0552b4 100644 --- a/docs/gallery_code/README.rst +++ b/docs/gallery_code/README.rst @@ -1,3 +1,5 @@ +.. _gallery_index: + Gallery ======= diff --git a/docs/gallery_code/general/README.rst b/docs/gallery_code/general/README.rst index c846755f1e..3a48e7cd8e 100644 --- a/docs/gallery_code/general/README.rst +++ b/docs/gallery_code/general/README.rst @@ -1,2 +1,3 @@ General ------- + diff --git a/docs/src/_static/icon_api.svg b/docs/src/_static/icon_api.svg new file mode 100644 index 0000000000..841b105973 --- /dev/null +++ b/docs/src/_static/icon_api.svg @@ -0,0 +1,144 @@ + + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/src/_static/icon_development.svg b/docs/src/_static/icon_development.svg new file mode 100644 index 0000000000..dbc342688c --- /dev/null +++ b/docs/src/_static/icon_development.svg @@ -0,0 +1,63 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/docs/src/_static/icon_instructions.svg b/docs/src/_static/icon_instructions.svg new file mode 100644 index 0000000000..62b3fc3620 --- /dev/null +++ b/docs/src/_static/icon_instructions.svg @@ -0,0 +1,162 @@ + + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/src/_static/icon_new_product.svg b/docs/src/_static/icon_new_product.svg new file mode 100644 index 0000000000..f222e1e066 --- /dev/null +++ b/docs/src/_static/icon_new_product.svg @@ -0,0 +1,182 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/src/_static/icon_shuttle.svg b/docs/src/_static/icon_shuttle.svg new file mode 100644 index 0000000000..46ba64d2e0 --- /dev/null +++ b/docs/src/_static/icon_shuttle.svg @@ -0,0 +1,71 @@ + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/docs/src/_static/icon_support.png b/docs/src/_static/icon_support.png new file mode 100644 index 0000000000000000000000000000000000000000..567cdb1b2fd899d4c62784cca6d18839cc41ed7c GIT binary patch literal 8037 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 literal 0 HcmV?d00001 diff --git a/docs/src/_static/icon_thumb.png b/docs/src/_static/icon_thumb.png new file mode 100644 index 0000000000000000000000000000000000000000..6a14875e22f325d2aaabe6bb28393cf1fca7adae GIT binary patch literal 18412 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 h|G$5REA#WIhi_wGt+whIA=+>{8n + units="cm" + scale-x="1.2" /> @@ -63,8 +64,8 @@ id="layer1" transform="translate(1765.8435,1039.1033)"> Iris diff --git a/docs/src/_static/theme_override.css b/docs/src/_static/theme_override.css index c56b720f69..326c1d4d4a 100644 --- a/docs/src/_static/theme_override.css +++ b/docs/src/_static/theme_override.css @@ -1,33 +1,10 @@ /* import the standard theme css */ @import url("css/theme.css"); -/* now we can add custom any css */ - -/* set the width of the logo */ -.wy-side-nav-search>a img.logo, -.wy-side-nav-search .wy-dropdown>a img.logo { - width: 12rem -} - -/* color of the logo background in the top left corner */ -.wy-side-nav-search { - background-color: lightgray; -} - -/* color of the font for the version in the top left corner */ -.wy-side-nav-search>div.version { - color: black; - font-weight: bold; -} - -/* Ensures tables do now have width scroll bars */ -table.docutils td { - white-space: unset; - word-wrap: break-word; -} +/* now we can add custom css.... */ /* Used for very strong warning */ -#slim-red-box-message { +#slim-red-box-banner { background: #ff0000; box-sizing: border-box; color: #ffffff; @@ -35,8 +12,17 @@ table.docutils td { padding: 0.5em; } -#slim-red-box-message a { +#slim-red-box-banner a { color: #ffffff; - font-weight: normal; - text-decoration:underline; + font-weight: normal; + text-decoration: underline; +} + +/* bullet point list with green ticks */ +ul.squarelist { + /* https://developer.mozilla.org/en-US/docs/Web/CSS/list-style-type */ + list-style-type: "\2705"; + margin-left: 0; + text-indent: 1em; + padding-left: 5em; } diff --git a/docs/src/_templates/custom_footer.html b/docs/src/_templates/custom_footer.html new file mode 100644 index 0000000000..f81fcc583e --- /dev/null +++ b/docs/src/_templates/custom_footer.html @@ -0,0 +1 @@ +

Built using Python {{ python_version }}.

diff --git a/docs/src/_templates/custom_sidebar_logo_version.html b/docs/src/_templates/custom_sidebar_logo_version.html new file mode 100644 index 0000000000..48bbe604a0 --- /dev/null +++ b/docs/src/_templates/custom_sidebar_logo_version.html @@ -0,0 +1,20 @@ +{% if on_rtd %} + {% if rtd_version == 'latest' %} + + + + {% elif rtd_version == 'stable' %} + + + + {% else %} + + + + {% endif %} +{%- else %} + {# not on rtd #} + + + +{%- endif %} diff --git a/docs/src/_templates/footer.html b/docs/src/_templates/footer.html deleted file mode 100644 index 1d5fb08b78..0000000000 --- a/docs/src/_templates/footer.html +++ /dev/null @@ -1,5 +0,0 @@ -{% extends "!footer.html" %} -{% block extrafooter %} - Built using Python {{ python_version }}. - {{ super() }} -{% endblock %} diff --git a/docs/src/_templates/layout.html b/docs/src/_templates/layout.html index 96a2e0913e..7377e866b7 100644 --- a/docs/src/_templates/layout.html +++ b/docs/src/_templates/layout.html @@ -1,16 +1,16 @@ -{% extends "!layout.html" %} +{% extends "pydata_sphinx_theme/layout.html" %} -{# This uses blocks. See: +{# This uses blocks. See: https://www.sphinx-doc.org/en/master/templating.html #} -/*---------------------------------------------------------------------------*/ -{%- block document %} - {% if READTHEDOCS and rtd_version == 'latest' %} -
+ {%- block docs_body %} + + {% if on_rtd and rtd_version == 'latest' %} +
You are viewing the latest unreleased documentation - v{{ version }}. You may prefer a + v{{ version }}. You may prefer a stable version.
@@ -19,29 +19,3 @@ {{ super() }} {%- endblock %} - -/*-----------------------------------------------------z----------------------*/ - -{% block menu %} - {{ super() }} - - {# menu_links and menu_links_name are set in conf.py (html_context) #} - - {% if menu_links %} -

- - {% if menu_links_name %} - {{ menu_links_name }} - {% else %} - External links - {% endif %} - -

-
    - {% for text, link in menu_links %} -
  • {{ text }}
  • - {% endfor %} -
- {% endif %} -{% endblock %} - diff --git a/docs/src/common_links.inc b/docs/src/common_links.inc index ce7f498d80..e80a597976 100644 --- a/docs/src/common_links.inc +++ b/docs/src/common_links.inc @@ -10,10 +10,9 @@ .. _conda: https://docs.conda.io/en/latest/ .. _contributor: https://github.com/SciTools/scitools.org.uk/blob/master/contributors.json .. _core developers: https://github.com/SciTools/scitools.org.uk/blob/master/contributors.json -.. _discussions: https://github.com/SciTools/iris/discussions .. _generating sss keys for GitHub: https://docs.github.com/en/github/authenticating-to-github/adding-a-new-ssh-key-to-your-github-account .. _GitHub Help Documentation: https://docs.github.com/en/github -.. _Iris GitHub Discussions: https://github.com/SciTools/iris/discussions +.. _GitHub Discussions: https://github.com/SciTools/iris/discussions .. _Iris: https://github.com/SciTools/iris .. _Iris GitHub: https://github.com/SciTools/iris .. _iris-sample-data: https://github.com/SciTools/iris-sample-data diff --git a/docs/src/conf.py b/docs/src/conf.py index 3b7731ab1b..0af6ec072f 100644 --- a/docs/src/conf.py +++ b/docs/src/conf.py @@ -41,20 +41,23 @@ def autolog(message): # -- Are we running on the readthedocs server, if so do some setup ----------- on_rtd = os.environ.get("READTHEDOCS") == "True" +# This is the rtd reference to the version, such as: latest, stable, v3.0.1 etc +rtd_version = os.environ.get("READTHEDOCS_VERSION") + +# For local testing purposes we can force being on RTD and the version +# on_rtd = True # useful for testing +# rtd_version = "latest" # useful for testing +# rtd_version = "stable" # useful for testing + if on_rtd: autolog("Build running on READTHEDOCS server") # list all the READTHEDOCS environment variables that may be of use - # at some point autolog("Listing all environment variables on the READTHEDOCS server...") for item, value in os.environ.items(): autolog("[READTHEDOCS] {} = {}".format(item, value)) -# This is the rtd reference to the version, such as: latest, stable, v3.0.1 etc -# For local testing purposes this could be explicitly set latest or stable. -rtd_version = os.environ.get("READTHEDOCS_VERSION") - # -- Path setup -------------------------------------------------------------- # If extensions (or modules to document with autodoc) are in another directory, @@ -91,6 +94,7 @@ def autolog(message): else: # major.minor.patch-dev -> major.minor.patch version = ".".join(iris.__version__.split("-")[0].split(".")[:3]) + # The full version, including alpha/beta/rc tags. release = iris.__version__ @@ -171,6 +175,7 @@ def _dotv(version): # -- panels extension --------------------------------------------------------- # See https://sphinx-panels.readthedocs.io/en/latest/ +panels_add_bootstrap_css = False # -- Napoleon extension ------------------------------------------------------- # See https://sphinxcontrib-napoleon.readthedocs.io/en/latest/sphinxcontrib.napoleon.html @@ -246,6 +251,10 @@ def _dotv(version): extlinks = { "issue": ("https://github.com/SciTools/iris/issues/%s", "Issue #"), "pull": ("https://github.com/SciTools/iris/pull/%s", "PR #"), + "discussion": ( + "https://github.com/SciTools/iris/discussions/%s", + "Discussion #", + ), } # -- Doctest ("make doctest")-------------------------------------------------- @@ -259,41 +268,61 @@ def _dotv(version): # html_logo = "_static/iris-logo-title.png" html_favicon = "_static/favicon.ico" -html_theme = "sphinx_rtd_theme" +html_theme = "pydata_sphinx_theme" + +# See https://pydata-sphinx-theme.readthedocs.io/en/latest/user_guide/configuring.html#configure-the-search-bar-position +html_sidebars = { + "**": [ + "custom_sidebar_logo_version", + "search-field", + "sidebar-nav-bs", + "sidebar-ethical-ads", + ] +} +# See https://pydata-sphinx-theme.readthedocs.io/en/latest/user_guide/configuring.html html_theme_options = { - "display_version": True, - "style_external_links": True, - "logo_only": "True", + "footer_items": ["copyright", "sphinx-version", "custom_footer"], + "collapse_navigation": True, + "navigation_depth": 3, + "show_prev_next": True, + "navbar_align": "content", + "github_url": "https://github.com/SciTools/iris", + "twitter_url": "https://twitter.com/scitools_iris", + # icons available: https://fontawesome.com/v5.15/icons?d=gallery&m=free + "icon_links": [ + { + "name": "GitHub Discussions", + "url": "https://github.com/SciTools/iris/discussions", + "icon": "far fa-comments", + }, + { + "name": "PyPI", + "url": "https://pypi.org/project/scitools-iris/", + "icon": "fas fa-box", + }, + { + "name": "Conda", + "url": "https://anaconda.org/conda-forge/iris", + "icon": "fas fa-boxes", + }, + ], + "use_edit_page_button": True, + "show_toc_level": 1, } html_context = { + # pydata_theme + "github_repo": "iris", + "github_user": "scitools", + "github_version": "main", + "doc_path": "docs/src", + # custom + "on_rtd": on_rtd, "rtd_version": rtd_version, "version": version, "copyright_years": copyright_years, "python_version": build_python_version, - # menu_links and menu_links_name are used in _templates/layout.html - # to include some nice icons. See http://fontawesome.io for a list of - # icons (used in the sphinx_rtd_theme) - "menu_links_name": "Support", - "menu_links": [ - ( - ' Source Code', - "https://github.com/SciTools/iris", - ), - ( - ' GitHub Discussions', - "https://github.com/SciTools/iris/discussions", - ), - ( - ' StackOverflow for "How Do I?"', - "https://stackoverflow.com/questions/tagged/python-iris", - ), - ( - ' Legacy Documentation', - "https://scitools.org.uk/iris/docs/v2.4.0/index.html", - ), - ], } # Add any paths that contain custom static files (such as style sheets) here, diff --git a/docs/src/developers_guide/contributing_deprecations.rst b/docs/src/developers_guide/contributing_deprecations.rst index 1ecafdca9f..0b22e2cbd2 100644 --- a/docs/src/developers_guide/contributing_deprecations.rst +++ b/docs/src/developers_guide/contributing_deprecations.rst @@ -25,29 +25,29 @@ deprecation is accompanied by the introduction of a new public API. Under these circumstances the following points apply: - - Using the deprecated API must result in a concise deprecation warning which - is an instance of :class:`iris.IrisDeprecation`. - It is easiest to call - :func:`iris._deprecation.warn_deprecated`, which is a - simple wrapper to :func:`warnings.warn` with the signature - `warn_deprecation(message, **kwargs)`. - - Where possible, your deprecation warning should include advice on - how to avoid using the deprecated API. For example, you might - reference a preferred API, or more detailed documentation elsewhere. - - You must update the docstring for the deprecated API to include a - Sphinx deprecation directive: - - :literal:`.. deprecated:: ` - - where you should replace `` with the major and minor version - of Iris in which this API is first deprecated. For example: `1.8`. - - As with the deprecation warning, you should include advice on how to - avoid using the deprecated API within the content of this directive. - Feel free to include more detail in the updated docstring than in the - deprecation warning. - - You should check the documentation for references to the deprecated - API and update them as appropriate. +- Using the deprecated API must result in a concise deprecation warning which + is an instance of :class:`iris.IrisDeprecation`. + It is easiest to call + :func:`iris._deprecation.warn_deprecated`, which is a + simple wrapper to :func:`warnings.warn` with the signature + `warn_deprecation(message, **kwargs)`. +- Where possible, your deprecation warning should include advice on + how to avoid using the deprecated API. For example, you might + reference a preferred API, or more detailed documentation elsewhere. +- You must update the docstring for the deprecated API to include a + Sphinx deprecation directive: + + :literal:`.. deprecated:: ` + + where you should replace `` with the major and minor version + of Iris in which this API is first deprecated. For example: `1.8`. + + As with the deprecation warning, you should include advice on how to + avoid using the deprecated API within the content of this directive. + Feel free to include more detail in the updated docstring than in the + deprecation warning. +- You should check the documentation for references to the deprecated + API and update them as appropriate. Changing a Default ------------------ @@ -64,14 +64,14 @@ it causes the corresponding public API to use its new default behaviour. The following points apply in addition to those for removing a public API: - - You should add a new boolean attribute to :data:`iris.FUTURE` (by - modifying :class:`iris.Future`) that controls the default behaviour - of the public API that needs updating. The initial state of the new - boolean attribute should be `False`. You should name the new boolean - attribute to indicate that setting it to `True` will select the new - default behaviour. - - You should include a reference to this :data:`iris.FUTURE` flag in your - deprecation warning and corresponding Sphinx deprecation directive. +- You should add a new boolean attribute to :data:`iris.FUTURE` (by + modifying :class:`iris.Future`) that controls the default behaviour + of the public API that needs updating. The initial state of the new + boolean attribute should be `False`. You should name the new boolean + attribute to indicate that setting it to `True` will select the new + default behaviour. +- You should include a reference to this :data:`iris.FUTURE` flag in your + deprecation warning and corresponding Sphinx deprecation directive. Removing a Deprecation @@ -94,11 +94,11 @@ and/or example code should be removed/updated as appropriate. Changing a Default ------------------ - - You should update the initial state of the relevant boolean attribute - of :data:`iris.FUTURE` to `True`. - - You should deprecate setting the relevant boolean attribute of - :class:`iris.Future` in the same way as described in - :ref:`removing-a-public-api`. +- You should update the initial state of the relevant boolean attribute + of :data:`iris.FUTURE` to `True`. +- You should deprecate setting the relevant boolean attribute of + :class:`iris.Future` in the same way as described in + :ref:`removing-a-public-api`. .. rubric:: Footnotes diff --git a/docs/src/developers_guide/contributing_getting_involved.rst b/docs/src/developers_guide/contributing_getting_involved.rst index f7bd4733a3..f4e677cea2 100644 --- a/docs/src/developers_guide/contributing_getting_involved.rst +++ b/docs/src/developers_guide/contributing_getting_involved.rst @@ -1,8 +1,9 @@ .. include:: ../common_links.inc .. _development_where_to_start: +.. _developers_guide: -Getting Involved +Developers Guide ---------------- Iris_ is an Open Source project hosted on Github and as such anyone with a @@ -17,7 +18,7 @@ The `Iris GitHub`_ project has been configured to use templates for each of the above issue types when creating a `new issue`_ to ensure the appropriate information is provided. -Alternatively, **join the conversation** in `Iris GitHub Discussions`_, when +Alternatively, **join the conversation** in Iris `GitHub Discussions`_, when you would like the opinions of the Iris community. A `pull request`_ may also be created by anyone who has become a @@ -25,7 +26,7 @@ A `pull request`_ may also be created by anyone who has become a ``main`` branch are only given to **core developers** of Iris_, this is to ensure a measure of control. -To get started we suggest reading recent `issues`_, `discussions`_ and +To get started we suggest reading recent `issues`_, `GitHub Discussions`_ and `pull requests`_ for Iris. If you are new to using GitHub we recommend reading the @@ -36,5 +37,29 @@ If you are new to using GitHub we recommend reading the `Governance `_ section of the `SciTools`_ ogranization web site. - .. _GitHub getting started: https://docs.github.com/en/github/getting-started-with-github + + +.. toctree:: + :maxdepth: 1 + :caption: Developers Guide + :name: development_index + :hidden: + + gitwash/index + contributing_documentation + contributing_codebase_index + contributing_changes + release + + +.. toctree:: + :maxdepth: 1 + :caption: Reference + :hidden: + + ../generated/api/iris + ../whatsnew/index + ../techpapers/index + ../copyright + ../voted_issues diff --git a/docs/src/developers_guide/contributing_graphics_tests.rst b/docs/src/developers_guide/contributing_graphics_tests.rst index 1268aa2686..07e2301141 100644 --- a/docs/src/developers_guide/contributing_graphics_tests.rst +++ b/docs/src/developers_guide/contributing_graphics_tests.rst @@ -44,24 +44,24 @@ reference images in the Iris repository itself. This consists of: - * The ``iris.tests.IrisTest_nometa.check_graphic`` function uses a perceptual - **image hash** of the outputs (see https://github.com/JohannesBuchner/imagehash) - as the basis for checking test results. +* The ``iris.tests.IrisTest_nometa.check_graphic`` function uses a perceptual + **image hash** of the outputs (see https://github.com/JohannesBuchner/imagehash) + as the basis for checking test results. - * The hashes of known **acceptable** results for each test are stored in a - lookup dictionary, saved to the repo file - ``lib/iris/tests/results/imagerepo.json`` - (`link `_) . +* The hashes of known **acceptable** results for each test are stored in a + lookup dictionary, saved to the repo file + ``lib/iris/tests/results/imagerepo.json`` + (`link `_) . - * An actual reference image for each hash value is stored in a *separate* - public repository https://github.com/SciTools/test-iris-imagehash. +* An actual reference image for each hash value is stored in a *separate* + public repository https://github.com/SciTools/test-iris-imagehash. - * The reference images allow human-eye assessment of whether a new output is - judged to be close enough to the older ones, or not. +* The reference images allow human-eye assessment of whether a new output is + judged to be close enough to the older ones, or not. - * The utility script ``iris/tests/idiff.py`` automates checking, enabling the - developer to easily compare proposed new **acceptable** result images - against the existing accepted reference images, for each failing test. +* The utility script ``iris/tests/idiff.py`` automates checking, enabling the + developer to easily compare proposed new **acceptable** result images + against the existing accepted reference images, for each failing test. The acceptable images for each test can be viewed online. The :ref:`testing.imagehash_index` lists all the graphical tests in the test suite and shows the known acceptable result images for comparison. @@ -92,29 +92,29 @@ you should follow: If the change is **accepted**: - * the imagehash value of the new result image is added into the relevant - set of 'valid result hashes' in the image result database file, - ``tests/results/imagerepo.json`` + * the imagehash value of the new result image is added into the relevant + set of 'valid result hashes' in the image result database file, + ``tests/results/imagerepo.json`` - * the relevant output file in ``tests/result_image_comparison`` is - renamed according to the image hash value, as ``.png``. - A copy of this new PNG file must then be added into the reference image - repository at https://github.com/SciTools/test-iris-imagehash - (See below). + * the relevant output file in ``tests/result_image_comparison`` is + renamed according to the image hash value, as ``.png``. + A copy of this new PNG file must then be added into the reference image + repository at https://github.com/SciTools/test-iris-imagehash + (See below). If a change is **skipped**: - * no further changes are made in the repo. + * no further changes are made in the repo. - * when you run ``iris/tests/idiff.py`` again, the skipped choice will be - presented again. + * when you run ``iris/tests/idiff.py`` again, the skipped choice will be + presented again. If a change is **rejected**: - * the output image is deleted from ``result_image_comparison``. + * the output image is deleted from ``result_image_comparison``. - * when you run ``iris/tests/idiff.py`` again, the skipped choice will not - appear, unless the relevant failing test is re-run. + * when you run ``iris/tests/idiff.py`` again, the skipped choice will not + appear, unless the relevant failing test is re-run. #. **Now re-run the tests**. The **new** result should now be recognised and the relevant test should pass. However, some tests can perform *multiple* @@ -132,16 +132,16 @@ To add your changes to Iris, you need to make two pull requests (PR). #. The first PR is made in the ``test-iris-imagehash`` repository, at https://github.com/SciTools/test-iris-imagehash. - * First, add all the newly-generated referenced PNG files into the - ``images/v4`` directory. In your Iris repo, these files are to be found - in the temporary results folder ``iris/tests/result_image_comparison``. + * First, add all the newly-generated referenced PNG files into the + ``images/v4`` directory. In your Iris repo, these files are to be found + in the temporary results folder ``iris/tests/result_image_comparison``. - * Then, to update the file which lists available images, - ``v4_files_listing.txt``, run from the project root directory:: + * Then, to update the file which lists available images, + ``v4_files_listing.txt``, run from the project root directory:: - python recreate_v4_files_listing.py + python recreate_v4_files_listing.py - * Create a PR proposing these changes, in the usual way. + * Create a PR proposing these changes, in the usual way. #. The second PR is created in the Iris_ repository, and should only include the change to the image results database, diff --git a/docs/src/developers_guide/contributing_pull_request_checklist.rst b/docs/src/developers_guide/contributing_pull_request_checklist.rst index 5afb461d68..524b5d2c17 100644 --- a/docs/src/developers_guide/contributing_pull_request_checklist.rst +++ b/docs/src/developers_guide/contributing_pull_request_checklist.rst @@ -16,8 +16,8 @@ is merged. Before submitting a pull request please consider this list. #. **Provide a helpful description** of the Pull Request. This should include: - * The aim of the change / the problem addressed / a link to the issue. - * How the change has been delivered. + * The aim of the change / the problem addressed / a link to the issue. + * How the change has been delivered. #. **Include a "What's New" entry**, if appropriate. See :ref:`whats_new_contributions`. @@ -51,12 +51,12 @@ is merged. Before submitting a pull request please consider this list. #. **Check for updates needed for supporting projects for test or example data**. For example: - * `iris-test-data`_ is a github project containing all the data to support - the tests. - * `iris-sample-data`_ is a github project containing all the data to support - the gallery and examples. - * `test-iris-imagehash`_ is a github project containing reference plot - images to support Iris :ref:`testing.graphics`. + * `iris-test-data`_ is a github project containing all the data to support + the tests. + * `iris-sample-data`_ is a github project containing all the data to support + the gallery and examples. + * `test-iris-imagehash`_ is a github project containing reference plot + images to support Iris :ref:`testing.graphics`. If new files are required by tests or code examples, they must be added to the appropriate supporting project via a suitable pull-request. This pull diff --git a/docs/src/developers_guide/contributing_testing.rst b/docs/src/developers_guide/contributing_testing.rst index d0c96834a9..a65bcebd55 100644 --- a/docs/src/developers_guide/contributing_testing.rst +++ b/docs/src/developers_guide/contributing_testing.rst @@ -8,8 +8,8 @@ Test Categories There are two main categories of tests within Iris: - - :ref:`testing.unit_test` - - :ref:`testing.integration` +- :ref:`testing.unit_test` +- :ref:`testing.integration` Ideally, all code changes should be accompanied by one or more unit tests, and by zero or more integration tests. diff --git a/docs/src/developers_guide/documenting/docstrings.rst b/docs/src/developers_guide/documenting/docstrings.rst index 8a06024ee2..ef0679aac0 100644 --- a/docs/src/developers_guide/documenting/docstrings.rst +++ b/docs/src/developers_guide/documenting/docstrings.rst @@ -10,8 +10,8 @@ the code and may be read directly in the source or via the :ref:`Iris`. This document has been influenced by the following PEP's, - * Attribute Docstrings :pep:`224` - * Docstring Conventions :pep:`257` +* Attribute Docstrings :pep:`224` +* Docstring Conventions :pep:`257` For consistency always use: diff --git a/docs/src/developers_guide/documenting/rest_guide.rst b/docs/src/developers_guide/documenting/rest_guide.rst index 4845132b15..c4330b1e63 100644 --- a/docs/src/developers_guide/documenting/rest_guide.rst +++ b/docs/src/developers_guide/documenting/rest_guide.rst @@ -14,8 +14,8 @@ reST is a lightweight markup language intended to be highly readable in source format. This guide will cover some of the more frequently used advanced reST markup syntaxes, for the basics of reST the following links may be useful: - * https://www.sphinx-doc.org/en/master/usage/restructuredtext/ - * http://packages.python.org/an_example_pypi_project/sphinx.html +* https://www.sphinx-doc.org/en/master/usage/restructuredtext/ +* http://packages.python.org/an_example_pypi_project/sphinx.html Reference documentation for reST can be found at http://docutils.sourceforge.net/rst.html. diff --git a/docs/src/developers_guide/documenting/whats_new_contributions.rst b/docs/src/developers_guide/documenting/whats_new_contributions.rst index 576fc5f6a6..c9dbe04b71 100644 --- a/docs/src/developers_guide/documenting/whats_new_contributions.rst +++ b/docs/src/developers_guide/documenting/whats_new_contributions.rst @@ -4,21 +4,16 @@ Contributing a "What's New" Entry ================================= -Iris uses a file named ``dev.rst`` to keep a draft of upcoming development changes -that will form the next stable release. Contributions to the :ref:`iris_whatsnew` -document are written by the developer most familiar with the change made. -The contribution should be included as part of the Iris Pull Request that -introduces the change. +Iris uses a file named ``latest.rst`` to keep a draft of upcoming development +changes that will form the next stable release. Contributions to the +:ref:`iris_whatsnew` document are written by the developer most familiar +with the change made. The contribution should be included as part of +the Iris Pull Request that introduces the change. -The ``dev.rst`` and the past release notes are kept in the +The ``latest.rst`` and the past release notes are kept in the ``docs/src/whatsnew/`` directory. If you are writing the first contribution after -an Iris release: **create the new** ``dev.rst`` by copying the content from -``dev.rst.template`` in the same directory. - -.. note:: - - Ensure that the symbolic link ``latest.rst`` references the ``dev.rst`` file - within the ``docs/src/whatsnew`` directory. +an Iris release: **create the new** ``latest.rst`` by copying the content from +``latest.rst.template`` in the same directory. Since the `Contribution categories`_ include Internal changes, **all** Iris Pull Requests should be accompanied by a "What's New" contribution. @@ -27,7 +22,7 @@ Pull Requests should be accompanied by a "What's New" contribution. Git Conflicts ============= -If changes to ``dev.rst`` are being suggested in several simultaneous +If changes to ``latest.rst`` are being suggested in several simultaneous Iris Pull Requests, Git will likely encounter merge conflicts. If this situation is thought likely (large PR, high repo activity etc.): @@ -38,7 +33,7 @@ situation is thought likely (large PR, high repo activity etc.): a **new pull request** be created specifically for the "What's New" entry, which references the main pull request and titled (e.g. for PR#9999): - What's New for #9999 + What's New for #9999 * PR author: create the "What's New" pull request @@ -48,7 +43,7 @@ situation is thought likely (large PR, high repo activity etc.): * PR reviewer: review the "What's New" PR, merge once acceptable -These measures should mean the suggested ``dev.rst`` changes are outstanding +These measures should mean the suggested ``latest.rst`` changes are outstanding for the minimum time, minimising conflicts and minimising the need to rebase or merge from trunk. diff --git a/docs/src/developers_guide/gitwash/development_workflow.rst b/docs/src/developers_guide/gitwash/development_workflow.rst index 0536ebfb62..6e74e8546d 100644 --- a/docs/src/developers_guide/gitwash/development_workflow.rst +++ b/docs/src/developers_guide/gitwash/development_workflow.rst @@ -25,7 +25,7 @@ In what follows we'll refer to the upstream iris ``main`` branch, as * If you can possibly avoid it, avoid merging trunk or any other branches into your feature branch while you are working. * If you do find yourself merging from trunk, consider :ref:`rebase-on-trunk` -* Ask on the `Iris GitHub Discussions`_ if you get stuck. +* Ask on the Iris `GitHub Discussions`_ if you get stuck. * Ask for code review! This way of working helps to keep work well organized, with readable history. diff --git a/docs/src/developers_guide/release.rst b/docs/src/developers_guide/release.rst index f4d44781fc..a918253ef7 100644 --- a/docs/src/developers_guide/release.rst +++ b/docs/src/developers_guide/release.rst @@ -19,7 +19,8 @@ A Release Manager will be nominated for each release of Iris. This role involves * deciding which features and bug fixes should be included in the release * managing the project board for the release -* using a `GitHub Releases Discussion Forum`_ for documenting intent and capturing any +* using :discussion:`GitHub Discussion releases category ` + for documenting intent and capturing any discussion about the release The Release Manager will make the release, ensuring that all the steps outlined @@ -183,7 +184,7 @@ back onto the ``SciTools/iris`` ``main`` branch. To achieve this, first cut a local branch from the latest ``main`` branch, and `git merge` the :literal:`.x` release branch into it. Ensure that the -``iris.__version__``, ``docs/src/whatsnew/index.rst``, ``docs/src/whatsnew/dev.rst``, +``iris.__version__``, ``docs/src/whatsnew/index.rst``, and ``docs/src/whatsnew/latest.rst`` are correct, before committing these changes and then proposing a pull-request on the ``main`` branch of ``SciTools/iris``. @@ -218,24 +219,22 @@ Release Steps #. 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/dev.rst`` to the release - version file ``v1.9.rst`` - * Update the symbolic link ``latest.rst`` to reference the latest - whatsnew ``v1.9.rst`` - * Use ``git`` to delete the ``docs/src/whatsnew/dev.rst.template`` file - * In ``v1.9.rst`` remove the ``[unreleased]`` caption from the page title. - Note that, the Iris version and release date are updated automatically - when the documentation is built - * Review the file for correctness - * Work with the development team to populate the ``Release Highlights`` - dropdown at the top of the file, which provides extra detail on notable - changes - * Use ``git`` to add and commit all changes, including removal of - ``dev.rst.template`` and update to the ``latest.rst`` symbolic link. + * Use ``git`` to rename ``docs/src/whatsnew/latest.rst`` to the release + version file ``v1.9.rst`` + * Use ``git`` to delete the ``docs/src/whatsnew/latest.rst.template`` file + * In ``v1.9.rst`` remove the ``[unreleased]`` caption from the page title. + Note that, the Iris version and release date are updated automatically + when the documentation is built + * Review the file for correctness + * Work with the development team to populate the ``Release Highlights`` + dropdown at the top of the file, which provides extra detail on notable + changes + * Use ``git`` to add and commit all changes, including removal of + ``latest.rst.template``. #. Update the ``whatsnew`` index ``docs/src/whatsnew/index.rst`` - * Remove the reference to ``dev.rst`` + * Remove the reference to ``latest.rst`` * Add a reference to ``v1.9.rst`` to the top of the list #. Check your changes by building the documentation and reviewing @@ -261,7 +260,6 @@ Post Release Steps .. _SciTools/iris: https://github.com/SciTools/iris .. _tag on the SciTools/Iris: https://github.com/SciTools/iris/releases -.. _GitHub Releases Discussion Forum: https://github.com/SciTools/iris/discussions/categories/releases .. _conda-forge Anaconda channel: https://anaconda.org/conda-forge/iris .. _conda-forge iris-feedstock: https://github.com/conda-forge/iris-feedstock .. _CFEP-05: https://github.com/conda-forge/cfep/blob/master/cfep-05.md diff --git a/docs/src/further_topics/index.rst b/docs/src/further_topics/index.rst deleted file mode 100644 index 81bff2f764..0000000000 --- a/docs/src/further_topics/index.rst +++ /dev/null @@ -1,26 +0,0 @@ -.. _further topics: - -Introduction -============ - -Some specific areas of Iris may require further explanation or a deep dive -into additional detail above and beyond that offered by the -:ref:`User Guide `. - -This section provides a collection of additional material on focused topics -that may be of interest to the more advanced or curious user. - -.. hint:: - - If you wish further documentation on any specific topics or areas of Iris - that are missing, then please let us know by raising a :issue:`GitHub Documentation Issue` - on `SciTools/Iris`_. - - -* :doc:`metadata` -* :doc:`lenient_metadata` -* :doc:`lenient_maths` -* :ref:`ugrid` - - -.. _SciTools/iris: https://github.com/SciTools/iris diff --git a/docs/src/further_topics/metadata.rst b/docs/src/further_topics/metadata.rst index 1b81f7055c..de1afb15af 100644 --- a/docs/src/further_topics/metadata.rst +++ b/docs/src/further_topics/metadata.rst @@ -1,3 +1,4 @@ +.. _further topics: .. _metadata: Metadata @@ -63,25 +64,26 @@ For example, the collective metadata used to define an ``var_name``, ``units``, and ``attributes`` members. Note that, these are the actual `data attribute`_ names of the metadata members on the Iris class. + .. _metadata members table: -.. table:: - Iris classes that model `CF Conventions`_ metadata +.. table:: Iris classes that model `CF Conventions`_ metadata :widths: auto :align: center - =================== ======================================= ============================== ========================================== ================================= ======================== ============================== =================== - Metadata Members :class:`~iris.coords.AncillaryVariable` :class:`~iris.coords.AuxCoord` :class:`~iris.aux_factory.AuxCoordFactory` :class:`~iris.coords.CellMeasure` :class:`~iris.cube.Cube` :class:`~iris.coords.DimCoord` Metadata Members - =================== ======================================= ============================== ========================================== ================================= ======================== ============================== =================== - ``standard_name`` ✔ ✔ ✔ ✔ ✔ ✔ ``standard_name`` - ``long_name`` ✔ ✔ ✔ ✔ ✔ ✔ ``long_name`` - ``var_name`` ✔ ✔ ✔ ✔ ✔ ✔ ``var_name`` - ``units`` ✔ ✔ ✔ ✔ ✔ ✔ ``units`` - ``attributes`` ✔ ✔ ✔ ✔ ✔ ✔ ``attributes`` - ``coord_system`` ✔ ✔ ✔ ``coord_system`` - ``climatological`` ✔ ✔ ✔ ``climatological`` - ``measure`` ✔ ``measure`` - ``cell_methods`` ✔ ``cell_methods`` - ``circular`` ✔ ``circular`` - =================== ======================================= ============================== ========================================== ================================= ======================== ============================== =================== + =================== ======================================= ============================== ========================================== ================================= ======================== ============================== + Metadata Members :class:`~iris.coords.AncillaryVariable` :class:`~iris.coords.AuxCoord` :class:`~iris.aux_factory.AuxCoordFactory` :class:`~iris.coords.CellMeasure` :class:`~iris.cube.Cube` :class:`~iris.coords.DimCoord` + =================== ======================================= ============================== ========================================== ================================= ======================== ============================== + ``standard_name`` ✔ ✔ ✔ ✔ ✔ ✔ + ``long_name`` ✔ ✔ ✔ ✔ ✔ ✔ + ``var_name`` ✔ ✔ ✔ ✔ ✔ ✔ + ``units`` ✔ ✔ ✔ ✔ ✔ ✔ + ``attributes`` ✔ ✔ ✔ ✔ ✔ ✔ + ``coord_system`` ✔ ✔ ✔ + ``climatological`` ✔ ✔ ✔ + ``measure`` ✔ + ``cell_methods`` ✔ + ``circular`` ✔ + =================== ======================================= ============================== ========================================== ================================= ======================== ============================== .. note:: diff --git a/docs/src/further_topics/ugrid/data_model.rst b/docs/src/further_topics/ugrid/data_model.rst index 4a2f64f627..55e4f79a96 100644 --- a/docs/src/further_topics/ugrid/data_model.rst +++ b/docs/src/further_topics/ugrid/data_model.rst @@ -52,7 +52,7 @@ example. .. _data_structured_grid: .. figure:: images/data_structured_grid.svg :alt: Diagram of how data is represented on a structured grid - :align: right + :align: left :width: 1280 Data on a structured grid. @@ -131,7 +131,7 @@ example of what is described above. .. _data_ugrid_mesh: .. figure:: images/data_ugrid_mesh.svg :alt: Diagram of how data is represented on an unstructured mesh - :align: right + :align: left :width: 1280 Data on an unstructured mesh @@ -157,7 +157,7 @@ elements. See :numref:`ugrid_element_centres` for a visualised example. .. _ugrid_element_centres: .. figure:: images/ugrid_element_centres.svg :alt: Diagram demonstrating mesh face-centred data. - :align: right + :align: left :width: 1280 Data can be assigned to mesh edge/face/volume 'centres' @@ -180,7 +180,7 @@ Every node is completely independent - every one can have unique X andY (and Z) .. _ugrid_node_independence: .. figure:: images/ugrid_node_independence.svg :alt: Diagram demonstrating the independence of each mesh node - :align: right + :align: left :width: 300 Every mesh node is completely independent @@ -199,7 +199,7 @@ array. See :numref:`ugrid_variable_faces`. .. _ugrid_variable_faces: .. figure:: images/ugrid_variable_faces.svg :alt: Diagram demonstrating mesh faces with variable node counts - :align: right + :align: left :width: 300 Mesh faces can have different node counts (using masking) @@ -216,7 +216,7 @@ areas (faces). See :numref:`ugrid_edge_data`. .. _ugrid_edge_data: .. figure:: images/ugrid_edge_data.svg :alt: Diagram demonstrating data assigned to mesh edges - :align: right + :align: left :width: 300 Data can be assigned to mesh edges diff --git a/docs/src/getting_started.rst b/docs/src/getting_started.rst new file mode 100644 index 0000000000..24299a4060 --- /dev/null +++ b/docs/src/getting_started.rst @@ -0,0 +1,15 @@ +.. _getting_started_index: + +Getting Started +=============== + +To get started with Iris we recommend reading :ref:`why_iris` was created and to +explore the examples in the :ref:`gallery_index` after :ref:`installing_iris` +Iris. + +.. toctree:: + :maxdepth: 1 + + why_iris + installing + generated/gallery/index \ No newline at end of file diff --git a/docs/src/index.rst b/docs/src/index.rst index d247b93411..fb0e93b1ae 100644 --- a/docs/src/index.rst +++ b/docs/src/index.rst @@ -1,7 +1,9 @@ +.. include:: common_links.inc .. _iris_docs: -Iris |version| -======================== + +Iris +==== **A powerful, format-agnostic, community-driven Python package for analysing and visualising Earth science data.** @@ -11,158 +13,137 @@ giving you a powerful, format-agnostic interface for working with your data. It excels when working with multi-dimensional Earth Science data, where tabular representations become unwieldy and inefficient. -`CF Standard names `_, -`units `_, and coordinate metadata -are built into Iris, giving you a rich and expressive interface for maintaining -an accurate representation of your data. Its treatment of data and -associated metadata as first-class objects includes: - -* visualisation interface based on `matplotlib `_ and - `cartopy `_, -* unit conversion, -* subsetting and extraction, -* merge and concatenate, -* aggregations and reductions (including min, max, mean and weighted averages), -* interpolation and regridding (including nearest-neighbor, linear and - area-weighted), and -* operator overloads (``+``, ``-``, ``*``, ``/``, etc.). - -A number of file formats are recognised by Iris, including CF-compliant NetCDF, -GRIB, and PP, and it has a plugin architecture to allow other formats to be -added seamlessly. - -Building upon `NumPy `_ and -`dask `_, Iris scales from efficient -single-machine workflows right through to multi-core clusters and HPC. -Interoperability with packages from the wider scientific Python ecosystem comes -from Iris' use of standard NumPy/dask arrays as its underlying data storage. - -Iris is part of SciTools, for more information see https://scitools.org.uk/. -For **Iris 2.4** and earlier documentation please see the -:link-badge:`https://scitools.org.uk/iris/docs/v2.4.0/,"legacy documentation",cls=badge-info text-white`. - +For more information see :ref:`why_iris`. .. panels:: :container: container-lg pb-3 - :column: col-lg-4 col-md-4 col-sm-6 col-xs-12 p-2 + :column: col-lg-4 col-md-4 col-sm-6 col-xs-12 p-2 text-center + :img-top-cls: w-50 m-auto px-1 py-2 - Install Iris as a user or developer. - +++ - .. link-button:: installing_iris - :type: ref - :text: Installing Iris - :classes: btn-outline-primary btn-block --- - Example code to create a variety of plots. + :img-top: _static/icon_shuttle.svg + + Information on Iris, how to install and a gallery of examples that + create plots. +++ - .. link-button:: sphx_glr_generated_gallery + .. link-button:: getting_started :type: ref - :text: Gallery - :classes: btn-outline-primary btn-block + :text: Getting Started + :classes: btn-outline-info btn-block + + --- - Find out what has recently changed in Iris. + :img-top: _static/icon_instructions.svg + + Learn how to use Iris, including loading, navigating, saving, + plotting and more. +++ - .. link-button:: iris_whatsnew + .. link-button:: user_guide_index :type: ref - :text: What's New - :classes: btn-outline-primary btn-block + :text: User Guide + :classes: btn-outline-info btn-block + --- - Learn how to use Iris. + :img-top: _static/icon_development.svg + + As a developer you can contribute to Iris. +++ - .. link-button:: user_guide_index + .. link-button:: development_where_to_start :type: ref - :text: User Guide - :classes: btn-outline-primary btn-block + :text: Developers Guide + :classes: btn-outline-info btn-block + --- + :img-top: _static/icon_api.svg + Browse full Iris functionality by module. +++ .. link-button:: Iris :type: ref :text: Iris API - :classes: btn-outline-primary btn-block + :classes: btn-outline-info btn-block + --- - As a developer you can contribute to Iris. + :img-top: _static/icon_new_product.svg + + Find out what has recently changed in Iris. +++ - .. link-button:: development_where_to_start + .. link-button:: iris_whatsnew :type: ref - :text: Getting Involved - :classes: btn-outline-primary btn-block + :text: What's New + :classes: btn-outline-info btn-block + --- + :img-top: _static/icon_thumb.png -.. toctree:: - :maxdepth: 1 - :caption: Getting Started - :hidden: + Raise the profile of issues by voting on them. + +++ + .. link-button:: voted_issues_top + :type: ref + :text: Voted Issues + :classes: btn-outline-info btn-block + + +Icons made by `FreePik `_ from +`Flaticon `_ + + +Support +~~~~~~~ + +We, the Iris developers have adopted `GitHub Discussions`_ to capture any +discussions or support questions related to Iris. + +See also `StackOverflow for "How Do I? `_ +that may be useful but we do not actively monitor this. + +The legacy support resources: - installing - generated/gallery/index +* `Users Google Group `_ +* `Developers Google Group `_ +* `Legacy Documentation`_ (Iris 2.4 or earlier) .. toctree:: + :caption: Getting Started :maxdepth: 1 - :caption: What's New in Iris :hidden: - whatsnew/latest - Archive + getting_started .. toctree:: - :maxdepth: 1 :caption: User Guide + :maxdepth: 1 :name: userguide_index :hidden: userguide/index - userguide/iris_cubes - userguide/loading_iris_cubes - userguide/saving_iris_cubes - userguide/navigating_a_cube - userguide/subsetting_a_cube - userguide/real_and_lazy_data - userguide/plotting_a_cube - userguide/interpolation_and_regridding - userguide/merge_and_concat - userguide/cube_statistics - userguide/cube_maths - userguide/citation - userguide/code_maintenance - - -.. _developers_guide: + .. toctree:: + :caption: Developers Guide :maxdepth: 1 - :caption: Further Topics + :name: developers_index :hidden: - further_topics/index - further_topics/metadata - further_topics/lenient_metadata - further_topics/lenient_maths - further_topics/ugrid/index + developers_guide/contributing_getting_involved .. toctree:: - :maxdepth: 2 - :caption: Developers Guide - :name: development_index + :caption: Iris API + :maxdepth: 1 :hidden: - developers_guide/contributing_getting_involved - developers_guide/gitwash/index - developers_guide/contributing_documentation - developers_guide/contributing_codebase_index - developers_guide/contributing_changes - developers_guide/release + generated/api/iris .. toctree:: + :caption: Developers Guide :maxdepth: 1 - :caption: Reference + :name: whats_new_index :hidden: - generated/api/iris - techpapers/index - copyright - voted_issues + whatsnew/index + +.. todolist:: \ No newline at end of file diff --git a/docs/src/installing.rst b/docs/src/installing.rst index 37a8942ab3..33b15610fa 100644 --- a/docs/src/installing.rst +++ b/docs/src/installing.rst @@ -1,7 +1,7 @@ .. _installing_iris: -Installing Iris -=============== +Installing +========== Iris is available using conda for the following platforms: diff --git a/docs/src/userguide/code_maintenance.rst b/docs/src/userguide/code_maintenance.rst index b2b498bc80..c01c1975a7 100644 --- a/docs/src/userguide/code_maintenance.rst +++ b/docs/src/userguide/code_maintenance.rst @@ -12,17 +12,17 @@ In practice, as Iris develops, most users will want to periodically upgrade their installed version to access new features or at least bug fixes. This is obvious if you are still developing other code that uses Iris, or using -code from other sources. +code from other sources. However, even if you have only legacy code that remains untouched, some code maintenance effort is probably still necessary: - * On the one hand, *in principle*, working code will go on working, as long - as you don't change anything else. +* On the one hand, *in principle*, working code will go on working, as long + as you don't change anything else. - * However, such "version stasis" can easily become a growing burden, if you - are simply waiting until an update becomes unavoidable, often that will - eventually occur when you need to update some other software component, - for some completely unconnected reason. +* However, such "version stasis" can easily become a growing burden, if you + are simply waiting until an update becomes unavoidable, often that will + eventually occur when you need to update some other software component, + for some completely unconnected reason. Principles of Change Management @@ -35,13 +35,13 @@ In Iris, however, we aim to reduce code maintenance problems to an absolute minimum by following defined change management rules. These ensure that, *within a major release number* : - * you can be confident that your code will still work with subsequent minor - releases +* you can be confident that your code will still work with subsequent minor + releases - * you will be aware of future incompatibility problems in advance +* you will be aware of future incompatibility problems in advance - * you can defer making code compatibility changes for some time, until it - suits you +* you can defer making code compatibility changes for some time, until it + suits you The above applies to minor version upgrades : e.g. code that works with version "1.4.2" should still work with a subsequent minor release such as "1.5.0" or diff --git a/docs/src/userguide/index.rst b/docs/src/userguide/index.rst index 2a3b32fe11..08923e7662 100644 --- a/docs/src/userguide/index.rst +++ b/docs/src/userguide/index.rst @@ -1,31 +1,47 @@ .. _user_guide_index: .. _user_guide_introduction: -Introduction -============ +User Guide +========== -If you are reading this user guide for the first time it is strongly recommended that you read the user guide -fully before experimenting with your own data files. +If you are reading this user guide for the first time it is strongly +recommended that you read the user guide fully before experimenting with your +own data files. - -Much of the content has supplementary links to the reference documentation; you will not need to follow these -links in order to understand the guide but they may serve as a useful reference for future exploration. +Much of the content has supplementary links to the reference documentation; +you will not need to follow these links in order to understand the guide but +they may serve as a useful reference for future exploration. .. only:: html - Since later pages depend on earlier ones, try reading this user guide sequentially using the ``next`` and ``previous`` links. - - -* :doc:`iris_cubes` -* :doc:`loading_iris_cubes` -* :doc:`saving_iris_cubes` -* :doc:`navigating_a_cube` -* :doc:`subsetting_a_cube` -* :doc:`real_and_lazy_data` -* :doc:`plotting_a_cube` -* :doc:`interpolation_and_regridding` -* :doc:`merge_and_concat` -* :doc:`cube_statistics` -* :doc:`cube_maths` -* :doc:`citation` -* :doc:`code_maintenance` + Since later pages depend on earlier ones, try reading this user guide + sequentially using the ``next`` and ``previous`` links at the bottom + of each page. + + +.. toctree:: + :maxdepth: 2 + + iris_cubes + loading_iris_cubes + saving_iris_cubes + navigating_a_cube + subsetting_a_cube + real_and_lazy_data + plotting_a_cube + interpolation_and_regridding + merge_and_concat + cube_statistics + cube_maths + citation + code_maintenance + + +.. toctree:: + :maxdepth: 2 + :caption: Further Topics + + ../further_topics/metadata + ../further_topics/lenient_metadata + ../further_topics/lenient_maths + ../further_topics/ugrid/index diff --git a/docs/src/userguide/interpolation_and_regridding.rst b/docs/src/userguide/interpolation_and_regridding.rst index f590485606..deae4427ed 100644 --- a/docs/src/userguide/interpolation_and_regridding.rst +++ b/docs/src/userguide/interpolation_and_regridding.rst @@ -19,14 +19,14 @@ In Iris we refer to the available types of interpolation and regridding as `schemes`. The following are the interpolation schemes that are currently available in Iris: - * linear interpolation (:class:`iris.analysis.Linear`), and - * nearest-neighbour interpolation (:class:`iris.analysis.Nearest`). +* linear interpolation (:class:`iris.analysis.Linear`), and +* nearest-neighbour interpolation (:class:`iris.analysis.Nearest`). The following are the regridding schemes that are currently available in Iris: - * linear regridding (:class:`iris.analysis.Linear`), - * nearest-neighbour regridding (:class:`iris.analysis.Nearest`), and - * area-weighted regridding (:class:`iris.analysis.AreaWeighted`, first-order conservative). +* linear regridding (:class:`iris.analysis.Linear`), +* nearest-neighbour regridding (:class:`iris.analysis.Nearest`), and +* area-weighted regridding (:class:`iris.analysis.AreaWeighted`, first-order conservative). The linear, nearest-neighbor, and area-weighted regridding schemes support lazy regridding, i.e. if the source cube has lazy data, the resulting cube @@ -42,8 +42,8 @@ Interpolation Interpolating a cube is achieved with the :meth:`~iris.cube.Cube.interpolate` method. This method expects two arguments: - #. the sample points to interpolate, and - #. the interpolation scheme to use. +#. the sample points to interpolate, and +#. the interpolation scheme to use. The result is a new cube, interpolated at the sample points. @@ -51,9 +51,9 @@ Sample points must be defined as an iterable of ``(coord, value(s))`` pairs. The `coord` argument can be either a coordinate name or coordinate instance. The specified coordinate must exist on the cube being interpolated! For example: - * coordinate names and scalar sample points: ``[('latitude', 51.48), ('longitude', 0)]``, - * a coordinate instance and a scalar sample point: ``[(cube.coord('latitude'), 51.48)]``, and - * a coordinate name and a NumPy array of sample points: ``[('longitude', np.linspace(-11, 2, 14))]`` +* coordinate names and scalar sample points: ``[('latitude', 51.48), ('longitude', 0)]``, +* a coordinate instance and a scalar sample point: ``[(cube.coord('latitude'), 51.48)]``, and +* a coordinate name and a NumPy array of sample points: ``[('longitude', np.linspace(-11, 2, 14))]`` are all examples of valid sample points. @@ -175,11 +175,11 @@ The extrapolation mode is controlled by the ``extrapolation_mode`` keyword. For the available interpolation schemes available in Iris, the ``extrapolation_mode`` keyword must be one of: - * ``extrapolate`` -- the extrapolation points will be calculated by extending the gradient of the closest two points, - * ``error`` -- a ValueError exception will be raised, notifying an attempt to extrapolate, - * ``nan`` -- the extrapolation points will be be set to NaN, - * ``mask`` -- the extrapolation points will always be masked, even if the source data is not a MaskedArray, or - * ``nanmask`` -- if the source data is a MaskedArray the extrapolation points will be masked. Otherwise they will be set to NaN. +* ``extrapolate`` -- the extrapolation points will be calculated by extending the gradient of the closest two points, +* ``error`` -- a ValueError exception will be raised, notifying an attempt to extrapolate, +* ``nan`` -- the extrapolation points will be be set to NaN, +* ``mask`` -- the extrapolation points will always be masked, even if the source data is not a MaskedArray, or +* ``nanmask`` -- if the source data is a MaskedArray the extrapolation points will be masked. Otherwise they will be set to NaN. Using an extrapolation mode is achieved by constructing an interpolation scheme with the extrapolation mode keyword set as required. The constructed scheme @@ -206,8 +206,8 @@ intensive part of an interpolation is setting up the interpolator. To cache an interpolator you must set up an interpolator scheme and call the scheme's interpolator method. The interpolator method takes as arguments: - #. a cube to be interpolated, and - #. an iterable of coordinate names or coordinate instances of the coordinates that are to be interpolated over. +#. a cube to be interpolated, and +#. an iterable of coordinate names or coordinate instances of the coordinates that are to be interpolated over. For example: @@ -244,8 +244,8 @@ regridding is based on the **horizontal** grid of *another cube*. Regridding a cube is achieved with the :meth:`cube.regrid() ` method. This method expects two arguments: - #. *another cube* that defines the target grid onto which the cube should be regridded, and - #. the regridding scheme to use. +#. *another cube* that defines the target grid onto which the cube should be regridded, and +#. the regridding scheme to use. .. note:: @@ -278,15 +278,15 @@ mode when defining the regridding scheme. For the available regridding schemes in Iris, the ``extrapolation_mode`` keyword must be one of: - * ``extrapolate`` -- +* ``extrapolate`` -- - * for :class:`~iris.analysis.Linear` the extrapolation points will be calculated by extending the gradient of the closest two points. - * for :class:`~iris.analysis.Nearest` the extrapolation points will take their value from the nearest source point. + * for :class:`~iris.analysis.Linear` the extrapolation points will be calculated by extending the gradient of the closest two points. + * for :class:`~iris.analysis.Nearest` the extrapolation points will take their value from the nearest source point. - * ``nan`` -- the extrapolation points will be be set to NaN. - * ``error`` -- a ValueError exception will be raised, notifying an attempt to extrapolate. - * ``mask`` -- the extrapolation points will always be masked, even if the source data is not a MaskedArray. - * ``nanmask`` -- if the source data is a MaskedArray the extrapolation points will be masked. Otherwise they will be set to NaN. +* ``nan`` -- the extrapolation points will be be set to NaN. +* ``error`` -- a ValueError exception will be raised, notifying an attempt to extrapolate. +* ``mask`` -- the extrapolation points will always be masked, even if the source data is not a MaskedArray. +* ``nanmask`` -- if the source data is a MaskedArray the extrapolation points will be masked. Otherwise they will be set to NaN. The ``rotated_psl`` cube is defined on a limited area rotated pole grid. If we regridded the ``rotated_psl`` cube onto the global grid as defined by the ``global_air_temp`` cube @@ -395,8 +395,8 @@ intensive part of a regrid is setting up the regridder. To cache a regridder you must set up a regridder scheme and call the scheme's regridder method. The regridder method takes as arguments: - #. a cube (that is to be regridded) defining the source grid, and - #. a cube defining the target grid to regrid the source cube to. +#. a cube (that is to be regridded) defining the source grid, and +#. a cube defining the target grid to regrid the source cube to. For example: diff --git a/docs/src/userguide/iris_cubes.rst b/docs/src/userguide/iris_cubes.rst index d13dee369c..29d8f3cefc 100644 --- a/docs/src/userguide/iris_cubes.rst +++ b/docs/src/userguide/iris_cubes.rst @@ -4,82 +4,105 @@ Iris Data Structures ==================== -The top level object in Iris is called a cube. A cube contains data and metadata about a phenomenon. +The top level object in Iris is called a cube. A cube contains data and +metadata about a phenomenon. -In Iris, a cube is an interpretation of the *Climate and Forecast (CF) Metadata Conventions* whose purpose is to: +In Iris, a cube is an interpretation of the *Climate and Forecast (CF) +Metadata Conventions* whose purpose is to: - *require conforming datasets to contain sufficient metadata that they are self-describing... including physical - units if appropriate, and that each value can be located in space (relative to earth-based coordinates) and time.* +.. panels:: + :container: container-lg pb-3 + :column: col-lg-12 p-2 -Whilst the CF conventions are often mentioned alongside NetCDF, Iris implements several major format importers which can take -files of specific formats and turn them into Iris cubes. Additionally, a framework is provided which allows users -to extend Iris' import capability to cater for specialist or unimplemented formats. + *require conforming datasets to contain sufficient metadata that they are + self-describing... including physical units if appropriate, and that each + value can be located in space (relative to earth-based coordinates) and + time.* -A single cube describes one and only one phenomenon, always has a name, a unit and -an n-dimensional data array to represents the cube's phenomenon. In order to locate the -data spatially, temporally, or in any other higher-dimensional space, a collection of *coordinates* -exist on the cube. + +Whilst the CF conventions are often mentioned alongside NetCDF, Iris implements +several major format importers which can take files of specific formats and +turn them into Iris cubes. Additionally, a framework is provided which allows +users to extend Iris' import capability to cater for specialist or +unimplemented formats. + +A single cube describes one and only one phenomenon, always has a name, a unit +and an n-dimensional data array to represents the cube's phenomenon. In order +to locate the data spatially, temporally, or in any other higher-dimensional +space, a collection of *coordinates* exist on the cube. Coordinates =========== -A coordinate is a container to store metadata about some dimension(s) of a cube's data array and therefore, -by definition, its phenomenon. - - * Each coordinate has a name and a unit. - * When a coordinate is added to a cube, the data dimensions that it represents are also provided. - - * The shape of a coordinate is always the same as the shape of the associated data dimension(s) on the cube. - * A dimension not explicitly listed signifies that the coordinate is independent of that dimension. - * Each dimension of a coordinate must be mapped to a data dimension. The only coordinates with no mapping are - scalar coordinates. - - * Depending on the underlying data that the coordinate is representing, its values may be discrete points or be - bounded to represent interval extents (e.g. temperature at *point x* **vs** rainfall accumulation *between 0000-1200 hours*). - * Coordinates have an attributes dictionary which can hold arbitrary extra metadata, excluding certain restricted CF names - * More complex coordinates may contain a coordinate system which is necessary to fully interpret the values - contained within the coordinate. - +A coordinate is a container to store metadata about some dimension(s) of a +cube's data array and therefore, by definition, its phenomenon. + +* Each coordinate has a name and a unit. +* When a coordinate is added to a cube, the data dimensions that it + represents are also provided. + + * The shape of a coordinate is always the same as the shape of the + associated data dimension(s) on the cube. + * A dimension not explicitly listed signifies that the coordinate is + independent of that dimension. + * Each dimension of a coordinate must be mapped to a data dimension. The + only coordinates with no mapping are scalar coordinates. + +* Depending on the underlying data that the coordinate is representing, its + values may be discrete points or be bounded to represent interval extents + (e.g. temperature at *point x* **vs** rainfall accumulation *between + 0000-1200 hours*). +* Coordinates have an attributes dictionary which can hold arbitrary extra + metadata, excluding certain restricted CF names +* More complex coordinates may contain a coordinate system which is + necessary to fully interpret the values contained within the coordinate. + There are two classes of coordinates: - **DimCoord** - - * Numeric - * Monotonic - * Representative of, at most, a single data dimension (1d) +**DimCoord** + +* Numeric +* Monotonic +* Representative of, at most, a single data dimension (1d) + +**AuxCoord** + +* May be of any type, including strings +* May represent multiple data dimensions (n-dimensional) - **AuxCoord** - - * May be of any type, including strings - * May represent multiple data dimensions (n-dimensional) - Cube ==== A cube consists of: - * a standard name and/or a long name and an appropriate unit - * a data array who's values are representative of the phenomenon - * a collection of coordinates and associated data dimensions on the cube's data array, which are split into two separate lists: +* a standard name and/or a long name and an appropriate unit +* a data array who's values are representative of the phenomenon +* a collection of coordinates and associated data dimensions on the cube's + data array, which are split into two separate lists: + + * *dimension coordinates* - DimCoords which uniquely map to exactly one + data dimension, ordered by dimension. + * *auxiliary coordinates* - DimCoords or AuxCoords which map to as many + data dimensions as the coordinate has dimensions. - * *dimension coordinates* - DimCoords which uniquely map to exactly one data dimension, ordered by dimension. - * *auxiliary coordinates* - DimCoords or AuxCoords which map to as many data dimensions as the coordinate has dimensions. - - * an attributes dictionary which, other than some protected CF names, can hold arbitrary extra metadata. - * a list of cell methods to represent operations which have already been applied to the data (e.g. "mean over time") - * a list of coordinate "factories" used for deriving coordinates from the values of other coordinates in the cube +* an attributes dictionary which, other than some protected CF names, can + hold arbitrary extra metadata. +* a list of cell methods to represent operations which have already been + applied to the data (e.g. "mean over time") +* a list of coordinate "factories" used for deriving coordinates from the + values of other coordinates in the cube Cubes in Practice ----------------- - A Simple Cube Example ===================== -Suppose we have some gridded data which has 24 air temperature readings (in Kelvin) which is located at -4 different longitudes, 2 different latitudes and 3 different heights. Our data array can be represented pictorially: +Suppose we have some gridded data which has 24 air temperature readings +(in Kelvin) which is located at 4 different longitudes, 2 different latitudes +and 3 different heights. Our data array can be represented pictorially: .. image:: multi_array.png @@ -87,61 +110,66 @@ Where dimensions 0, 1, and 2 have lengths 3, 2 and 4 respectively. The Iris cube to represent this data would consist of: - * a standard name of ``air_temperature`` and a unit of ``kelvin`` - * a data array of shape ``(3, 2, 4)`` - * a coordinate, mapping to dimension 0, consisting of: - - * a standard name of ``height`` and unit of ``meters`` - * an array of length 3 representing the 3 ``height`` points - - * a coordinate, mapping to dimension 1, consisting of: - - * a standard name of ``latitude`` and unit of ``degrees`` - * an array of length 2 representing the 2 latitude points - * a coordinate system such that the ``latitude`` points could be fully located on the globe - - * a coordinate, mapping to dimension 2, consisting of: - - * a standard name of ``longitude`` and unit of ``degrees`` - * an array of length 4 representing the 4 longitude points - * a coordinate system such that the ``longitude`` points could be fully located on the globe - +* a standard name of ``air_temperature`` and a unit of ``kelvin`` +* a data array of shape ``(3, 2, 4)`` +* a coordinate, mapping to dimension 0, consisting of: + + * a standard name of ``height`` and unit of ``meters`` + * an array of length 3 representing the 3 ``height`` points +* a coordinate, mapping to dimension 1, consisting of: + * a standard name of ``latitude`` and unit of ``degrees`` + * an array of length 2 representing the 2 latitude points + * a coordinate system such that the ``latitude`` points could be fully + located on the globe -Pictorially the cube has taken on more information than a simple array: +* a coordinate, mapping to dimension 2, consisting of: + + * a standard name of ``longitude`` and unit of ``degrees`` + * an array of length 4 representing the 4 longitude points + * a coordinate system such that the ``longitude`` points could be fully + located on the globe + +Pictorially the cube has taken on more information than a simple array: .. image:: multi_array_to_cube.png -Additionally further information may be optionally attached to the cube. -For example, it is possible to attach any of the following: - - * a coordinate, not mapping to any data dimensions, consisting of: - - * a standard name of ``time`` and unit of ``days since 2000-01-01 00:00`` - * a data array of length 1 representing the time that the data array is valid for - - * an auxiliary coordinate, mapping to dimensions 1 and 2, consisting of: - - * a long name of ``place name`` and no unit - * a 2d string array of shape ``(2, 4)`` with the names of the 8 places that the lat/lons correspond to - - * an auxiliary coordinate "factory", which can derive its own mapping, consisting of: - - * a standard name of ``height`` and a unit of ``feet`` - * knowledge of how data values for this coordinate can be calculated given the ``height in meters`` coordinate - - * a cell method of "mean" over "ensemble" to indicate that the data has been meaned over - a collection of "ensembles" (i.e. multiple model runs). +Additionally further information may be optionally attached to the cube. +For example, it is possible to attach any of the following: + +* a coordinate, not mapping to any data dimensions, consisting of: + + * a standard name of ``time`` and unit of ``days since 2000-01-01 00:00`` + * a data array of length 1 representing the time that the data array is + valid for + +* an auxiliary coordinate, mapping to dimensions 1 and 2, consisting of: + + * a long name of ``place name`` and no unit + * a 2d string array of shape ``(2, 4)`` with the names of the 8 places + that the lat/lons correspond to + +* an auxiliary coordinate "factory", which can derive its own mapping, + consisting of: + + * a standard name of ``height`` and a unit of ``feet`` + * knowledge of how data values for this coordinate can be calculated + given the ``height in meters`` coordinate + +* a cell method of "mean" over "ensemble" to indicate that the data has been + meaned over a collection of "ensembles" (i.e. multiple model runs). Printing a Cube =============== -Every Iris cube can be printed to screen as you will see later in the user guide. It is worth familiarising yourself with the -output as this is the quickest way of inspecting the contents of a cube. Here is the result of printing a real life cube: +Every Iris cube can be printed to screen as you will see later in the user +guide. It is worth familiarising yourself with the output as this is the +quickest way of inspecting the contents of a cube. Here is the result of +printing a real life cube: .. _hybrid_cube_printout: @@ -150,7 +178,7 @@ output as this is the quickest way of inspecting the contents of a cube. Here is import iris filename = iris.sample_data_path('uk_hires.pp') - # NOTE: Every time the output of this cube changes, the full list of deductions below should be re-assessed. + # NOTE: Every time the output of this cube changes, the full list of deductions below should be re-assessed. print(iris.load_cube(filename, 'air_potential_temperature')) .. testoutput:: @@ -178,16 +206,22 @@ output as this is the quickest way of inspecting the contents of a cube. Here is Using this output we can deduce that: - * The cube represents air potential temperature. - * There are 4 data dimensions, and the data has a shape of ``(3, 7, 204, 187)`` - * The 4 data dimensions are mapped to the ``time``, ``model_level_number``, - ``grid_latitude``, ``grid_longitude`` coordinates respectively - * There are three 1d auxiliary coordinates and one 2d auxiliary (``surface_altitude``) - * There is a single ``altitude`` derived coordinate, which spans 3 data dimensions - * There are 7 distinct values in the "model_level_number" coordinate. Similar inferences can - be made for the other dimension coordinates. - * There are 7, not necessarily distinct, values in the ``level_height`` coordinate. - * There is a single ``forecast_reference_time`` scalar coordinate representing the entire cube. - * The cube has one further attribute relating to the phenomenon. - In this case the originating file format, PP, encodes information in a STASH code which in some cases can - be useful for identifying advanced experiment information relating to the phenomenon. +* The cube represents air potential temperature. +* There are 4 data dimensions, and the data has a shape of ``(3, 7, 204, 187)`` +* The 4 data dimensions are mapped to the ``time``, ``model_level_number``, + ``grid_latitude``, ``grid_longitude`` coordinates respectively +* There are three 1d auxiliary coordinates and one 2d auxiliary + (``surface_altitude``) +* There is a single ``altitude`` derived coordinate, which spans 3 data + dimensions +* There are 7 distinct values in the "model_level_number" coordinate. Similar + inferences can + be made for the other dimension coordinates. +* There are 7, not necessarily distinct, values in the ``level_height`` + coordinate. +* There is a single ``forecast_reference_time`` scalar coordinate representing + the entire cube. +* The cube has one further attribute relating to the phenomenon. + In this case the originating file format, PP, encodes information in a STASH + code which in some cases can be useful for identifying advanced experiment + information relating to the phenomenon. diff --git a/docs/src/userguide/loading_iris_cubes.rst b/docs/src/userguide/loading_iris_cubes.rst index 9e9c343300..33ad932d70 100644 --- a/docs/src/userguide/loading_iris_cubes.rst +++ b/docs/src/userguide/loading_iris_cubes.rst @@ -39,15 +39,15 @@ This shows that there were 2 cubes as a result of loading the file, they were: The ``surface_altitude`` cube was 2 dimensional with: - * the two dimensions have extents of 204 and 187 respectively and are - represented by the ``grid_latitude`` and ``grid_longitude`` coordinates. +* the two dimensions have extents of 204 and 187 respectively and are + represented by the ``grid_latitude`` and ``grid_longitude`` coordinates. The ``air_potential_temperature`` cubes were 4 dimensional with: - * the same length ``grid_latitude`` and ``grid_longitude`` dimensions as - ``surface_altitide`` - * a ``time`` dimension of length 3 - * a ``model_level_number`` dimension of length 7 +* the same length ``grid_latitude`` and ``grid_longitude`` dimensions as + ``surface_altitide`` +* a ``time`` dimension of length 3 +* a ``model_level_number`` dimension of length 7 .. note:: @@ -55,7 +55,7 @@ The ``air_potential_temperature`` cubes were 4 dimensional with: (even if it only contains one :class:`iris.cube.Cube` - see :ref:`strict-loading`). Anything that can be done with a Python :class:`list` can be done with an :class:`iris.cube.CubeList`. - + The order of this list should not be relied upon. Ways of loading a specific cube or cubes are covered in :ref:`constrained-loading` and :ref:`strict-loading`. diff --git a/docs/src/userguide/merge_and_concat.rst b/docs/src/userguide/merge_and_concat.rst index e8425df5ec..08c3ce9711 100644 --- a/docs/src/userguide/merge_and_concat.rst +++ b/docs/src/userguide/merge_and_concat.rst @@ -22,14 +22,14 @@ result in fewer cubes as output. The following diagram illustrates the two proce There is one major difference between the ``merge`` and ``concatenate`` processes. - * The ``merge`` process combines multiple input cubes into a - single resultant cube with new dimensions created from the - *scalar coordinate values* of the input cubes. - - * The ``concatenate`` process combines multiple input cubes into a - single resultant cube with the same *number of dimensions* as the input cubes, - but with the length of one or more dimensions extended by *joining together - sequential dimension coordinates*. +* The ``merge`` process combines multiple input cubes into a + single resultant cube with new dimensions created from the + *scalar coordinate values* of the input cubes. + +* The ``concatenate`` process combines multiple input cubes into a + single resultant cube with the same *number of dimensions* as the input cubes, + but with the length of one or more dimensions extended by *joining together + sequential dimension coordinates*. Let's imagine 28 individual cubes representing the temperature at a location ``(y, x)``; one cube for each day of February. We can use diff --git a/docs/src/userguide/plotting_a_cube.rst b/docs/src/userguide/plotting_a_cube.rst index cfb3445d9b..a2334367c5 100644 --- a/docs/src/userguide/plotting_a_cube.rst +++ b/docs/src/userguide/plotting_a_cube.rst @@ -101,15 +101,15 @@ see :py:func:`matplotlib.pyplot.savefig`). Some of the formats which are supported by **plt.savefig**: - ====== ====== ====================================================================== - Format Type Description - ====== ====== ====================================================================== - EPS Vector Encapsulated PostScript - PDF Vector Portable Document Format - PNG Raster Portable Network Graphics, a format with a lossless compression method - PS Vector PostScript, ideal for printer output - SVG Vector Scalable Vector Graphics, XML based - ====== ====== ====================================================================== +====== ====== ====================================================================== +Format Type Description +====== ====== ====================================================================== +EPS Vector Encapsulated PostScript +PDF Vector Portable Document Format +PNG Raster Portable Network Graphics, a format with a lossless compression method +PS Vector PostScript, ideal for printer output +SVG Vector Scalable Vector Graphics, XML based +====== ====== ====================================================================== ****************** Iris Cube Plotting @@ -125,12 +125,12 @@ wrapper functions. As a rule of thumb: - * if you wish to do a visualisation with a cube, use ``iris.plot`` or - ``iris.quickplot``. - * if you wish to show, save or manipulate **any** visualisation, - including ones created with Iris, use ``matplotlib.pyplot``. - * if you wish to create a non cube visualisation, also use - ``matplotlib.pyplot``. +* if you wish to do a visualisation with a cube, use ``iris.plot`` or + ``iris.quickplot``. +* if you wish to show, save or manipulate **any** visualisation, + including ones created with Iris, use ``matplotlib.pyplot``. +* if you wish to create a non cube visualisation, also use + ``matplotlib.pyplot``. The ``iris.quickplot`` module is exactly the same as the ``iris.plot`` module, except that ``quickplot`` will add a title, x and y labels and a colorbar diff --git a/docs/src/userguide/real_and_lazy_data.rst b/docs/src/userguide/real_and_lazy_data.rst index 0bc1846457..9d66a2f086 100644 --- a/docs/src/userguide/real_and_lazy_data.rst +++ b/docs/src/userguide/real_and_lazy_data.rst @@ -140,11 +140,11 @@ Core Data Cubes have the concept of "core data". This returns the cube's data in its current state: - * If a cube has lazy data, calling the cube's :meth:`~iris.cube.Cube.core_data` method - will return the cube's lazy dask array. Calling the cube's - :meth:`~iris.cube.Cube.core_data` method **will never realise** the cube's data. - * If a cube has real data, calling the cube's :meth:`~iris.cube.Cube.core_data` method - will return the cube's real NumPy array. +* If a cube has lazy data, calling the cube's :meth:`~iris.cube.Cube.core_data` method + will return the cube's lazy dask array. Calling the cube's + :meth:`~iris.cube.Cube.core_data` method **will never realise** the cube's data. +* If a cube has real data, calling the cube's :meth:`~iris.cube.Cube.core_data` method + will return the cube's real NumPy array. For example:: @@ -174,14 +174,14 @@ In the same way that Iris cubes contain a data array, Iris coordinates contain a points array and an optional bounds array. Coordinate points and bounds arrays can also be real or lazy: - * A :class:`~iris.coords.DimCoord` will only ever have **real** points and bounds - arrays because of monotonicity checks that realise lazy arrays. - * An :class:`~iris.coords.AuxCoord` can have **real or lazy** points and bounds. - * An :class:`~iris.aux_factory.AuxCoordFactory` (or derived coordinate) - can have **real or lazy** points and bounds. If all of the - :class:`~iris.coords.AuxCoord` instances used to construct the derived coordinate - have real points and bounds then the derived coordinate will have real points - and bounds, otherwise the derived coordinate will have lazy points and bounds. +* A :class:`~iris.coords.DimCoord` will only ever have **real** points and bounds + arrays because of monotonicity checks that realise lazy arrays. +* An :class:`~iris.coords.AuxCoord` can have **real or lazy** points and bounds. +* An :class:`~iris.aux_factory.AuxCoordFactory` (or derived coordinate) + can have **real or lazy** points and bounds. If all of the + :class:`~iris.coords.AuxCoord` instances used to construct the derived coordinate + have real points and bounds then the derived coordinate will have real points + and bounds, otherwise the derived coordinate will have lazy points and bounds. Iris cubes and coordinates have very similar interfaces, which extends to accessing coordinates' lazy points and bounds: diff --git a/docs/src/voted_issues.rst b/docs/src/voted_issues.rst index 473af3351e..7d983448b9 100644 --- a/docs/src/voted_issues.rst +++ b/docs/src/voted_issues.rst @@ -1,6 +1,6 @@ .. include:: common_links.inc -.. _voted_issues: +.. _voted_issues_top: Voted Issues ============ diff --git a/docs/src/whatsnew/3.2.rst b/docs/src/whatsnew/3.2.rst index 4a82b24857..63deb5d459 100644 --- a/docs/src/whatsnew/3.2.rst +++ b/docs/src/whatsnew/3.2.rst @@ -289,7 +289,7 @@ v3.2.1 (11 Mar 2022) #. `@rcomer`_ updated the "Plotting Wind Direction Using Quiver" Gallery example. (:pull:`4120`) -#. `@trexfeathers`_ included `Iris GitHub Discussions`_ in +#. `@trexfeathers`_ included Iris `GitHub Discussions`_ in :ref:`get involved `. (:pull:`4307`) #. `@wjbenfold`_ improved readability in :ref:`userguide interpolation diff --git a/docs/src/whatsnew/dev.rst b/docs/src/whatsnew/dev.rst deleted file mode 100644 index 843815b46b..0000000000 --- a/docs/src/whatsnew/dev.rst +++ /dev/null @@ -1,137 +0,0 @@ -.. include:: ../common_links.inc - -|iris_version| |build_date| [unreleased] -**************************************** - -This document explains the changes made to Iris for this release -(:doc:`View all changes `.) - - -.. dropdown:: :opticon:`report` |iris_version| Release Highlights - :container: + shadow - :title: text-primary text-center font-weight-bold - :body: bg-light - :animate: fade-in - :open: - - The highlights for this minor release of Iris include: - - * N/A - - And finally, get in touch with us on :issue:`GitHub` if you have - any issues or feature requests for improving Iris. Enjoy! - - -📢 Announcements -================ - -#. N/A - - -✨ Features -=========== - -#. `@wjbenfold`_ added support for ``false_easting`` and ``false_northing`` to - :class:`~iris.coord_system.Mercator`. (:issue:`3107`, :pull:`4524`) - -#. `@rcomer`_ implemented lazy aggregation for the - :obj:`iris.analysis.PERCENTILE` aggregator. (:pull:`3901`) - -#. `@pp-mo`_ fixed cube arithmetic operation for cubes with meshes. - (:issue:`4454`, :pull:`4651`) - - -🐛 Bugs Fixed -============= - -#. `@rcomer`_ reverted part of the change from :pull:`3906` so that - :func:`iris.plot.plot` no longer defaults to placing a "Y" coordinate (e.g. - latitude) on the y-axis of the plot. (:issue:`4493`, :pull:`4601`) - -#. `@rcomer`_ enabled passing of scalar objects to :func:`~iris.plot.plot` and - :func:`~iris.plot.scatter`. (:pull:`4616`) - -#. `@rcomer`_ fixed :meth:`~iris.cube.Cube.aggregated_by` with `mdtol` for 1D - cubes where an aggregated section is entirely masked, reported at - :issue:`3190`. (:pull:`4246`) - -#. `@rcomer`_ ensured that a :class:`matplotlib.axes.Axes`'s position is preserved - when Iris replaces it with a :class:`cartopy.mpl.geoaxes.GeoAxes`, fixing - :issue:`1157`. (:pull:`4273`) - -#. `@rcomer`_ fixed :meth:`~iris.coords.Coord.nearest_neighbour_index` for edge - cases where the requested point is float and the coordinate has integer - bounds, reported at :issue:`2969`. (:pull:`4245`) - -#. `@rcomer`_ modified bounds setting on :obj:`~iris.coords.DimCoord` instances - so that the order of the cell bounds is automatically reversed - to match the coordinate's direction if necessary. This is consistent with - the `Bounds for 1-D coordinate variables` subsection of the `Cell Boundaries`_ - section of the CF Conventions and ensures that contiguity is preserved if a - coordinate's direction is reversed. (:issue:`3249`, :issue:`423`, - :issue:`4078`, :issue:`3756`, :pull:`4466`) - - -💣 Incompatible Changes -======================= - -#. N/A - - -🚀 Performance Enhancements -=========================== - -#. N/A - - -🔥 Deprecations -=============== - -#. N/A - - -🔗 Dependencies -=============== - -#. `@rcomer`_ introduced the ``nc-time-axis >=1.4`` minimum pin, reflecting that - we no longer use the deprecated :class:`nc_time_axis.CalendarDateTime` - when plotting against time coordinates. (:pull:`4584`) - - -📚 Documentation -================ - -#. `@tkknight`_ added a page to show the issues that have been voted for. See - :ref:`voted_issues`. (:issue:`3307`, :pull:`4617`) -#. `@wjbenfold`_ added a note about fixing proxy URLs in lockfiles generated - because dependencies have changed. (:pull:`4666`) -#. `@lbdreyer`_ moved most of the User Guide's :class:`iris.Constraint` examples - from :ref:`loading_iris_cubes` to :ref:`cube_extraction` and added an - example of constraining on bounded time. (:pull:`4656`) - - -💼 Internal -=========== - -#. `@trexfeathers`_ and `@pp-mo`_ finished implementing a mature benchmarking - infrastructure (see :ref:`contributing.benchmarks`), building on 2 hard - years of lessons learned 🎉. (:pull:`4477`, :pull:`4562`, :pull:`4571`, - :pull:`4583`, :pull:`4621`) -#. `@wjbenfold`_ used the aforementioned benchmarking infrastructure to - introduce deep (large 3rd dimension) loading and realisation benchmarks. - (:pull:`4654`) -#. `@wjbenfold`_ made :func:`iris.tests.stock.simple_1d` respect the - ``with_bounds`` argument. (:pull:`4658`) - - -.. comment - Whatsnew author names (@github name) in alphabetical order. Note that, - core dev names are automatically included by the common_links.inc: - - - - -.. comment - Whatsnew resources in alphabetical order: - -.. _Cell Boundaries: https://cfconventions.org/Data/cf-conventions/cf-conventions-1.9/cf-conventions.html#cell-boundaries diff --git a/docs/src/whatsnew/index.rst b/docs/src/whatsnew/index.rst index 7e0829da5b..949d727a99 100644 --- a/docs/src/whatsnew/index.rst +++ b/docs/src/whatsnew/index.rst @@ -10,7 +10,7 @@ Iris versions. .. toctree:: :maxdepth: 1 - dev.rst + latest.rst 3.2.rst 3.1.rst 3.0.rst diff --git a/docs/src/whatsnew/latest.rst b/docs/src/whatsnew/latest.rst deleted file mode 120000 index 56aebe92dd..0000000000 --- a/docs/src/whatsnew/latest.rst +++ /dev/null @@ -1 +0,0 @@ -dev.rst \ No newline at end of file diff --git a/docs/src/whatsnew/latest.rst b/docs/src/whatsnew/latest.rst new file mode 100644 index 0000000000..52313d5d81 --- /dev/null +++ b/docs/src/whatsnew/latest.rst @@ -0,0 +1,145 @@ +.. include:: ../common_links.inc + +|iris_version| |build_date| [unreleased] +**************************************** + +This document explains the changes made to Iris for this release +(:doc:`View all changes `.) + + +.. dropdown:: :opticon:`report` |iris_version| Release Highlights + :container: + shadow + :title: text-primary text-center font-weight-bold + :body: bg-light + :animate: fade-in + :open: + + The highlights for this minor release of Iris include: + + * N/A + + And finally, get in touch with us on :issue:`GitHub` if you have + any issues or feature requests for improving Iris. Enjoy! + + +📢 Announcements +================ + +#. N/A + + +✨ Features +=========== + +#. `@wjbenfold`_ added support for ``false_easting`` and ``false_northing`` to + :class:`~iris.coord_system.Mercator`. (:issue:`3107`, :pull:`4524`) + +#. `@rcomer`_ implemented lazy aggregation for the + :obj:`iris.analysis.PERCENTILE` aggregator. (:pull:`3901`) + +#. `@pp-mo`_ fixed cube arithmetic operation for cubes with meshes. + (:issue:`4454`, :pull:`4651`) + + +🐛 Bugs Fixed +============= + +#. `@rcomer`_ reverted part of the change from :pull:`3906` so that + :func:`iris.plot.plot` no longer defaults to placing a "Y" coordinate (e.g. + latitude) on the y-axis of the plot. (:issue:`4493`, :pull:`4601`) + +#. `@rcomer`_ enabled passing of scalar objects to :func:`~iris.plot.plot` and + :func:`~iris.plot.scatter`. (:pull:`4616`) + +#. `@rcomer`_ fixed :meth:`~iris.cube.Cube.aggregated_by` with `mdtol` for 1D + cubes where an aggregated section is entirely masked, reported at + :issue:`3190`. (:pull:`4246`) + +#. `@rcomer`_ ensured that a :class:`matplotlib.axes.Axes`'s position is preserved + when Iris replaces it with a :class:`cartopy.mpl.geoaxes.GeoAxes`, fixing + :issue:`1157`. (:pull:`4273`) + +#. `@rcomer`_ fixed :meth:`~iris.coords.Coord.nearest_neighbour_index` for edge + cases where the requested point is float and the coordinate has integer + bounds, reported at :issue:`2969`. (:pull:`4245`) + +#. `@rcomer`_ modified bounds setting on :obj:`~iris.coords.DimCoord` instances + so that the order of the cell bounds is automatically reversed + to match the coordinate's direction if necessary. This is consistent with + the `Bounds for 1-D coordinate variables` subsection of the `Cell Boundaries`_ + section of the CF Conventions and ensures that contiguity is preserved if a + coordinate's direction is reversed. (:issue:`3249`, :issue:`423`, + :issue:`4078`, :issue:`3756`, :pull:`4466`) + + +💣 Incompatible Changes +======================= + +#. N/A + + +🚀 Performance Enhancements +=========================== + +#. N/A + + +🔥 Deprecations +=============== + +#. N/A + + +🔗 Dependencies +=============== + +#. `@rcomer`_ introduced the ``nc-time-axis >=1.4`` minimum pin, reflecting that + we no longer use the deprecated :class:`nc_time_axis.CalendarDateTime` + when plotting against time coordinates. (:pull:`4584`) + + +📚 Documentation +================ + +#. `@tkknight`_ added a page to show the issues that have been voted for. See + :ref:`voted_issues_top`. (:issue:`3307`, :pull:`4617`) + +#. `@wjbenfold`_ added a note about fixing proxy URLs in lockfiles generated + because dependencies have changed. (:pull:`4666`) + +#. `@lbdreyer`_ moved most of the User Guide's :class:`iris.Constraint` examples + from :ref:`loading_iris_cubes` to :ref:`cube_extraction` and added an + example of constraining on bounded time. (:pull:`4656`) + +#. `@tkknight`_ adopted the `PyData Sphinx Theme`_ for the documentation. + (:discussion:`4344`, :pull:`4661`) + + +💼 Internal +=========== + +#. `@trexfeathers`_ and `@pp-mo`_ finished implementing a mature benchmarking + infrastructure (see :ref:`contributing.benchmarks`), building on 2 hard + years of lessons learned 🎉. (:pull:`4477`, :pull:`4562`, :pull:`4571`, + :pull:`4583`, :pull:`4621`) + +#. `@wjbenfold`_ used the aforementioned benchmarking infrastructure to + introduce deep (large 3rd dimension) loading and realisation benchmarks. + (:pull:`4654`) + +#. `@wjbenfold`_ made :func:`iris.tests.stock.simple_1d` respect the + ``with_bounds`` argument. (:pull:`4658`) + + +.. comment + Whatsnew author names (@github name) in alphabetical order. Note that, + core dev names are automatically included by the common_links.inc: + + + + +.. comment + Whatsnew resources in alphabetical order: + +.. _Cell Boundaries: https://cfconventions.org/Data/cf-conventions/cf-conventions-1.9/cf-conventions.html#cell-boundaries +.. _PyData Sphinx Theme: https://pydata-sphinx-theme.readthedocs.io/en/stable/index.html diff --git a/docs/src/whatsnew/dev.rst.template b/docs/src/whatsnew/latest.rst.template similarity index 100% rename from docs/src/whatsnew/dev.rst.template rename to docs/src/whatsnew/latest.rst.template diff --git a/docs/src/why_iris.rst b/docs/src/why_iris.rst new file mode 100644 index 0000000000..63a515f68e --- /dev/null +++ b/docs/src/why_iris.rst @@ -0,0 +1,44 @@ +.. _why_iris: + +Why Iris +======== + +**A powerful, format-agnostic, community-driven Python package for analysing +and visualising Earth science data.** + +Iris implements a data model based on the `CF conventions `_ +giving you a powerful, format-agnostic interface for working with your data. +It excels when working with multi-dimensional Earth Science data, where tabular +representations become unwieldy and inefficient. + +`CF Standard names `_, +`units `_, and coordinate metadata +are built into Iris, giving you a rich and expressive interface for maintaining +an accurate representation of your data. Its treatment of data and +associated metadata as first-class objects includes: + +.. rst-class:: squarelist + +* visualisation interface based on `matplotlib `_ and + `cartopy `_, +* unit conversion, +* subsetting and extraction, +* merge and concatenate, +* aggregations and reductions (including min, max, mean and weighted averages), +* interpolation and regridding (including nearest-neighbor, linear and + area-weighted), and +* operator overloads (``+``, ``-``, ``*``, ``/``, etc.). + +A number of file formats are recognised by Iris, including CF-compliant NetCDF, +GRIB, and PP, and it has a plugin architecture to allow other formats to be +added seamlessly. + +Building upon `NumPy `_ and +`dask `_, Iris scales from efficient +single-machine workflows right through to multi-core clusters and HPC. +Interoperability with packages from the wider scientific Python ecosystem comes +from Iris' use of standard NumPy/dask arrays as its underlying data storage. + +Iris is part of SciTools, for more information see https://scitools.org.uk/. +For **Iris 2.4** and earlier documentation please see the +:link-badge:`https://scitools.org.uk/iris/docs/v2.4.0/,"legacy documentation",cls=badge-info text-white`. diff --git a/requirements/ci/nox.lock/py38-linux-64.lock b/requirements/ci/nox.lock/py38-linux-64.lock index 7883f1deaa..c91683f0cf 100644 --- a/requirements/ci/nox.lock/py38-linux-64.lock +++ b/requirements/ci/nox.lock/py38-linux-64.lock @@ -1,6 +1,6 @@ # Generated by conda-lock. # platform: linux-64 -# input_hash: d7bddc89ba289d4c1b48871b1289eb0bf45715ef2e274de01e245547fb6a8df6 +# input_hash: c7823bd2d790451e307ab5cbb450b09ee9460f9e8334dbf40f32d8bab4a2fefa @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-2021.10.8-ha878542_0.tar.bz2#575611b8a84f45960e87722eeb51fa26 @@ -39,7 +39,7 @@ https://conda.anaconda.org/conda-forge/linux-64/libiconv-1.16-h516909a_0.tar.bz2 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.18-pthreads_h8fe5266_0.tar.bz2#41532e4448c0cce086d6570f95e4e12e +https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.20-pthreads_h78a6416_0.tar.bz2#9b6d0781953c9e353faee494336cc229 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/libuuid-2.32.1-h7f98852_1000.tar.bz2#772d69f030955d9646d3d0eaf21d859d @@ -64,7 +64,7 @@ https://conda.anaconda.org/conda-forge/linux-64/xxhash-0.8.0-h7f98852_3.tar.bz2# https://conda.anaconda.org/conda-forge/linux-64/xz-5.2.5-h516909a_1.tar.bz2#33f601066901f3e1a85af3522a8113f9 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/libblas-3.9.0-13_linux64_openblas.tar.bz2#8a4038563ed92dfa622bd72c0d8f31d3 +https://conda.anaconda.org/conda-forge/linux-64/libblas-3.9.0-14_linux64_openblas.tar.bz2#fb31fbbde682414550bbe15e3964420f 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/libedit-3.1.20191231-he28a2e2_2.tar.bz2#4d331e44109e3f0e19b4cb8f9b82f3e1 @@ -72,7 +72,7 @@ https://conda.anaconda.org/conda-forge/linux-64/libevent-2.1.10-h9b69904_4.tar.b https://conda.anaconda.org/conda-forge/linux-64/libllvm13-13.0.1-hf817b99_2.tar.bz2#47da3ce0d8b2e65ccb226c186dd91eba 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/mysql-common-8.0.28-haf5c9bc_2.tar.bz2#3593de3e1062c658062a53bf3874ff41 +https://conda.anaconda.org/conda-forge/linux-64/mysql-common-8.0.28-haf5c9bc_3.tar.bz2#3fece7479fccc385e5e4f898277b7983 https://conda.anaconda.org/conda-forge/linux-64/readline-8.1-h46c0cb4_0.tar.bz2#5788de3c8d7a7d64ac56c784c4ef48e6 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 @@ -82,17 +82,17 @@ https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.2-ha95c52a_0.tar.bz2#52 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/hdf4-4.2.15-h10796ff_3.tar.bz2#21a8d66dc17f065023b33145c42652fe 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-13_linux64_openblas.tar.bz2#b17676dbd6688396c3a3076259fb7907 +https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-14_linux64_openblas.tar.bz2#1b41ea4c32014d878e84de4e5690df7a https://conda.anaconda.org/conda-forge/linux-64/libclang-13.0.1-default_hc23dcda_0.tar.bz2#8cebb0736cba83485b13dc10d242d96d https://conda.anaconda.org/conda-forge/linux-64/libglib-2.70.2-h174f98d_4.tar.bz2#d44314ffae96b17657fbf3f8e47b04fc -https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-13_linux64_openblas.tar.bz2#018b80e8f21d8560ae4961567e3e00c9 +https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-14_linux64_openblas.tar.bz2#13367ebd0243a949cee7564b13c3cd42 https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.47.0-h727a467_0.tar.bz2#a22567abfea169ff8048506b1ca9b230 https://conda.anaconda.org/conda-forge/linux-64/libpng-1.6.37-h21135ba_2.tar.bz2#b6acf807307d033d4b7e758b4f44b036 https://conda.anaconda.org/conda-forge/linux-64/libssh2-1.10.0-ha56f1ee_2.tar.bz2#6ab4eaa11ff01801cffca0a27489dc04 https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.3.0-h542a066_3.tar.bz2#1a0efb4dfd880b0376da8e1ba39fa838 https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.9.12-h885dcf4_1.tar.bz2#d1355eaa48f465782f228275a0a69771 https://conda.anaconda.org/conda-forge/linux-64/libzip-1.8.0-h4de3113_1.tar.bz2#175a746a43d42c053b91aa765fbc197d -https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-8.0.28-h28c427c_2.tar.bz2#bc001857cad0d2859f1507f420691bcc +https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-8.0.28-h28c427c_3.tar.bz2#ce5af4269e1b874696380f6ace9a72d7 https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.37.1-h4ff8645_0.tar.bz2#8057ac02d6d10a162d7eb4b0ca7ed291 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 @@ -119,7 +119,7 @@ https://conda.anaconda.org/conda-forge/linux-64/curl-7.82.0-h7bff187_0.tar.bz2#6 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.4-pyhd8ed1ab_0.tar.bz2#7b50d840543d9cdae100e91582c33035 https://conda.anaconda.org/conda-forge/noarch/filelock-3.6.0-pyhd8ed1ab_0.tar.bz2#6e03ca6c7b47a4152a2b12c6eee3bd32 -https://conda.anaconda.org/conda-forge/linux-64/fontconfig-2.13.96-h8e229c2_2.tar.bz2#70d2ba376dff51a33343169642aebb44 +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.3.0-pyhd8ed1ab_0.tar.bz2#960b78685ccbedb992886fc4ce37bf6e https://conda.anaconda.org/conda-forge/linux-64/gst-plugins-base-1.20.1-hcf0ee16_1.tar.bz2#4c41fc47db7d631862f12e5e5c885cb9 https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.12.1-mpi_mpich_h08b82f9_4.tar.bz2#975d5635b158c1b3c5c795f9d0a430a1 @@ -139,6 +139,7 @@ https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.8-2_cp38.tar.bz2#bf https://conda.anaconda.org/conda-forge/noarch/pytz-2022.1-pyhd8ed1ab_0.tar.bz2#b87d66d6d3991d988fb31510c95a9267 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.1-pyhd8ed1ab_0.tar.bz2#d821b295c4bd18ad27e1e19543a5784a https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-applehelp-1.0.2-py_0.tar.bz2#20b2eaeaeea4ef9a9a0d99770620fd09 https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-devhelp-1.0.2-py_0.tar.bz2#68e01cac9d38d0e717cd5c87bc3d2cc9 https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-htmlhelp-2.0.0-pyhd8ed1ab_0.tar.bz2#77dad82eb9c8c1525ff7953e0756d708 @@ -147,9 +148,10 @@ https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-qthelp-1.0.3-py_0.ta https://conda.anaconda.org/conda-forge/noarch/toml-0.10.2-pyhd8ed1ab_0.tar.bz2#f832c45a477c78bebd107098db465095 https://conda.anaconda.org/conda-forge/noarch/toolz-0.11.2-pyhd8ed1ab_0.tar.bz2#f348d1590550371edfac5ed3c1d44f7e 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.7.0-pyhd8ed1ab_1.tar.bz2#b689b2cbc8481b224777415e1a193170 +https://conda.anaconda.org/conda-forge/noarch/zipp-3.8.0-pyhd8ed1ab_0.tar.bz2#050b94cf4a8c760656e51d2d44e4632c 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.9.1-pyh44b312d_0.tar.bz2#74136ed39bfea0832d338df1e58d013e +https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.10.0-pyha770c72_0.tar.bz2#c29e0b4a2ff546cedbe6dc2921948a1d https://conda.anaconda.org/conda-forge/linux-64/cairo-1.16.0-ha12eb4b_1010.tar.bz2#e15c0969bf37df9dae513a48ac871a7d https://conda.anaconda.org/conda-forge/linux-64/certifi-2021.10.8-py38h578d9bd_2.tar.bz2#63a01bce71bc3e8c8e0510ed997d1458 https://conda.anaconda.org/conda-forge/linux-64/cffi-1.15.0-py38h3931269_0.tar.bz2#9c491a90ae11d08ca97326a0ed876f3a @@ -159,8 +161,8 @@ https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.4.2-py38h43d8883_1. https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.3-h283352f_2.tar.bz2#2b0d39005a2e8347f329fe578bd6488a https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.8.1-mpi_mpich_h319fa22_1.tar.bz2#7583fbaea3648f692c0c019254bc196c 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-py38he865349_0.tar.bz2#b1b3d6847a68251a1465206ab466b475 -https://conda.anaconda.org/conda-forge/linux-64/numpy-1.22.3-py38h05e7239_0.tar.bz2#90b4ee61abb81fb3f3995ec9d4c734f0 +https://conda.anaconda.org/conda-forge/linux-64/mpi4py-3.1.3-py38h97ac3a3_1.tar.bz2#7a65afac627e81e2d4c1fef44409dbf5 +https://conda.anaconda.org/conda-forge/linux-64/numpy-1.22.3-py38h05e7239_1.tar.bz2#0856f29eb084060e01a39813fed4dc92 https://conda.anaconda.org/conda-forge/noarch/packaging-21.3-pyhd8ed1ab_0.tar.bz2#71f1ab2de48613876becddd496371c85 https://conda.anaconda.org/conda-forge/noarch/partd-1.2.0-pyhd8ed1ab_0.tar.bz2#0c32f563d7f22e3a34c95cad8cc95651 https://conda.anaconda.org/conda-forge/linux-64/pillow-6.2.1-py38hd70f55b_1.tar.bz2#80d719bee2b77a106b199150c0829107 @@ -171,21 +173,21 @@ https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.8.2-pyhd8ed1ab_0 https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-3.0.0-py38h0a891b7_0.tar.bz2#12eaa8cbfedfbf7879e5653467b03c94 https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0-py38h0a891b7_4.tar.bz2#ba24ff01bb38c5cd5be54b45ef685db3 https://conda.anaconda.org/conda-forge/linux-64/qt-5.12.9-h1304e3e_6.tar.bz2#f2985d160b8c43dd427923c04cd732fe -https://conda.anaconda.org/conda-forge/linux-64/setuptools-61.3.0-py38h578d9bd_0.tar.bz2#9d042d3e103851e2f59433313ff84df4 +https://conda.anaconda.org/conda-forge/linux-64/setuptools-62.0.0-py38h578d9bd_0.tar.bz2#137bed0dfd03cc5a762978b46dc021e3 https://conda.anaconda.org/conda-forge/linux-64/tornado-6.1-py38h0a891b7_3.tar.bz2#d9e2836a4a46935f84b858462d54a7c3 -https://conda.anaconda.org/conda-forge/linux-64/unicodedata2-14.0.0-py38h497a2fe_0.tar.bz2#8da7787169411910df2a62dc8ef533e0 +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.14.0-py38h578d9bd_1.tar.bz2#6c6a5da4d7e2af1dbbfb1e21daa559cc 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.0-py38h3ec907f_0.tar.bz2#35411e5fc8dd523f9e68316847e6a25b -https://conda.anaconda.org/conda-forge/linux-64/cryptography-36.0.2-py38h2b5fc30_0.tar.bz2#22274a82cf664d94a9e7c8f9deddf52a +https://conda.anaconda.org/conda-forge/linux-64/cryptography-36.0.2-py38h2b5fc30_1.tar.bz2#1541e6e63753f197165277eac0d434a1 https://conda.anaconda.org/conda-forge/noarch/dask-core-2022.4.0-pyhd8ed1ab_0.tar.bz2#a47051950a34bef40d8369a5f320b78d -https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.31.2-py38h0a891b7_0.tar.bz2#381ffd61d2617af9bddb1cee60352480 +https://conda.anaconda.org/conda-forge/linux-64/fonttools-4.31.2-py38h0a891b7_1.tar.bz2#46b9f0b776c532c038147915f8fb1e38 https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-4.2.0-h40b6f09_0.tar.bz2#017b20e7e98860f0bfa7492ce16390a7 https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.1-pyhd8ed1ab_0.tar.bz2#40b3f446c61729cdd21ed9d85627df6e https://conda.anaconda.org/conda-forge/linux-64/mo_pack-0.2.0-py38h6c62de6_1006.tar.bz2#829b1209dfadd431a11048d6eeaf5bef https://conda.anaconda.org/conda-forge/linux-64/netcdf-fortran-4.5.4-mpi_mpich_h1364a43_0.tar.bz2#b6ba4f487ef9fd5d353ff277df06d133 https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.6.0-pyhd8ed1ab_0.tar.bz2#0941325bf48969e2b3b19d0951740950 -https://conda.anaconda.org/conda-forge/linux-64/pandas-1.4.1-py38h43a58ef_0.tar.bz2#1083ebe2edc30e4fb9568d1f66e3588b +https://conda.anaconda.org/conda-forge/linux-64/pandas-1.4.2-py38h47df419_0.tar.bz2#52b2e1f0dbcba061f4d83c9879c9bd76 https://conda.anaconda.org/conda-forge/noarch/pip-22.0.4-pyhd8ed1ab_0.tar.bz2#b1239ce8ef2a1eec485c398a683c5bff https://conda.anaconda.org/conda-forge/noarch/pygments-2.11.2-pyhd8ed1ab_0.tar.bz2#caef60540e2239e27bf62569a5015e3b https://conda.anaconda.org/conda-forge/linux-64/pyproj-3.3.0-py38hbc0797c_2.tar.bz2#7e4c695d10aa5e4576e87fb00a9d2511 @@ -209,17 +211,17 @@ https://conda.anaconda.org/conda-forge/linux-64/pyqtwebengine-5.12.1-py38h7400c1 https://conda.anaconda.org/conda-forge/linux-64/cartopy-0.20.2-py38h51d8e34_4.tar.bz2#9f23c80d08456c02ab284f974719b013 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/gtk2-2.24.33-h90689f9_2.tar.bz2#957a0255ab58aaf394a91725d73ab422 -https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.52.5-h0a9e6e8_2.tar.bz2#aa768fdaad03509a97df37f81163346b +https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.52.5-h0a9e6e8_3.tar.bz2#a08562889b985d021550e22443cf0fce https://conda.anaconda.org/conda-forge/noarch/nc-time-axis-1.4.0-pyhd8ed1ab_0.tar.bz2#9113b4e4fa2fa4a7f129c71a6f319475 -https://conda.anaconda.org/conda-forge/linux-64/pre-commit-2.17.0-py38h578d9bd_0.tar.bz2#839ac9dba9a6126c9532781a9ea4506b +https://conda.anaconda.org/conda-forge/linux-64/pre-commit-2.18.1-py38h578d9bd_0.tar.bz2#b590f730888ff2615587968a73248761 https://conda.anaconda.org/conda-forge/linux-64/pyqt-5.12.3-py38h578d9bd_8.tar.bz2#88368a5889f31dff922a2d57bbfc3f5b https://conda.anaconda.org/conda-forge/noarch/urllib3-1.26.9-pyhd8ed1ab_0.tar.bz2#0ea179ee251aa7100807c35bc0252693 https://conda.anaconda.org/conda-forge/linux-64/graphviz-3.0.0-h5abf519_1.tar.bz2#fcaf13b2713335ff871ba551d5bda679 https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.5.1-py38h578d9bd_0.tar.bz2#0d78be9cf1c400ba8e3077cf060492f1 https://conda.anaconda.org/conda-forge/noarch/requests-2.27.1-pyhd8ed1ab_0.tar.bz2#7c1c427246b057b8fa97200ecdb2ed62 +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-4.5.0-pyh6c4a22f_0.tar.bz2#46b38d88c4270ff9ba78a89c83c66345 https://conda.anaconda.org/conda-forge/noarch/sphinx-copybutton-0.5.0-pyhd8ed1ab_0.tar.bz2#4c969cdd5191306c269490f7ff236d9c https://conda.anaconda.org/conda-forge/noarch/sphinx-gallery-0.10.1-pyhd8ed1ab_0.tar.bz2#4918585fe5e5341740f7e63c61743efb https://conda.anaconda.org/conda-forge/noarch/sphinx-panels-0.6.0-pyhd8ed1ab_0.tar.bz2#6eec6480601f5d15babf9c3b3987f34a -https://conda.anaconda.org/conda-forge/noarch/sphinx_rtd_theme-1.0.0-pyhd8ed1ab_0.tar.bz2#9f633f2f2869184e31acfeae95b24345 https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-serializinghtml-1.1.5-pyhd8ed1ab_1.tar.bz2#63d2f874f990fdcab47c822b608d6ade diff --git a/requirements/ci/py38.yml b/requirements/ci/py38.yml index ef095815c9..91cd9d3f5f 100644 --- a/requirements/ci/py38.yml +++ b/requirements/ci/py38.yml @@ -44,4 +44,4 @@ dependencies: - sphinx-copybutton - sphinx-gallery - sphinx-panels - - sphinx_rtd_theme + - pydata-sphinx-theme diff --git a/tools/update_lockfiles.py b/tools/update_lockfiles.py index 9d5705c7a7..f05210be87 100755 --- a/tools/update_lockfiles.py +++ b/tools/update_lockfiles.py @@ -53,7 +53,8 @@ 'lock', '--filename-template', ofile_template, '--file', infile, + '-k', 'explicit', '--platform', 'linux-64' ]) print(f"lockfile saved to {ofile_template}".format(platform='linux-64'), - file=sys.stderr) \ No newline at end of file + file=sys.stderr)