From 7ee078f5da1a370a7a44d51356c23dcd846c577a Mon Sep 17 00:00:00 2001 From: stephenworsley <49274989+stephenworsley@users.noreply.github.com> Date: Thu, 8 Apr 2021 16:20:19 +0100 Subject: [PATCH 01/11] Update unstructured_scheme Feature Branch (#51) * Unstructured scheme skeleton (#31) * create skeleton of unstructured regridding code This code is primarily based on Iris's current Area weighted regridding scheme, with the required adaptions for accepting unstructred UGRID data. * Unstructured Scheme - Basic GridInfo Handling (#32) Add basic `GridInfo` function and associated tests. It returns an ESMF regridding object for gridded data. * Unstructured scheme iris source (#33) * refresh cirrus-ci and nox * add iris artifact support * deal with special-case cirrus-ci quoting * review actions * Unstructured Scheme - Basic MeshInfo Handling (#36) * provide iris Mesh to MeshInfo conversion * test against iris feature branch * lint fixes and documentation * fix typo * address review comments. * change tests to better reflect UGRID orientation * address review comments. * address review comments * ASCII art fix * Unstructured Scheme - Cube Creation 2D (#39) * support creation of 2D cubes * lint fixes * rewrite test * remove mesh from _create_cube arguments * add TODO * add TODO * Unstructured Scheme - Mesh Fetching (#46) * support mesh fetching * fix docstring * lint fix * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add docstrings * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add and restructure tests * fix test * fix test * address review comments * address review comments Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Bill Little Co-authored-by: Anna Booton Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .cirrus.yml | 2 + .../experimental/unstructured_scheme.py | 195 ++++++++++++++++++ .../unstructured_scheme/__init__.py | 1 + .../test_MeshToGridESMFRegridder.py | 58 ++++++ .../unstructured_scheme/test__create_cube.py | 37 ++++ .../test__cube_to_GridInfo.py | 94 +++++++++ .../test__mesh_to_MeshInfo.py | 112 ++++++++++ ...id_unstructured_to_rectilinear__prepare.py | 70 +++++++ ...test_regrid_unstructured_to_rectilinear.py | 57 +++++ requirements/py36.yml | 1 + requirements/py37.yml | 1 + requirements/py38.yml | 1 + 12 files changed, 629 insertions(+) create mode 100644 esmf_regrid/experimental/unstructured_scheme.py create mode 100644 esmf_regrid/tests/unit/experimental/unstructured_scheme/__init__.py create mode 100644 esmf_regrid/tests/unit/experimental/unstructured_scheme/test_MeshToGridESMFRegridder.py create mode 100644 esmf_regrid/tests/unit/experimental/unstructured_scheme/test__create_cube.py create mode 100644 esmf_regrid/tests/unit/experimental/unstructured_scheme/test__cube_to_GridInfo.py create mode 100644 esmf_regrid/tests/unit/experimental/unstructured_scheme/test__mesh_to_MeshInfo.py create mode 100644 esmf_regrid/tests/unit/experimental/unstructured_scheme/test__regrid_unstructured_to_rectilinear__prepare.py create mode 100644 esmf_regrid/tests/unit/experimental/unstructured_scheme/test_regrid_unstructured_to_rectilinear.py diff --git a/.cirrus.yml b/.cirrus.yml index 1fb42180..9d7ef969 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -30,6 +30,8 @@ env: PIP_CACHE_PACKAGES: "pip setuptools wheel nox pyyaml" # Conda packages to be installed. CONDA_CACHE_PACKAGES: "nox pip pyyaml" + # Use specific custom iris source feature branch. + IRIS_SOURCE: "github:mesh-data-model" # diff --git a/esmf_regrid/experimental/unstructured_scheme.py b/esmf_regrid/experimental/unstructured_scheme.py new file mode 100644 index 00000000..6ef03cd1 --- /dev/null +++ b/esmf_regrid/experimental/unstructured_scheme.py @@ -0,0 +1,195 @@ +"""Provides an iris interface for unstructured regridding.""" + +import copy + +import iris +from iris.analysis._interpolation import get_xy_dim_coords +import numpy as np + +# from numpy import ma + +from esmf_regrid.esmf_regridder import GridInfo, Regridder +from esmf_regrid.experimental.unstructured_regrid import MeshInfo + + +# Taken from PR #26 +def _bounds_cf_to_simple_1d(cf_bounds): + assert (cf_bounds[1:, 0] == cf_bounds[:-1, 1]).all() + simple_bounds = np.empty((cf_bounds.shape[0] + 1,), dtype=np.float64) + simple_bounds[:-1] = cf_bounds[:, 0] + simple_bounds[-1] = cf_bounds[-1, 1] + return simple_bounds + + +def _mesh_to_MeshInfo(mesh): + # Returns a MeshInfo object describing the mesh of the cube. + assert mesh.topology_dimension == 2 + meshinfo = MeshInfo( + np.stack([coord.points for coord in mesh.node_coords], axis=-1), + mesh.face_node_connectivity.indices, + mesh.face_node_connectivity.start_index, + ) + return meshinfo + + +def _cube_to_GridInfo(cube): + # This is a simplified version of an equivalent function/method in PR #26. + # It is anticipated that this function will be replaced by the one in PR #26. + # + # Returns a GridInfo object describing the horizontal grid of the cube. + # This may be inherited from code written for the rectilinear regridding scheme. + lon = cube.coord("longitude") + lat = cube.coord("latitude") + # Ensure coords come from a proper grid. + assert isinstance(lon, iris.coords.DimCoord) + assert isinstance(lat, iris.coords.DimCoord) + # TODO: accomodate other x/y coords. + # TODO: perform checks on lat/lon. + # Checks may cover units, coord systems (e.g. rotated pole), contiguous bounds. + return GridInfo( + lon.points, + lat.points, + _bounds_cf_to_simple_1d(lon.bounds), + _bounds_cf_to_simple_1d(lat.bounds), + circular=lon.circular, + ) + + +# def _regrid_along_dims(regridder, data, src_dim, mdtol): +# # Before regridding, data is transposed to a standard form. +# # This will be done either with something like the following code +# # or else done within the regridder by specifying args. +# # new_axes = list(range(len(data.shape))) +# # new_axes.pop(src_dim) +# # new_axes.append(src_dim) +# # data = ma.transpose(data, axes=new_axes) +# +# result = regridder.regrid(data, mdtol=mdtol) +# return result + + +def _create_cube(data, src_cube, mesh_dim, grid_x, grid_y): + # Here we expect the args to be as follows: + # data: a masked array containing the result of the regridding operation + # src_cube: the source cube which data is regrid from + # mesh_dim: the dimension on src_cube which the mesh belongs to + # mesh: the Mesh (or MeshCoord) object belonging to src_cube + # grid_x: the coordinate on the target cube representing the x axis + # grid_y: the coordinate on the target cube representing the y axis + + new_cube = iris.cube.Cube(data) + + # TODO: The following code assumes a 1D source cube and mesh_dim = 0. + # This is therefore simple code which should be updated when we start + # supporting the regridding of extra dimensions. + + # TODO: The following code is rigid with respect to which dimensions + # the x coord and y coord are assigned to. We should decide if it is + # appropriate to copy the dimension ordering from the target cube + # instead. + new_cube.add_dim_coord(grid_x, mesh_dim + 1) + new_cube.add_dim_coord(grid_y, mesh_dim) + + new_cube.metadata = copy.deepcopy(src_cube.metadata) + + for coord in src_cube.coords(dimensions=()): + new_cube.add_aux_coord(coord.copy()) + + return new_cube + + +def _regrid_unstructured_to_rectilinear__prepare(src_mesh_cube, target_grid_cube): + # TODO: Perform checks on the arguments. (grid coords are contiguous, + # spherical and monotonic. Mesh is defined on faces) + + # TODO: Account for differences in units. + + # TODO: Account for differences in coord systems. + + # TODO: Record appropriate dimensions (i.e. which dimension the mesh belongs to) + + grid_x, grid_y = get_xy_dim_coords(target_grid_cube) + mesh = src_mesh_cube.mesh + # TODO: Improve the checking of mesh validity. Check the mesh location and + # raise appropriate error messages. + assert mesh is not None + # From src_mesh_cube, fetch the mesh, and the dimension on the cube which that + # mesh belongs to. + mesh_dim = src_mesh_cube.mesh_dim() + + meshinfo = _mesh_to_MeshInfo(mesh) + gridinfo = _cube_to_GridInfo(target_grid_cube) + + regridder = Regridder(meshinfo, gridinfo) + + regrid_info = (mesh_dim, grid_x, grid_y, regridder) + + return regrid_info + + +def _regrid_unstructured_to_rectilinear__perform(src_cube, regrid_info, mdtol): + mesh_dim, grid_x, grid_y, regridder = regrid_info + + # Perform regridding with realised data for the moment. This may be changed + # in future to handle src_cube.lazy_data. + new_data = regridder.regrid(src_cube.data, mdtol=mdtol) + # When we want to handle extra dimensions, we may want to do something like: + # new_data = _regrid_along_dims(src_cube.data, mesh_dim, mdtol) + + new_cube = _create_cube( + new_data, + src_cube, + mesh_dim, + grid_x, + grid_y, + ) + + # TODO: apply tweaks to created cube (slice out length 1 dimensions) + + return new_cube + + +def regrid_unstructured_to_rectilinear(src_cube, grid_cube, mdtol=0): + """TODO: write docstring.""" + regrid_info = _regrid_unstructured_to_rectilinear__prepare(src_cube, grid_cube) + result = _regrid_unstructured_to_rectilinear__perform(src_cube, regrid_info, mdtol) + return result + + +class MeshToGridESMFRegridder: + """TODO: write docstring.""" + + def __init__(self, src_mesh_cube, target_grid_cube, mdtol=1): + """TODO: write docstring.""" + # TODO: Record information about the identity of the mesh. This would + # typically be a copy of the mesh, though given the potential size of + # the mesh, it may make sense to either retain a reference to the actual + # mesh or else something like a hash of the mesh. + + # Missing data tolerance. + # Code directly copied from iris. + if not (0 <= mdtol <= 1): + msg = "Value for mdtol must be in range 0 - 1, got {}." + raise ValueError(msg.format(mdtol)) + self.mdtol = mdtol + + partial_regrid_info = _regrid_unstructured_to_rectilinear__prepare( + src_mesh_cube, target_grid_cube + ) + + # Store regrid info. + _, self.grid_x, self.grid_y, self.regridder = partial_regrid_info + + def __call__(self, cube): + """TODO: write docstring.""" + mesh = cube.mesh + # TODO: Ensure cube has the same mesh as that of the recorded mesh. + # For the time being, we simply check that the mesh exists. + assert mesh is not None + mesh_dim = cube.mesh_dim() + + regrid_info = (mesh_dim, self.grid_x, self.grid_y, self.regridder) + + return _regrid_unstructured_to_rectilinear__perform( + cube, regrid_info, self.mdtol + ) diff --git a/esmf_regrid/tests/unit/experimental/unstructured_scheme/__init__.py b/esmf_regrid/tests/unit/experimental/unstructured_scheme/__init__.py new file mode 100644 index 00000000..0a6654c6 --- /dev/null +++ b/esmf_regrid/tests/unit/experimental/unstructured_scheme/__init__.py @@ -0,0 +1 @@ +"""Unit tests for :mod:`esmf_regrid.experimental.unstructured_scheme`.""" diff --git a/esmf_regrid/tests/unit/experimental/unstructured_scheme/test_MeshToGridESMFRegridder.py b/esmf_regrid/tests/unit/experimental/unstructured_scheme/test_MeshToGridESMFRegridder.py new file mode 100644 index 00000000..6141aff5 --- /dev/null +++ b/esmf_regrid/tests/unit/experimental/unstructured_scheme/test_MeshToGridESMFRegridder.py @@ -0,0 +1,58 @@ +"""Unit tests for :func:`esmf_regrid.experimental.unstructured_scheme.MeshToGridESMFRegridder`.""" + +from iris.coords import AuxCoord, DimCoord +import numpy as np + +from esmf_regrid.experimental.unstructured_scheme import ( + MeshToGridESMFRegridder, +) +from esmf_regrid.tests.unit.experimental.unstructured_scheme.test__cube_to_GridInfo import ( + _grid_cube, +) +from esmf_regrid.tests.unit.experimental.unstructured_scheme.test__regrid_unstructured_to_rectilinear__prepare import ( + _flat_mesh_cube, +) + + +def test_flat_cubes(): + """ + Basic test for :func:`esmf_regrid.experimental.unstructured_scheme.MeshToGridESMFRegridder`. + + Tests with flat cubes as input (a 1D mesh cube and a 2D grid cube). + """ + src = _flat_mesh_cube() + + n_lons = 6 + n_lats = 5 + lon_bounds = (-180, 180) + lat_bounds = (-90, 90) + tgt = _grid_cube(n_lons, n_lats, lon_bounds, lat_bounds, circular=True) + # Ensure data in the target grid is different to the expected data. + # i.e. target grid data is all zero, expected data is all one + tgt.data[:] = 0 + + def _add_metadata(cube): + result = cube.copy() + result.units = "K" + result.attributes = {"a": 1} + result.standard_name = "air_temperature" + scalar_height = AuxCoord([5], units="m", standard_name="height") + scalar_time = DimCoord([10], units="s", standard_name="time") + result.add_aux_coord(scalar_height) + result.add_aux_coord(scalar_time) + return result + + src = _add_metadata(src) + src.data[:] = 1 # Ensure all data in the source is one. + regridder = MeshToGridESMFRegridder(src, tgt) + result = regridder(src) + + expected_data = np.ones([n_lats, n_lons]) + expected_cube = _add_metadata(tgt) + + # Lenient check for data. + assert np.allclose(expected_data, result.data) + + # Check metadata and scalar coords. + expected_cube.data = result.data + assert expected_cube == result diff --git a/esmf_regrid/tests/unit/experimental/unstructured_scheme/test__create_cube.py b/esmf_regrid/tests/unit/experimental/unstructured_scheme/test__create_cube.py new file mode 100644 index 00000000..1d9cc33d --- /dev/null +++ b/esmf_regrid/tests/unit/experimental/unstructured_scheme/test__create_cube.py @@ -0,0 +1,37 @@ +"""Unit tests for miscellaneous helper functions in `esmf_regrid.experimental.unstructured_scheme`.""" + +import iris +import numpy as np + +from esmf_regrid.experimental.unstructured_scheme import _create_cube + + +def test_create_cube_2D(): + """Test creation of 2D output grid.""" + data = np.ones([2, 3]) + + # Create a source cube with metadata and scalar coords + src_cube = iris.cube.Cube(np.zeros(5)) + src_cube.units = "K" + src_cube.attributes = {"a": 1} + src_cube.standard_name = "air_temperature" + scalar_height = iris.coords.AuxCoord([5], units="m", standard_name="height") + scalar_time = iris.coords.DimCoord([10], units="s", standard_name="time") + src_cube.add_aux_coord(scalar_height) + src_cube.add_aux_coord(scalar_time) + + mesh_dim = 0 + + grid_x = iris.coords.DimCoord(np.arange(3), standard_name="longitude") + grid_y = iris.coords.DimCoord(np.arange(2), standard_name="latitude") + + cube = _create_cube(data, src_cube, mesh_dim, grid_x, grid_y) + src_metadata = src_cube.metadata + + expected_cube = iris.cube.Cube(data) + expected_cube.metadata = src_metadata + expected_cube.add_dim_coord(grid_x, 1) + expected_cube.add_dim_coord(grid_y, 0) + expected_cube.add_aux_coord(scalar_height) + expected_cube.add_aux_coord(scalar_time) + assert expected_cube == cube diff --git a/esmf_regrid/tests/unit/experimental/unstructured_scheme/test__cube_to_GridInfo.py b/esmf_regrid/tests/unit/experimental/unstructured_scheme/test__cube_to_GridInfo.py new file mode 100644 index 00000000..96cd54db --- /dev/null +++ b/esmf_regrid/tests/unit/experimental/unstructured_scheme/test__cube_to_GridInfo.py @@ -0,0 +1,94 @@ +"""Unit tests for miscellaneous helper functions in `esmf_regrid.experimental.unstructured_scheme`.""" + +from iris.coords import DimCoord +from iris.cube import Cube +import numpy as np +import scipy.sparse + +from esmf_regrid.esmf_regridder import Regridder +from esmf_regrid.experimental.unstructured_scheme import _cube_to_GridInfo + + +def _generate_points_and_bounds(n, outer_bounds): + lower, upper = outer_bounds + full_span = np.linspace(lower, upper, n * 2 + 1) + points = full_span[1::2] + bound_span = full_span[::2] + bounds = np.stack([bound_span[:-1], bound_span[1:]], axis=-1) + return points, bounds + + +def _grid_cube(n_lons, n_lats, lon_outer_bounds, lat_outer_bounds, circular=False): + lon_points, lon_bounds = _generate_points_and_bounds(n_lons, lon_outer_bounds) + lon = DimCoord( + lon_points, "longitude", units="degrees", bounds=lon_bounds, circular=circular + ) + lat_points, lat_bounds = _generate_points_and_bounds(n_lats, lat_outer_bounds) + lat = DimCoord(lat_points, "latitude", units="degrees", bounds=lat_bounds) + + data = np.zeros([n_lats, n_lons]) + cube = Cube(data) + cube.add_dim_coord(lon, 1) + cube.add_dim_coord(lat, 0) + return cube + + +def test_global_grid(): + """Test conversion of a global grid.""" + n_lons = 6 + n_lats = 5 + lon_bounds = (-180, 180) + lat_bounds = (-90, 90) + + cube = _grid_cube(n_lons, n_lats, lon_bounds, lat_bounds, circular=True) + gridinfo = _cube_to_GridInfo(cube) + # Ensure conversion to ESMF works without error + _ = gridinfo.make_esmf_field() + + # The following test ensures there are no overlapping cells. + # This catches geometric/topological abnormalities that would arise from, + # for example: switching lat/lon values, using euclidean coords vs spherical. + rg = Regridder(gridinfo, gridinfo) + expected_weights = scipy.sparse.identity(n_lats * n_lons) + assert np.array_equal(expected_weights.todense(), rg.weight_matrix.todense()) + + +def test_local_grid(): + """Test conversion of a local grid.""" + n_lons = 6 + n_lats = 5 + lon_bounds = (-20, 20) + lat_bounds = (20, 60) + + cube = _grid_cube(n_lons, n_lats, lon_bounds, lat_bounds) + gridinfo = _cube_to_GridInfo(cube) + # Ensure conversion to ESMF works without error + _ = gridinfo.make_esmf_field() + + # The following test ensures there are no overlapping cells. + # Note that this test fails when longitude is circular. + rg = Regridder(gridinfo, gridinfo) + expected_weights = scipy.sparse.identity(n_lats * n_lons) + assert np.array_equal(expected_weights.todense(), rg.weight_matrix.todense()) + + +def test_grid_with_scalars(): + """Test conversion of a grid with scalar coords.""" + n_lons = 1 + n_lats = 5 + lon_bounds = (-20, 20) + lat_bounds = (20, 60) + + cube = _grid_cube(n_lons, n_lats, lon_bounds, lat_bounds) + # Convert longitude to a scalar + cube = cube[:, 0] + assert len(cube.shape) == 1 + + gridinfo = _cube_to_GridInfo(cube) + # Ensure conversion to ESMF works without error + _ = gridinfo.make_esmf_field() + + # The following test ensures there are no overlapping cells. + rg = Regridder(gridinfo, gridinfo) + expected_weights = scipy.sparse.identity(n_lats * n_lons) + assert np.array_equal(expected_weights.todense(), rg.weight_matrix.todense()) diff --git a/esmf_regrid/tests/unit/experimental/unstructured_scheme/test__mesh_to_MeshInfo.py b/esmf_regrid/tests/unit/experimental/unstructured_scheme/test__mesh_to_MeshInfo.py new file mode 100644 index 00000000..229fba68 --- /dev/null +++ b/esmf_regrid/tests/unit/experimental/unstructured_scheme/test__mesh_to_MeshInfo.py @@ -0,0 +1,112 @@ +"""Unit tests for :func:`esmf_regrid.experimental.unstructured_scheme._mesh_to_MeshInfo`.""" + +from iris.coords import AuxCoord +from iris.experimental.ugrid import Connectivity, Mesh +import numpy as np +from numpy import ma +import scipy.sparse + +from esmf_regrid.esmf_regridder import Regridder +from esmf_regrid.experimental.unstructured_scheme import _mesh_to_MeshInfo + + +def _pyramid_topology_connectivity_array(): + """ + Generate the face_node_connectivity array for a topological pyramid. + + The mesh described is a topological pyramid in the sense that there + exists a polygonal base (described by the indices [0, 1, 2, 3, 4]) + and all other faces are triangles connected to a single node (the node + with index 5). + """ + fnc_array = [ + [0, 1, 2, 3, 4], + [1, 0, 5, -1, -1], + [2, 1, 5, -1, -1], + [3, 2, 5, -1, -1], + [4, 3, 5, -1, -1], + [0, 4, 5, -1, -1], + ] + fnc_mask = [ + [0, 0, 0, 0, 0], + [0, 0, 0, 1, 1], + [0, 0, 0, 1, 1], + [0, 0, 0, 1, 1], + [0, 0, 0, 1, 1], + [0, 0, 0, 1, 1], + ] + fnc_ma = ma.array(fnc_array, mask=fnc_mask) + return fnc_ma + + +def _example_mesh(): + """Generate a global mesh with a pentagonal pyramid topology.""" + # The base of the pyramid is the following pentagon. + # + # 60 0 3 + # | \ / | + # 10 | 4 | + # | | + # | | + # -60 1----------2 + # + # 120 180 -120 + # + # The point of the pyramid is at the coordinate (0, 0). + # The geometry is designed so that a valid ESMF object is only produced when + # the orientation is correct (the face nodes are visited in an anticlockwise + # order). This sensitivity is due to the base of the pyramid being convex. + + # Generate face_node_connectivity (fnc). + fnc_ma = _pyramid_topology_connectivity_array() + fnc = Connectivity( + fnc_ma, + cf_role="face_node_connectivity", + start_index=0, + ) + lon_values = [120, 120, -120, -120, 180, 0] + lat_values = [60, -60, -60, 60, 10, 0] + lons = AuxCoord(lon_values, standard_name="longitude") + lats = AuxCoord(lat_values, standard_name="latitude") + mesh = Mesh(2, ((lons, "x"), (lats, "y")), fnc) + return mesh + + +def test__mesh_to_MeshInfo(): + """Basic test for :func:`esmf_regrid.experimental.unstructured_scheme._mesh_to_MeshInfo`.""" + mesh = _example_mesh() + meshinfo = _mesh_to_MeshInfo(mesh) + + expected_nodes = np.array( + [ + [120, 60], + [120, -60], + [-120, -60], + [-120, 60], + [180, 10], + [0, 0], + ] + ) + assert np.array_equal(expected_nodes, meshinfo.node_coords) + + expected_connectivity = _pyramid_topology_connectivity_array() + assert np.array_equal(expected_connectivity, meshinfo.fnc) + + expected_start_index = 0 + assert expected_start_index == meshinfo.esi + + +def test_anticlockwise_validity(): + """Test validity of objects derived from Mesh objects with anticlockwise orientation.""" + mesh = _example_mesh() + meshinfo = _mesh_to_MeshInfo(mesh) + + # Ensure conversion to ESMF works without error. + _ = meshinfo.make_esmf_field() + + # The following test ensures there are no overlapping cells. + # This catches geometric/topological abnormalities that would arise from, + # for example: switching lat/lon values, using euclidean coords vs spherical. + rg = Regridder(meshinfo, meshinfo) + expected_weights = scipy.sparse.identity(meshinfo.size()) + assert np.allclose(expected_weights.todense(), rg.weight_matrix.todense()) diff --git a/esmf_regrid/tests/unit/experimental/unstructured_scheme/test__regrid_unstructured_to_rectilinear__prepare.py b/esmf_regrid/tests/unit/experimental/unstructured_scheme/test__regrid_unstructured_to_rectilinear__prepare.py new file mode 100644 index 00000000..4fce173a --- /dev/null +++ b/esmf_regrid/tests/unit/experimental/unstructured_scheme/test__regrid_unstructured_to_rectilinear__prepare.py @@ -0,0 +1,70 @@ +"""Unit tests for :func:`esmf_regrid.experimental.unstructured_scheme._regrid_unstructured_to_rectilinear__prepare`.""" + +from iris.coords import AuxCoord +from iris.cube import Cube +import numpy as np + +from esmf_regrid.esmf_regridder import GridInfo +from esmf_regrid.experimental.unstructured_regrid import MeshInfo +from esmf_regrid.experimental.unstructured_scheme import ( + _regrid_unstructured_to_rectilinear__prepare, +) +from esmf_regrid.tests.unit.experimental.unstructured_scheme.test__cube_to_GridInfo import ( + _grid_cube, +) +from esmf_regrid.tests.unit.experimental.unstructured_scheme.test__mesh_to_MeshInfo import ( + _example_mesh, +) + + +def _full_mesh(): + mesh = _example_mesh() + + # In order to add a mesh to a cube, face locations must be added. + # These are not used in calculations and are here given a value of zero. + mesh_length = mesh.connectivity(contains_face=True).shape[0] + dummy_face_lon = AuxCoord(np.zeros(mesh_length), standard_name="longitude") + dummy_face_lat = AuxCoord(np.zeros(mesh_length), standard_name="latitude") + mesh.add_coords(face_x=dummy_face_lon, face_y=dummy_face_lat) + mesh.long_name = "example mesh" + return mesh + + +def _flat_mesh_cube(): + """ + Return a 1D cube with a mesh attached. + + Returned cube has no metadata except for the mesh and two MeshCoords. + Returned cube has data consisting of an array of ones. + """ + mesh = _full_mesh() + mesh_length = mesh.connectivity(contains_face=True).shape[0] + + cube = Cube(np.ones([mesh_length])) + mesh_coord_x, mesh_coord_y = mesh.to_MeshCoords("face") + cube.add_aux_coord(mesh_coord_x, 0) + cube.add_aux_coord(mesh_coord_y, 0) + return cube + + +def test_flat_cubes(): + """ + Basic test for :func:`esmf_regrid.experimental.unstructured_scheme._regrid_unstructured_to_rectilinear__prepare`. + + Tests with flat cubes as input (a 1D mesh cube and a 2D grid cube). + """ + src = _flat_mesh_cube() + + n_lons = 6 + n_lats = 5 + lon_bounds = (-180, 180) + lat_bounds = (-90, 90) + tgt = _grid_cube(n_lons, n_lats, lon_bounds, lat_bounds, circular=True) + regrid_info = _regrid_unstructured_to_rectilinear__prepare(src, tgt) + mesh_dim, grid_x, grid_y, regridder = regrid_info + + assert mesh_dim == 0 + assert grid_x == tgt.coord("longitude") + assert grid_y == tgt.coord("latitude") + assert type(regridder.tgt) == GridInfo + assert type(regridder.src) == MeshInfo diff --git a/esmf_regrid/tests/unit/experimental/unstructured_scheme/test_regrid_unstructured_to_rectilinear.py b/esmf_regrid/tests/unit/experimental/unstructured_scheme/test_regrid_unstructured_to_rectilinear.py new file mode 100644 index 00000000..96240a8d --- /dev/null +++ b/esmf_regrid/tests/unit/experimental/unstructured_scheme/test_regrid_unstructured_to_rectilinear.py @@ -0,0 +1,57 @@ +"""Unit tests for :func:`esmf_regrid.experimental.unstructured_scheme.regrid_unstructured_to_rectilinear`.""" + +from iris.coords import AuxCoord, DimCoord +import numpy as np + +from esmf_regrid.experimental.unstructured_scheme import ( + regrid_unstructured_to_rectilinear, +) +from esmf_regrid.tests.unit.experimental.unstructured_scheme.test__cube_to_GridInfo import ( + _grid_cube, +) +from esmf_regrid.tests.unit.experimental.unstructured_scheme.test__regrid_unstructured_to_rectilinear__prepare import ( + _flat_mesh_cube, +) + + +def test_flat_cubes(): + """ + Basic test for :func:`esmf_regrid.experimental.unstructured_scheme.regrid_unstructured_to_rectilinear`. + + Tests with flat cubes as input (a 1D mesh cube and a 2D grid cube). + """ + src = _flat_mesh_cube() + + n_lons = 6 + n_lats = 5 + lon_bounds = (-180, 180) + lat_bounds = (-90, 90) + tgt = _grid_cube(n_lons, n_lats, lon_bounds, lat_bounds, circular=True) + # Ensure data in the target grid is different to the expected data. + # i.e. target grid data is all zero, expected data is all one + tgt.data[:] = 0 + + def _add_metadata(cube): + result = cube.copy() + result.units = "K" + result.attributes = {"a": 1} + result.standard_name = "air_temperature" + scalar_height = AuxCoord([5], units="m", standard_name="height") + scalar_time = DimCoord([10], units="s", standard_name="time") + result.add_aux_coord(scalar_height) + result.add_aux_coord(scalar_time) + return result + + src = _add_metadata(src) + src.data[:] = 1 # Ensure all data in the source is one. + result = regrid_unstructured_to_rectilinear(src, tgt) + + expected_data = np.ones([n_lats, n_lons]) + expected_cube = _add_metadata(tgt) + + # Lenient check for data. + assert np.allclose(expected_data, result.data) + + # Check metadata and scalar coords. + expected_cube.data = result.data + assert expected_cube == result diff --git a/requirements/py36.yml b/requirements/py36.yml index 5852ab89..faa237a4 100644 --- a/requirements/py36.yml +++ b/requirements/py36.yml @@ -11,6 +11,7 @@ dependencies: # Core dependencies. - cartopy>=0.18 + - iris - numpy>=1.14 - scipy - esmpy>=7.0 diff --git a/requirements/py37.yml b/requirements/py37.yml index b27bea38..ff066dae 100644 --- a/requirements/py37.yml +++ b/requirements/py37.yml @@ -11,6 +11,7 @@ dependencies: # Core dependencies. - cartopy>=0.18 + - iris - numpy>=1.14 - scipy - esmpy>=7.0 diff --git a/requirements/py38.yml b/requirements/py38.yml index d3cdcaf3..96d0306e 100644 --- a/requirements/py38.yml +++ b/requirements/py38.yml @@ -11,6 +11,7 @@ dependencies: # Core dependencies. - cartopy>=0.18 + - iris - numpy>=1.14 - scipy - esmpy>=7.0 From 7c01ece86d5949a2235617924a33551a19c13471 Mon Sep 17 00:00:00 2001 From: stephenworsley <49274989+stephenworsley@users.noreply.github.com> Date: Thu, 8 Apr 2021 17:10:43 +0100 Subject: [PATCH 02/11] fix test (#52) --- .../experimental/unstructured_scheme/test__mesh_to_MeshInfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esmf_regrid/tests/unit/experimental/unstructured_scheme/test__mesh_to_MeshInfo.py b/esmf_regrid/tests/unit/experimental/unstructured_scheme/test__mesh_to_MeshInfo.py index 229fba68..490c33f9 100644 --- a/esmf_regrid/tests/unit/experimental/unstructured_scheme/test__mesh_to_MeshInfo.py +++ b/esmf_regrid/tests/unit/experimental/unstructured_scheme/test__mesh_to_MeshInfo.py @@ -108,5 +108,5 @@ def test_anticlockwise_validity(): # This catches geometric/topological abnormalities that would arise from, # for example: switching lat/lon values, using euclidean coords vs spherical. rg = Regridder(meshinfo, meshinfo) - expected_weights = scipy.sparse.identity(meshinfo.size()) + expected_weights = scipy.sparse.identity(meshinfo.size) assert np.allclose(expected_weights.todense(), rg.weight_matrix.todense()) From d4d7a0722297977d36fb7c7496df8a7ce87bbb39 Mon Sep 17 00:00:00 2001 From: stephenworsley <49274989+stephenworsley@users.noreply.github.com> Date: Fri, 9 Apr 2021 13:41:26 +0100 Subject: [PATCH 03/11] Unstructured Scheme - Cube Creation 3D (#47) * handle extra dims in _create_cube * add test * lint fix * address review comment --- .../experimental/unstructured_scheme.py | 38 ++++++++++--- .../unstructured_scheme/test__create_cube.py | 54 +++++++++++++++++-- 2 files changed, 80 insertions(+), 12 deletions(-) diff --git a/esmf_regrid/experimental/unstructured_scheme.py b/esmf_regrid/experimental/unstructured_scheme.py index 6ef03cd1..677163f3 100644 --- a/esmf_regrid/experimental/unstructured_scheme.py +++ b/esmf_regrid/experimental/unstructured_scheme.py @@ -73,16 +73,11 @@ def _create_cube(data, src_cube, mesh_dim, grid_x, grid_y): # data: a masked array containing the result of the regridding operation # src_cube: the source cube which data is regrid from # mesh_dim: the dimension on src_cube which the mesh belongs to - # mesh: the Mesh (or MeshCoord) object belonging to src_cube # grid_x: the coordinate on the target cube representing the x axis # grid_y: the coordinate on the target cube representing the y axis new_cube = iris.cube.Cube(data) - # TODO: The following code assumes a 1D source cube and mesh_dim = 0. - # This is therefore simple code which should be updated when we start - # supporting the regridding of extra dimensions. - # TODO: The following code is rigid with respect to which dimensions # the x coord and y coord are assigned to. We should decide if it is # appropriate to copy the dimension ordering from the target cube @@ -92,8 +87,37 @@ def _create_cube(data, src_cube, mesh_dim, grid_x, grid_y): new_cube.metadata = copy.deepcopy(src_cube.metadata) - for coord in src_cube.coords(dimensions=()): - new_cube.add_aux_coord(coord.copy()) + # TODO: Handle derived coordinates. The following code is taken from + # iris, the parts dealing with derived coordinates have been + # commented out for the time being. + # coord_mapping = {} + + def copy_coords(src_coords, add_method): + for coord in src_coords: + dims = src_cube.coord_dims(coord) + if hasattr(coord, "mesh") or mesh_dim in dims: + continue + # Since the mesh will be replaced by a 2D grid, dims which are + # beyond the mesh_dim are increased by one. + dims = [dim if dim < mesh_dim else dim + 1 for dim in dims] + result_coord = coord.copy() + # Add result_coord to the owner of add_method. + add_method(result_coord, dims) + # coord_mapping[id(coord)] = result_coord + + copy_coords(src_cube.dim_coords, new_cube.add_dim_coord) + copy_coords(src_cube.aux_coords, new_cube.add_aux_coord) + + # for factory in src_cube.aux_factories: + # # TODO: Regrid dependant coordinates which span mesh_dim. + # try: + # result.add_aux_factory(factory.updated(coord_mapping)) + # except KeyError: + # msg = ( + # "Cannot update aux_factory {!r} because of dropped" + # " coordinates.".format(factory.name()) + # ) + # warnings.warn(msg) return new_cube diff --git a/esmf_regrid/tests/unit/experimental/unstructured_scheme/test__create_cube.py b/esmf_regrid/tests/unit/experimental/unstructured_scheme/test__create_cube.py index 1d9cc33d..50536059 100644 --- a/esmf_regrid/tests/unit/experimental/unstructured_scheme/test__create_cube.py +++ b/esmf_regrid/tests/unit/experimental/unstructured_scheme/test__create_cube.py @@ -1,6 +1,8 @@ """Unit tests for miscellaneous helper functions in `esmf_regrid.experimental.unstructured_scheme`.""" import iris +from iris.coords import AuxCoord, DimCoord +from iris.cube import Cube import numpy as np from esmf_regrid.experimental.unstructured_scheme import _create_cube @@ -11,17 +13,56 @@ def test_create_cube_2D(): data = np.ones([2, 3]) # Create a source cube with metadata and scalar coords - src_cube = iris.cube.Cube(np.zeros(5)) + src_cube = Cube(np.zeros(5)) src_cube.units = "K" src_cube.attributes = {"a": 1} src_cube.standard_name = "air_temperature" - scalar_height = iris.coords.AuxCoord([5], units="m", standard_name="height") - scalar_time = iris.coords.DimCoord([10], units="s", standard_name="time") + scalar_height = AuxCoord([5], units="m", standard_name="height") + scalar_time = DimCoord([10], units="s", standard_name="time") src_cube.add_aux_coord(scalar_height) src_cube.add_aux_coord(scalar_time) mesh_dim = 0 + grid_x = DimCoord(np.arange(3), standard_name="longitude") + grid_y = DimCoord(np.arange(2), standard_name="latitude") + + cube = _create_cube(data, src_cube, mesh_dim, grid_x, grid_y) + src_metadata = src_cube.metadata + + expected_cube = Cube(data) + expected_cube.metadata = src_metadata + expected_cube.add_dim_coord(grid_x, 1) + expected_cube.add_dim_coord(grid_y, 0) + expected_cube.add_aux_coord(scalar_height) + expected_cube.add_aux_coord(scalar_time) + assert expected_cube == cube + + +def test_create_cube_4D(): + """Test creation of 2D output grid.""" + data = np.ones([4, 2, 3, 5]) + + # Create a source cube with metadata and scalar coords + src_cube = Cube(np.zeros([4, 5, 5])) + src_cube.units = "K" + src_cube.attributes = {"a": 1} + src_cube.standard_name = "air_temperature" + scalar_height = AuxCoord([5], units="m", standard_name="height") + scalar_time = DimCoord([10], units="s", standard_name="time") + src_cube.add_aux_coord(scalar_height) + src_cube.add_aux_coord(scalar_time) + first_coord = DimCoord(np.arange(4), standard_name="air_pressure") + src_cube.add_dim_coord(first_coord, 0) + last_coord = AuxCoord(np.arange(5), long_name="last_coord") + src_cube.add_aux_coord(last_coord, 2) + multidim_coord = AuxCoord(np.ones([4, 5]), long_name="2d_coord") + src_cube.add_aux_coord(multidim_coord, (0, 2)) + ignored_coord = AuxCoord(np.arange(5), long_name="ignore") + src_cube.add_aux_coord(ignored_coord, 1) + + mesh_dim = 1 + grid_x = iris.coords.DimCoord(np.arange(3), standard_name="longitude") grid_y = iris.coords.DimCoord(np.arange(2), standard_name="latitude") @@ -30,8 +71,11 @@ def test_create_cube_2D(): expected_cube = iris.cube.Cube(data) expected_cube.metadata = src_metadata - expected_cube.add_dim_coord(grid_x, 1) - expected_cube.add_dim_coord(grid_y, 0) + expected_cube.add_dim_coord(grid_x, 2) + expected_cube.add_dim_coord(grid_y, 1) + expected_cube.add_dim_coord(first_coord, 0) + expected_cube.add_aux_coord(last_coord, 3) + expected_cube.add_aux_coord(multidim_coord, (0, 3)) expected_cube.add_aux_coord(scalar_height) expected_cube.add_aux_coord(scalar_time) assert expected_cube == cube From dffe7defdc9890514ee8998b39e9e86d3517aa5b Mon Sep 17 00:00:00 2001 From: stephenworsley <49274989+stephenworsley@users.noreply.github.com> Date: Wed, 14 Apr 2021 18:34:08 +0100 Subject: [PATCH 04/11] Unstructured Scheme - Additional Polish (docstrings and test coverage) (#53) * add docstrings * add test * add docstrings * fix typo * fix typo * format docstrings * format docstring * address review comments * Satisfying the flake8 CI Co-authored-by: James Penn --- .../experimental/unstructured_scheme.py | 133 ++++++++++++++++-- .../test_MeshToGridESMFRegridder.py | 21 +++ 2 files changed, 142 insertions(+), 12 deletions(-) diff --git a/esmf_regrid/experimental/unstructured_scheme.py b/esmf_regrid/experimental/unstructured_scheme.py index 677163f3..bf2d8332 100644 --- a/esmf_regrid/experimental/unstructured_scheme.py +++ b/esmf_regrid/experimental/unstructured_scheme.py @@ -43,7 +43,7 @@ def _cube_to_GridInfo(cube): # Ensure coords come from a proper grid. assert isinstance(lon, iris.coords.DimCoord) assert isinstance(lat, iris.coords.DimCoord) - # TODO: accomodate other x/y coords. + # TODO: accommodate other x/y coords. # TODO: perform checks on lat/lon. # Checks may cover units, coord systems (e.g. rotated pole), contiguous bounds. return GridInfo( @@ -69,13 +69,38 @@ def _cube_to_GridInfo(cube): def _create_cube(data, src_cube, mesh_dim, grid_x, grid_y): - # Here we expect the args to be as follows: - # data: a masked array containing the result of the regridding operation - # src_cube: the source cube which data is regrid from - # mesh_dim: the dimension on src_cube which the mesh belongs to - # grid_x: the coordinate on the target cube representing the x axis - # grid_y: the coordinate on the target cube representing the y axis - + """ + Return a new cube for the result of regridding. + + Returned cube represents the result of regridding the source cube + onto the new grid. + All the metadata and coordinates of the result cube are copied from + the source cube, with two exceptions: + - Grid dimension coordinates are copied from the grid cube. + - Auxiliary coordinates which span the grid dimensions are + ignored. + + Parameters + ---------- + data : array + The regridded data as an N-dimensional NumPy array. + src_cube : cube + The source Cube. + mes_dim : int + The dimension of the mesh within the source Cube. + grid_x : DimCoord + The :class:`iris.coords.DimCoord` for the new grid's X + coordinate. + grid_y : DimCoord + The :class:`iris.coords.DimCoord` for the new grid's Y + coordinate. + + Returns + ------- + cube + A new iris.cube.Cube instance. + + """ new_cube = iris.cube.Cube(data) # TODO: The following code is rigid with respect to which dimensions @@ -123,6 +148,13 @@ def copy_coords(src_coords, add_method): def _regrid_unstructured_to_rectilinear__prepare(src_mesh_cube, target_grid_cube): + """ + First (setup) part of 'regrid_unstructured_to_rectilinear'. + + Check inputs and calculate the sparse regrid matrix and related info. + The 'regrid info' returned can be re-used over many 2d slices. + + """ # TODO: Perform checks on the arguments. (grid coords are contiguous, # spherical and monotonic. Mesh is defined on faces) @@ -152,6 +184,12 @@ def _regrid_unstructured_to_rectilinear__prepare(src_mesh_cube, target_grid_cube def _regrid_unstructured_to_rectilinear__perform(src_cube, regrid_info, mdtol): + """ + Second (regrid) part of 'regrid_unstructured_to_rectilinear'. + + Perform the prepared regrid calculation on a single 2d cube. + + """ mesh_dim, grid_x, grid_y, regridder = regrid_info # Perform regridding with realised data for the moment. This may be changed @@ -174,17 +212,70 @@ def _regrid_unstructured_to_rectilinear__perform(src_cube, regrid_info, mdtol): def regrid_unstructured_to_rectilinear(src_cube, grid_cube, mdtol=0): - """TODO: write docstring.""" + """ + Regrid unstructured cube onto rectilinear grid. + + Return a new cube with data values calculated using the area weighted + mean of data values from unstructured cube src_cube regridded onto the + horizontal grid of grid_cube. The dimension on the cube belonging to + the mesh will replaced by the two dimensions associated with the grid. + This function requires that the horizontal dimension of src_cube is + described by a 2D mesh with data located on the faces of that mesh. + This function requires that the horizontal grid of grid_cube is + rectilinear (i.e. expressed in terms of two orthogonal 1D coordinates). + This function also requires that the coordinates describing the + horizontal grid have bounds. + + Parameters + ---------- + src_cube : cube + An unstructured instance of iris.cube.Cube that supplies the data, + metadata and coordinates. + grid_cube : cube + A rectilinear instance of iris.cube.Cube that supplies the desired + horizontal grid definition. + mdtol : float, optional + Tolerance of missing data. The value returned in each element of the + returned cube's data array will be masked if the fraction of masked + data in the overlapping cells of the source cube exceeds mdtol. This + fraction is calculated based on the area of masked cells within each + target cell. mdtol=0 means no missing data is tolerated while mdtol=1 + will mean the resulting element will be masked if and only if all the + overlapping cells of the source cube are masked. Defaults to 0. + + Returns + ------- + cube + A new iris.cube.Cube instance. + + """ regrid_info = _regrid_unstructured_to_rectilinear__prepare(src_cube, grid_cube) result = _regrid_unstructured_to_rectilinear__perform(src_cube, regrid_info, mdtol) return result class MeshToGridESMFRegridder: - """TODO: write docstring.""" + """Regridder class for unstructured to rectilinear cubes.""" def __init__(self, src_mesh_cube, target_grid_cube, mdtol=1): - """TODO: write docstring.""" + """ + Create regridder for conversions between source mesh and target grid. + + Parameters + ---------- + src_grid_cube : cube + The unstructured iris cube providing the source grid. + target_grid_cube : cube + The rectilinear iris cube providing the target grid. + mdtol : float, optional + Tolerance of missing data. The value returned in each element of + the returned array will be masked if the fraction of masked data + exceeds mdtol. mdtol=0 means no missing data is tolerated while + mdtol=1 will mean the resulting element will be masked if and only + if all the contributing elements of data are masked. + Defaults to 1. + + """ # TODO: Record information about the identity of the mesh. This would # typically be a copy of the mesh, though given the potential size of # the mesh, it may make sense to either retain a reference to the actual @@ -205,7 +296,25 @@ def __init__(self, src_mesh_cube, target_grid_cube, mdtol=1): _, self.grid_x, self.grid_y, self.regridder = partial_regrid_info def __call__(self, cube): - """TODO: write docstring.""" + """ + Regrid this cube onto the target grid of this regridder instance. + + The given cube must be defined with the same mesh as the source + cube used to create this MeshToGridESMFRegridder instance. + + Parameters + ---------- + cube : cube + A iris.cube.Cube instance to be regridded. + + Returns + ------- + A cube defined with the horizontal dimensions of the target + and the other dimensions from this cube. The data values of + this cube will be converted to values on the new grid using + area-weighted regridding via ESMF generated weights. + + """ mesh = cube.mesh # TODO: Ensure cube has the same mesh as that of the recorded mesh. # For the time being, we simply check that the mesh exists. diff --git a/esmf_regrid/tests/unit/experimental/unstructured_scheme/test_MeshToGridESMFRegridder.py b/esmf_regrid/tests/unit/experimental/unstructured_scheme/test_MeshToGridESMFRegridder.py index 6141aff5..abc72655 100644 --- a/esmf_regrid/tests/unit/experimental/unstructured_scheme/test_MeshToGridESMFRegridder.py +++ b/esmf_regrid/tests/unit/experimental/unstructured_scheme/test_MeshToGridESMFRegridder.py @@ -2,6 +2,7 @@ from iris.coords import AuxCoord, DimCoord import numpy as np +import pytest from esmf_regrid.experimental.unstructured_scheme import ( MeshToGridESMFRegridder, @@ -56,3 +57,23 @@ def _add_metadata(cube): # Check metadata and scalar coords. expected_cube.data = result.data assert expected_cube == result + + +def test_invalid_mdtol(): + """ + Test initialisation of :func:`esmf_regrid.experimental.unstructured_scheme.MeshToGridESMFRegridder`. + + Checks that an error is raised when mdtol is out of range. + """ + src = _flat_mesh_cube() + + n_lons = 6 + n_lats = 5 + lon_bounds = (-180, 180) + lat_bounds = (-90, 90) + tgt = _grid_cube(n_lons, n_lats, lon_bounds, lat_bounds, circular=True) + + with pytest.raises(ValueError): + _ = MeshToGridESMFRegridder(src, tgt, mdtol=2) + with pytest.raises(ValueError): + _ = MeshToGridESMFRegridder(src, tgt, mdtol=-1) From e10e3821008534f3c919a001e65ea1a4dcf22119 Mon Sep 17 00:00:00 2001 From: stephenworsley <49274989+stephenworsley@users.noreply.github.com> Date: Thu, 15 Apr 2021 16:23:41 +0100 Subject: [PATCH 05/11] add __init__ to tests (#56) --- esmf_regrid/tests/unit/experimental/__init__.py | 1 + .../tests/unit/experimental/unstuctured_regrid/__init__.py | 1 + 2 files changed, 2 insertions(+) create mode 100644 esmf_regrid/tests/unit/experimental/__init__.py create mode 100644 esmf_regrid/tests/unit/experimental/unstuctured_regrid/__init__.py diff --git a/esmf_regrid/tests/unit/experimental/__init__.py b/esmf_regrid/tests/unit/experimental/__init__.py new file mode 100644 index 00000000..b8653193 --- /dev/null +++ b/esmf_regrid/tests/unit/experimental/__init__.py @@ -0,0 +1 @@ +"""Unit tests for :mod:`esmf_regrid.experimental`.""" diff --git a/esmf_regrid/tests/unit/experimental/unstuctured_regrid/__init__.py b/esmf_regrid/tests/unit/experimental/unstuctured_regrid/__init__.py new file mode 100644 index 00000000..18b91be7 --- /dev/null +++ b/esmf_regrid/tests/unit/experimental/unstuctured_regrid/__init__.py @@ -0,0 +1 @@ +"""Unit tests for :mod:`esmf_regrid.experimental.unstructured_regrid`.""" From 6caaf028a605abb5e8a970be9fd56eb12be28126 Mon Sep 17 00:00:00 2001 From: stephenworsley <49274989+stephenworsley@users.noreply.github.com> Date: Fri, 16 Apr 2021 15:05:23 +0100 Subject: [PATCH 06/11] Unstructured Scheme - Extra Dims (#55) * extend support to extra dimensions * clarify comment * add test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * simplify function * remove import * simplify function * address review comments * fix test Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../experimental/unstructured_scheme.py | 30 +++++------ .../test_MeshToGridESMFRegridder.py | 53 +++++++++++++++++++ ...test_regrid_unstructured_to_rectilinear.py | 50 +++++++++++++++++ 3 files changed, 117 insertions(+), 16 deletions(-) diff --git a/esmf_regrid/experimental/unstructured_scheme.py b/esmf_regrid/experimental/unstructured_scheme.py index bf2d8332..ad3b2128 100644 --- a/esmf_regrid/experimental/unstructured_scheme.py +++ b/esmf_regrid/experimental/unstructured_scheme.py @@ -6,8 +6,6 @@ from iris.analysis._interpolation import get_xy_dim_coords import numpy as np -# from numpy import ma - from esmf_regrid.esmf_regridder import GridInfo, Regridder from esmf_regrid.experimental.unstructured_regrid import MeshInfo @@ -55,17 +53,19 @@ def _cube_to_GridInfo(cube): ) -# def _regrid_along_dims(regridder, data, src_dim, mdtol): -# # Before regridding, data is transposed to a standard form. -# # This will be done either with something like the following code -# # or else done within the regridder by specifying args. -# # new_axes = list(range(len(data.shape))) -# # new_axes.pop(src_dim) -# # new_axes.append(src_dim) -# # data = ma.transpose(data, axes=new_axes) -# -# result = regridder.regrid(data, mdtol=mdtol) -# return result +def _regrid_along_mesh_dim(regridder, data, mesh_dim, mdtol): + # Before regridding, data is transposed to a standard form. + # In the future, this may be done within the regridder by specifying args. + + # Move the mesh axis to be the last dimension. + data = np.moveaxis(data, mesh_dim, -1) + + result = regridder.regrid(data, mdtol=mdtol) + + # Move grid axes back into the original position of the mesh. + result = np.moveaxis(result, [-2, -1], [mesh_dim, mesh_dim + 1]) + + return result def _create_cube(data, src_cube, mesh_dim, grid_x, grid_y): @@ -194,9 +194,7 @@ def _regrid_unstructured_to_rectilinear__perform(src_cube, regrid_info, mdtol): # Perform regridding with realised data for the moment. This may be changed # in future to handle src_cube.lazy_data. - new_data = regridder.regrid(src_cube.data, mdtol=mdtol) - # When we want to handle extra dimensions, we may want to do something like: - # new_data = _regrid_along_dims(src_cube.data, mesh_dim, mdtol) + new_data = _regrid_along_mesh_dim(regridder, src_cube.data, mesh_dim, mdtol) new_cube = _create_cube( new_data, diff --git a/esmf_regrid/tests/unit/experimental/unstructured_scheme/test_MeshToGridESMFRegridder.py b/esmf_regrid/tests/unit/experimental/unstructured_scheme/test_MeshToGridESMFRegridder.py index abc72655..2a2085d7 100644 --- a/esmf_regrid/tests/unit/experimental/unstructured_scheme/test_MeshToGridESMFRegridder.py +++ b/esmf_regrid/tests/unit/experimental/unstructured_scheme/test_MeshToGridESMFRegridder.py @@ -1,6 +1,7 @@ """Unit tests for :func:`esmf_regrid.experimental.unstructured_scheme.MeshToGridESMFRegridder`.""" from iris.coords import AuxCoord, DimCoord +from iris.cube import Cube import numpy as np import pytest @@ -12,6 +13,7 @@ ) from esmf_regrid.tests.unit.experimental.unstructured_scheme.test__regrid_unstructured_to_rectilinear__prepare import ( _flat_mesh_cube, + _full_mesh, ) @@ -59,6 +61,57 @@ def _add_metadata(cube): assert expected_cube == result +def test_multidim_cubes(): + """ + Test for :func:`esmf_regrid.experimental.unstructured_scheme.MeshToGridESMFRegridder`. + + Tests with multidimensional cubes. The source cube contains + coordinates on the dimensions before and after the mesh dimension. + """ + mesh = _full_mesh() + mesh_length = mesh.connectivity(contains_face=True).shape[0] + + h = 2 + t = 3 + height = DimCoord(np.arange(h), standard_name="height") + time = DimCoord(np.arange(t), standard_name="time") + + src_data = np.empty([t, mesh_length, h]) + src_data[:] = np.arange(t * h).reshape([t, h])[:, np.newaxis, :] + mesh_cube = Cube(src_data) + mesh_coord_x, mesh_coord_y = mesh.to_MeshCoords("face") + mesh_cube.add_aux_coord(mesh_coord_x, 1) + mesh_cube.add_aux_coord(mesh_coord_y, 1) + mesh_cube.add_dim_coord(time, 0) + mesh_cube.add_dim_coord(height, 2) + + n_lons = 6 + n_lats = 5 + lon_bounds = (-180, 180) + lat_bounds = (-90, 90) + tgt = _grid_cube(n_lons, n_lats, lon_bounds, lat_bounds, circular=True) + + src_cube = mesh_cube.copy() + src_cube.transpose([1, 0, 2]) + regridder = MeshToGridESMFRegridder(src_cube, tgt) + result = regridder(mesh_cube) + + # Lenient check for data. + expected_data = np.empty([t, n_lats, n_lons, h]) + expected_data[:] = np.arange(t * h).reshape(t, h)[:, np.newaxis, np.newaxis, :] + assert np.allclose(expected_data, result.data) + + expected_cube = Cube(expected_data) + expected_cube.add_dim_coord(time, 0) + expected_cube.add_dim_coord(tgt.coord("latitude"), 1) + expected_cube.add_dim_coord(tgt.coord("longitude"), 2) + expected_cube.add_dim_coord(height, 3) + + # Check metadata and scalar coords. + result.data = expected_data + assert expected_cube == result + + def test_invalid_mdtol(): """ Test initialisation of :func:`esmf_regrid.experimental.unstructured_scheme.MeshToGridESMFRegridder`. diff --git a/esmf_regrid/tests/unit/experimental/unstructured_scheme/test_regrid_unstructured_to_rectilinear.py b/esmf_regrid/tests/unit/experimental/unstructured_scheme/test_regrid_unstructured_to_rectilinear.py index 96240a8d..783c9c34 100644 --- a/esmf_regrid/tests/unit/experimental/unstructured_scheme/test_regrid_unstructured_to_rectilinear.py +++ b/esmf_regrid/tests/unit/experimental/unstructured_scheme/test_regrid_unstructured_to_rectilinear.py @@ -1,6 +1,7 @@ """Unit tests for :func:`esmf_regrid.experimental.unstructured_scheme.regrid_unstructured_to_rectilinear`.""" from iris.coords import AuxCoord, DimCoord +from iris.cube import Cube import numpy as np from esmf_regrid.experimental.unstructured_scheme import ( @@ -11,6 +12,7 @@ ) from esmf_regrid.tests.unit.experimental.unstructured_scheme.test__regrid_unstructured_to_rectilinear__prepare import ( _flat_mesh_cube, + _full_mesh, ) @@ -55,3 +57,51 @@ def _add_metadata(cube): # Check metadata and scalar coords. expected_cube.data = result.data assert expected_cube == result + + +def test_multidim_cubes(): + """ + Test for :func:`esmf_regrid.experimental.unstructured_scheme.regrid_unstructured_to_rectilinear`. + + Tests with multidimensional cubes. The source cube contains + coordinates on the dimensions before and after the mesh dimension. + """ + mesh = _full_mesh() + mesh_length = mesh.connectivity(contains_face=True).shape[0] + + h = 2 + t = 3 + height = DimCoord(np.arange(h), standard_name="height") + time = DimCoord(np.arange(t), standard_name="time") + + src_data = np.empty([t, mesh_length, h]) + src_data[:] = np.arange(t * h).reshape([t, h])[:, np.newaxis, :] + cube = Cube(src_data) + mesh_coord_x, mesh_coord_y = mesh.to_MeshCoords("face") + cube.add_aux_coord(mesh_coord_x, 1) + cube.add_aux_coord(mesh_coord_y, 1) + cube.add_dim_coord(time, 0) + cube.add_dim_coord(height, 2) + + n_lons = 6 + n_lats = 5 + lon_bounds = (-180, 180) + lat_bounds = (-90, 90) + tgt = _grid_cube(n_lons, n_lats, lon_bounds, lat_bounds, circular=True) + + result = regrid_unstructured_to_rectilinear(cube, tgt) + + # Lenient check for data. + expected_data = np.empty([t, n_lats, n_lons, h]) + expected_data[:] = np.arange(t * h).reshape(t, h)[:, np.newaxis, np.newaxis, :] + assert np.allclose(expected_data, result.data) + + expected_cube = Cube(expected_data) + expected_cube.add_dim_coord(time, 0) + expected_cube.add_dim_coord(tgt.coord("latitude"), 1) + expected_cube.add_dim_coord(tgt.coord("longitude"), 2) + expected_cube.add_dim_coord(height, 3) + + # Check metadata and scalar coords. + result.data = expected_data + assert expected_cube == result From 486dc966aaedfec56b982864542e9ba152d38f66 Mon Sep 17 00:00:00 2001 From: stephenworsley <49274989+stephenworsley@users.noreply.github.com> Date: Mon, 19 Apr 2021 11:58:17 +0100 Subject: [PATCH 07/11] Update version to v0.1.dev1 (#58) --- esmf_regrid/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esmf_regrid/__init__.py b/esmf_regrid/__init__.py index 96632c8b..808b2862 100644 --- a/esmf_regrid/__init__.py +++ b/esmf_regrid/__init__.py @@ -1,4 +1,4 @@ from .schemes import * -__version__ = "0.1.dev0" +__version__ = "0.1.dev1" From 27785f5fa7fc930c78427fc9bce2eca004aefba5 Mon Sep 17 00:00:00 2001 From: stephenworsley <49274989+stephenworsley@users.noreply.github.com> Date: Mon, 19 Apr 2021 12:06:22 +0100 Subject: [PATCH 08/11] Update Version to v0.1.dev2 (#59) --- esmf_regrid/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esmf_regrid/__init__.py b/esmf_regrid/__init__.py index 808b2862..0f0d9e20 100644 --- a/esmf_regrid/__init__.py +++ b/esmf_regrid/__init__.py @@ -1,4 +1,4 @@ from .schemes import * -__version__ = "0.1.dev1" +__version__ = "0.1.dev2" From 8eeddbab18abcef23026cd817112defdc0b3dafd Mon Sep 17 00:00:00 2001 From: Martin Yeo Date: Fri, 21 May 2021 15:41:47 +0100 Subject: [PATCH 09/11] Updated lockfiles according to branch requirement spec. --- requirements/nox.lock/py36-linux-64.lock | 157 ++++++++++++++++++++-- requirements/nox.lock/py37-linux-64.lock | 161 +++++++++++++++++++++-- requirements/nox.lock/py38-linux-64.lock | 158 ++++++++++++++++++++-- 3 files changed, 441 insertions(+), 35 deletions(-) diff --git a/requirements/nox.lock/py36-linux-64.lock b/requirements/nox.lock/py36-linux-64.lock index 6eed6881..002de9db 100644 --- a/requirements/nox.lock/py36-linux-64.lock +++ b/requirements/nox.lock/py36-linux-64.lock @@ -1,61 +1,130 @@ # platform: linux-64 -# env_hash: 8140e197d8e6099c06063c068442ac6b12a07246ef7cb3328a7269fe4bf597c1 +# env_hash: 8d4eabba93d707c1be9daf57e71eb8926194a98edf2ed574d471346ab3db9f66 @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-2020.12.5-ha878542_0.tar.bz2#7eb5d4ffeee663caa1635cd67071bc1b +https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2#0c96522c6bdaed4b1566d11387caaf45 +https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6 +https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb +https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-hab24e00_0.tar.bz2#19410c3df09dfb12d1206132a1d357c5 https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.35.1-hea4e1c9_2.tar.bz2#83610dba766a186bdc7a116053b782a4 https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-9.3.0-hff62375_19.tar.bz2#c2d8da3cb171e4aa642d20c6e4e42a04 https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-9.3.0-h6de172a_19.tar.bz2#cd9a24a8dde03ec0cf0e603b0bea85a1 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.23-ha770c72_2.tar.bz2#ce876d0c998e1e2eb1dc67b01937737f +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-9.3.0-hff62375_19.tar.bz2#aea379bd68fdcdf9499fa1453f852ac1 https://conda.anaconda.org/conda-forge/linux-64/libgomp-9.3.0-h2828fa1_19.tar.bz2#ab0a307912033126da02507b59e79ec9 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-9.3.0-h2828fa1_19.tar.bz2#9d5cdfc51476ee4dcdd96ed2dca3f943 +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.17.1-h7f98852_1.tar.bz2#ed1dc233ed5e3eaa9bfbaac64d130c5e +https://conda.anaconda.org/conda-forge/linux-64/expat-2.3.0-h9c3ff4c_0.tar.bz2#1fb8f0254eb78a2a0c120155e1b1a207 +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.9.1-h9c3ff4c_2.tar.bz2#b9a6d9422aed3ad84ec6ccee9bfcaa0f +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-68.1-h58526e2_0.tar.bz2#fc7a4271dc2a7f4fd78cd63695baf7c3 https://conda.anaconda.org/conda-forge/linux-64/jpeg-9d-h36c2ea0_0.tar.bz2#ea02ce6037dbe81803ae6123e5ba1568 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.3-h58526e2_2.tar.bz2#665369991d8dd290ac5ee92fce3e6bf5 -https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.15-pthreads_h8fe5266_0.tar.bz2#b4bdbbff92cacdd7b28b1b0a0366b24b +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/libogg-1.3.4-h7f98852_1.tar.bz2#6e8cc2173440d77708196c5b93771680 +https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.15-pthreads_h8fe5266_1.tar.bz2#bb5527a16584426a897f22643d9a36a6 +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-h58526e2_1007.tar.bz2#7f6569a0c2f27acb8fc90600b382e544 +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.0-h7f98852_2.tar.bz2#fb63a035a3b552c88a30d84b89ebf4c4 https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.9.3-h9c3ff4c_0.tar.bz2#4eb64ee0d5cd43096ffcf843c76b05d4 https://conda.anaconda.org/conda-forge/linux-64/mpich-3.4.1-h846660c_104.tar.bz2#94f01e56905a7af1479c9f72b00e9864 https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.2-h58526e2_4.tar.bz2#509f2a21c4a09214cd737a480dfd80c9 +https://conda.anaconda.org/conda-forge/linux-64/nspr-4.30-h9c3ff4c_0.tar.bz2#e6dc1f8f6e0bcebe8e3d8a5bca258dbe https://conda.anaconda.org/conda-forge/linux-64/openssl-1.1.1k-h7f98852_0.tar.bz2#07fae2cb088379c8441e0f3ffa1f4025 +https://conda.anaconda.org/conda-forge/linux-64/pcre-8.44-he1b5a44_0.tar.bz2#e647d89cd5cdf62760cf283a001841ff +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/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-h516909a_0.tar.bz2#03a530e925414902547cf48da7756db8 https://conda.anaconda.org/conda-forge/linux-64/zlib-1.2.11-h516909a_1010.tar.bz2#339cc5584e6d26bc73a875ba900028c3 -https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h10796ff_2.tar.bz2#c6f500917ce0d20043a7e3de53b0e5bd +https://conda.anaconda.org/conda-forge/linux-64/gettext-0.19.8.1-h0b5b191_1005.tar.bz2#ff6f69b593a9e74c0e6b61908ac513fa +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/libblas-3.9.0-9_openblas.tar.bz2#5f08755e98b2a43ca68124e629a5a0cb 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-hcdb4288_3.tar.bz2#d8f51405997093ff1799ded7650439c4 +https://conda.anaconda.org/conda-forge/linux-64/libllvm11-11.1.0-hf817b99_2.tar.bz2#646fa2f7c60b69ee8f918668e9c2fd31 https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.43.0-h812cca2_0.tar.bz2#1867d1e9658596b3fac8847a7702eef4 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.9.0-ha56f1ee_6.tar.bz2#f0dfb86444df325e599dbc3f4c0a3f5b +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_1003.tar.bz2#a9371e9e40aded194dcba1447606c9a1 +https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.9.12-h72842e0_0.tar.bz2#bd14fdf5b9ee5568056a40a6a2f41866 https://conda.anaconda.org/conda-forge/linux-64/libzip-1.7.3-h4de3113_0.tar.bz2#2568763f88009f95e9262cba837dbb82 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.10-h21135ba_1.tar.bz2#c647f70aa7e3d4cc4e029cc1c9a99953 -https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.0-ha95c52a_0.tar.bz2#b56f94865e2de36abf054e7bfa499034 +https://conda.anaconda.org/conda-forge/linux-64/udunits2-2.2.27.27-h975c496_1.tar.bz2#e663bd5dbc8cc4c1647d9f51cf25872c +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/zstd-1.4.9-ha95c52a_0.tar.bz2#b481dc9fda3af2a681d08a4d5cd1ea0b 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/krb5-1.19.1-hcc1bbae_0.tar.bz2#59b0695a515a6c54d45463dbf208ae38 https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-9_openblas.tar.bz2#edee85b4f83376ceae81e0975b8bffa2 +https://conda.anaconda.org/conda-forge/linux-64/libclang-11.1.0-default_ha53f305_1.tar.bz2#b9b71585ca4fcb5d442c5a9df5dd7e98 +https://conda.anaconda.org/conda-forge/linux-64/libglib-2.68.2-h3e27bee_0.tar.bz2#a48401ff2ecb708b8b08bf3547eff205 https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-9_openblas.tar.bz2#572d84ab07962986f6dd8e4637a475ca -https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.2.0-hf544144_3.tar.bz2#53798351da909e181243e17dccee41ad +https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.2.0-hbd63e13_2.tar.bz2#e3f034b29a122699b06da40c155f1a70 +https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.0.3-he3ba5ed_0.tar.bz2#f9dbabc7e01c459ed7a1d1d64b206e9b +https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-8.0.23-h935591d_2.tar.bz2#b36368d163fca85e110529ddc4c67985 https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.35.5-h74cdb3f_0.tar.bz2#e876c82c21e7074d299e13762d02466c +https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.7.1-h7f98852_0.tar.bz2#1759774cc5f1e965178c9cbab0b29a13 +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/fontconfig-2.13.1-hba837de_1005.tar.bz2#fd3611672eb91bc9d24fd6fb970037eb +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/glib-tools-2.68.2-h9c3ff4c_0.tar.bz2#3afedfe4c8f500ed953dcc488730908a +https://conda.anaconda.org/conda-forge/linux-64/gstreamer-1.18.4-h76c114f_2.tar.bz2#5db765d4974fa89f64c1544eb2a552cb +https://conda.anaconda.org/conda-forge/linux-64/gts-0.7.6-h64030ff_2.tar.bz2#112eb9b5b93f0c02e59aea4fd1967363 https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.12-hddcbb42_0.tar.bz2#797117394a4aa588de6d741b06fad80f https://conda.anaconda.org/conda-forge/linux-64/libcurl-7.76.1-h2574ce0_2.tar.bz2#cc7097ea31a80337c2203c22f08d3f03 -https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.4.0-hb52868f_1.tar.bz2#b7ad78ad2e9ee155f59e6428406ee824 +https://conda.anaconda.org/conda-forge/linux-64/libpq-13.3-hd57d9b9_0.tar.bz2#66ef2cacc483205b7d303f7b02601c3b +https://conda.anaconda.org/conda-forge/linux-64/libwebp-1.2.0-h3452ae3_0.tar.bz2#8f4e19a8988c38feec7db41bcd0bf0d0 +https://conda.anaconda.org/conda-forge/linux-64/nss-3.65-hb5efdd6_0.tar.bz2#645afd59bb8a540be3545850f586e028 https://conda.anaconda.org/conda-forge/linux-64/python-3.6.13-hffdb5ce_0_cpython.tar.bz2#575e65ac9386755d3b9fd70f20dc1e37 +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/appdirs-1.4.4-pyh9f0ad1d_0.tar.bz2#5f095bc6454094e96f146491fd03633b https://conda.anaconda.org/conda-forge/noarch/attrs-21.2.0-pyhd8ed1ab_0.tar.bz2#d2e1c7f388ac403df7079b411c37cc50 +https://conda.anaconda.org/conda-forge/linux-64/cairo-1.16.0-h6cf1ce9_1008.tar.bz2#a43fb47d15e116f8be4be7e6b17ab59f +https://conda.anaconda.org/conda-forge/noarch/click-7.1.2-pyh9f0ad1d_0.tar.bz2#bd50a970ce07e660c319fdc4d730d3f1 +https://conda.anaconda.org/conda-forge/noarch/cloudpickle-1.6.0-py_0.tar.bz2#76d764d8881719e305f6fa368dc2b65e +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.76.1-hea6ffbf_2.tar.bz2#0312ab411fff1aca55e45ea3f885a8ab https://conda.anaconda.org/conda-forge/noarch/dataclasses-0.8-pyh787bdff_0.tar.bz2#99ccd57a7c9761e97e17777f0cde21eb https://conda.anaconda.org/conda-forge/noarch/distlib-0.3.1-pyh9f0ad1d_0.tar.bz2#db990401a267e2b15854af5f3f84f763 https://conda.anaconda.org/conda-forge/noarch/filelock-3.0.12-pyh9f0ad1d_0.tar.bz2#7544ed05bbbe9bb687bc9bcbe4d6cb46 +https://conda.anaconda.org/conda-forge/noarch/fsspec-2021.5.0-pyhd8ed1ab_0.tar.bz2#da823f51ebb876dafbed1c892fd80956 +https://conda.anaconda.org/conda-forge/linux-64/glib-2.68.2-h9c3ff4c_0.tar.bz2#a418792ba3a452bff1ab2ed4df628f80 +https://conda.anaconda.org/conda-forge/linux-64/gst-plugins-base-1.18.4-hf529b03_2.tar.bz2#526fadaa13ec264cb919436953bc2766 https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.10.6-mpi_mpich_h996c276_1014.tar.bz2#6af2e2e4dfb0ef36c35042cd69a1599d +https://conda.anaconda.org/conda-forge/noarch/heapdict-1.0.1-py_0.tar.bz2#77242bfb1e74a627fb06319b5a2d3b95 +https://conda.anaconda.org/conda-forge/noarch/idna-2.10-pyh9f0ad1d_0.tar.bz2#f95a12b4f435aae6680fe55ae2eb1b06 +https://conda.anaconda.org/conda-forge/noarch/imagesize-1.2.0-py_0.tar.bz2#5879bd2c4b399a5072468e5fe587bf1b https://conda.anaconda.org/conda-forge/noarch/iniconfig-1.1.1-pyh9f0ad1d_0.tar.bz2#39161f81cc5e5ca45b8226fbb06c6905 +https://conda.anaconda.org/conda-forge/noarch/iris-sample-data-2.3.0-pyh9f0ad1d_0.tar.bz2#e4a33192da1a6dc4967ba18c6c765945 +https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.2-h78a0170_0.tar.bz2#ac0c23e6f3bbb61569781f00b5666f97 +https://conda.anaconda.org/conda-forge/noarch/locket-0.2.0-py_2.tar.bz2#709e8671651c7ec3d1ad07800339ff1d https://conda.anaconda.org/conda-forge/noarch/mccabe-0.6.1-py_1.tar.bz2#a326cb400c1ccd91789f3e7d02124d61 https://conda.anaconda.org/conda-forge/noarch/more-itertools-8.7.0-pyhd8ed1ab_1.tar.bz2#d51aa0bff3e4da989b2ab3a7f3e52dfc +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/pathspec-0.8.1-pyhd3deb0d_0.tar.bz2#fcd2fbb062b55d14a77e664c89ee17a6 https://conda.anaconda.org/conda-forge/linux-64/proj-7.2.0-h277dcde_2.tar.bz2#db654ee11298d3463bad67445707654c @@ -63,51 +132,119 @@ https://conda.anaconda.org/conda-forge/noarch/py-1.10.0-pyhd3deb0d_0.tar.bz2#88c https://conda.anaconda.org/conda-forge/noarch/pycodestyle-2.7.0-pyhd8ed1ab_0.tar.bz2#0234673eb2ecfbdf4e54574ab4d95f81 https://conda.anaconda.org/conda-forge/noarch/pycparser-2.20-pyh9f0ad1d_2.tar.bz2#aa798d50ffd182a0f6f31478c7f434f6 https://conda.anaconda.org/conda-forge/noarch/pyflakes-2.3.1-pyhd8ed1ab_0.tar.bz2#01e9ada82bd261ee2b6366aa832018cc +https://conda.anaconda.org/conda-forge/noarch/pyke-1.1.1-pyhd8ed1ab_1004.tar.bz2#5f0236abfbb6d53826d1afed1e64f82e https://conda.anaconda.org/conda-forge/noarch/pyparsing-2.4.7-pyh9f0ad1d_0.tar.bz2#626c4f20d5bf06dcec9cf2eaa31725c7 https://conda.anaconda.org/conda-forge/noarch/pyshp-2.1.3-pyh44b312d_0.tar.bz2#2d1867b980785eb44b8122184d8b42a6 https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.6-1_cp36m.tar.bz2#7f3681b01bd688b48bfbdae483b2918f +https://conda.anaconda.org/conda-forge/noarch/pytz-2021.1-pyhd8ed1ab_0.tar.bz2#3af2e9424d5eb0063824a3f9b850d411 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.1.0-pyhd8ed1ab_0.tar.bz2#f1d64c0cf0eedf655a96ccdc1573c05a +https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_0.tar.bz2#6d6552722448103793743dabfbda532d +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-1.0.3-py_0.tar.bz2#4508a40465ebf0105e52f7194f299411 +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/sphinxcontrib-serializinghtml-1.1.4-py_0.tar.bz2#8ea6a8036e28dba8827d35c764709358 +https://conda.anaconda.org/conda-forge/noarch/tblib-1.7.0-pyhd8ed1ab_0.tar.bz2#3d4afc31302aa7be471feb6be048ed76 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.1-py_0.tar.bz2#d1e66b58cb00b3817ad9f05eec098c00 https://conda.anaconda.org/conda-forge/noarch/typing_extensions-3.7.4.3-py_0.tar.bz2#12b96e382730541a4b332420227055ae +https://conda.anaconda.org/conda-forge/noarch/wheel-0.36.2-pyhd3deb0d_0.tar.bz2#768bfbe026426d0e76b377997d1f2b98 https://conda.anaconda.org/conda-forge/noarch/zipp-3.4.1-pyhd8ed1ab_0.tar.bz2#a4fa30eb74a326092b3d8078b1f1aae1 +https://conda.anaconda.org/conda-forge/linux-64/antlr-python-runtime-4.7.2-py36h5fab9bb_1002.tar.bz2#37df435690656fc56f8b031cd759ef77 +https://conda.anaconda.org/conda-forge/noarch/babel-2.9.1-pyh44b312d_0.tar.bz2#74136ed39bfea0832d338df1e58d013e https://conda.anaconda.org/conda-forge/linux-64/certifi-2020.12.5-py36h5fab9bb_1.tar.bz2#da529031bd8882eff7d72484661c1b83 https://conda.anaconda.org/conda-forge/linux-64/cffi-1.14.5-py36hc120d54_0.tar.bz2#f49c72aeb497efcd918217d142ddfc9c https://conda.anaconda.org/conda-forge/noarch/cfgv-3.2.0-py_0.tar.bz2#4972efcb3e2cbd3954b24a17266be25c -https://conda.anaconda.org/conda-forge/linux-64/click-8.0.0-py36h5fab9bb_0.tar.bz2#b9d57f660a87faa38203f56f4f431223 +https://conda.anaconda.org/conda-forge/linux-64/chardet-4.0.0-py36h5fab9bb_1.tar.bz2#b63c63a44b8d37acaff014df8a512d92 https://conda.anaconda.org/conda-forge/noarch/cycler-0.10.0-py_2.tar.bz2#f6d7c7e6d8f42cbbec7e07a8d879f91c +https://conda.anaconda.org/conda-forge/linux-64/cython-0.29.23-py36hc4f0c31_0.tar.bz2#0c18914235bf89e22bebd3ba077da8cb +https://conda.anaconda.org/conda-forge/linux-64/cytoolz-0.11.0-py36h8f6f2f9_3.tar.bz2#cfdf59a409935a32e9f51b37d25b66f7 +https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h48d8840_2.tar.bz2#eba672c69baf366fdedd1c6f702dbb81 +https://conda.anaconda.org/conda-forge/linux-64/docutils-0.16-py36h5fab9bb_3.tar.bz2#5d0a90c22c5c8a1f573df6b6f2afc5ee +https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-2.8.1-h83ec7ef_0.tar.bz2#654935b08e8bd4a8cbf6a4253e290c04 +https://conda.anaconda.org/conda-forge/linux-64/immutables-0.15-py36h8f6f2f9_0.tar.bz2#9f5767abe2f02d4bf73a7896a8f26790 https://conda.anaconda.org/conda-forge/linux-64/importlib-metadata-4.0.1-py36h5fab9bb_0.tar.bz2#3c9bfd1ccfdf3002b174e4e1e7385341 https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.3.1-py36h605e78d_1.tar.bz2#a92afbf92c5416585457e5de5c3d98c7 https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.8.0-mpi_mpich_hf07302c_2.tar.bz2#d76a3f327eb8e26b5ce6b042ac1abeb3 +https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.0.1-py36h8f6f2f9_0.tar.bz2#e450eb239eb68d0467b1c6d0fef28ae9 https://conda.anaconda.org/conda-forge/linux-64/mpi4py-3.0.3-py36h7b8b12a_7.tar.bz2#a85a032afabc323a400d618c448e278a +https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.0.2-py36h605e78d_1.tar.bz2#9460d0c8c77d3d5c410eb6743a48eca8 https://conda.anaconda.org/conda-forge/linux-64/mypy_extensions-0.4.3-py36h5fab9bb_3.tar.bz2#abc16a8fe5dc31bde74702e28ed59164 https://conda.anaconda.org/conda-forge/linux-64/numpy-1.19.5-py36h2aa4a07_1.tar.bz2#825e240765327dcdb8b0add973714e9e https://conda.anaconda.org/conda-forge/noarch/packaging-20.9-pyh44b312d_0.tar.bz2#be69a38e912054a62dc82cc3c7711a64 -https://conda.anaconda.org/conda-forge/linux-64/pillow-8.2.0-py36ha6010c0_1.tar.bz2#c6319092abb6107b05b59c67e83c74a3 -https://conda.anaconda.org/conda-forge/noarch/pydocstyle-6.1.0-pyhd8ed1ab_0.tar.bz2#7d17e3146e16c3313d937e031a14ca1f +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.2-py36h8328e55_0.tar.bz2#71ce4115a7035932d0abdaec69e1d432 +https://conda.anaconda.org/conda-forge/noarch/pockets-0.9.1-py_0.tar.bz2#1b52f0c42e8077e5a33e00fe72269364 +https://conda.anaconda.org/conda-forge/linux-64/psutil-5.8.0-py36h8f6f2f9_1.tar.bz2#ccecd9206d61f029549a81a980174ed8 +https://conda.anaconda.org/conda-forge/noarch/pydocstyle-6.1.1-pyhd8ed1ab_0.tar.bz2#e417954a987d382b3142886726ab3aad +https://conda.anaconda.org/conda-forge/linux-64/pyqt5-sip-4.19.18-py36hc4f0c31_7.tar.bz2#6bb1cad16acc8b2ac34fb4a75ef8cc09 +https://conda.anaconda.org/conda-forge/linux-64/pysocks-1.7.1-py36h5fab9bb_3.tar.bz2#4dfb9be0b2975bc7933f32c6db7af205 https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.8.1-py_0.tar.bz2#0d0150ed9c2d25817f5324108d3f7571 +https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-2.0.2-py36h8f6f2f9_0.tar.bz2#7fd3e41e00f0ebcac23b775ad5a8ca90 https://conda.anaconda.org/conda-forge/linux-64/pyyaml-5.4.1-py36h8f6f2f9_0.tar.bz2#c4be96c884afed3ce48ab143570a439f https://conda.anaconda.org/conda-forge/linux-64/regex-2021.4.4-py36h8f6f2f9_0.tar.bz2#df048f01fdcae6f0f75a05a4160b69d6 https://conda.anaconda.org/conda-forge/linux-64/tornado-6.1-py36h8f6f2f9_1.tar.bz2#3d19680e14cb7cf6f383ba1fd3a72f2c https://conda.anaconda.org/conda-forge/linux-64/typed-ast-1.4.3-py36h8f6f2f9_0.tar.bz2#738cc2808eaff7e220a6f46bbddd4e5a +https://conda.anaconda.org/conda-forge/noarch/zict-2.0.0-py_0.tar.bz2#4750152be22f24d695b3004c5e1712d3 https://conda.anaconda.org/conda-forge/noarch/black-20.8b1-py_1.tar.bz2#e555d6b71ec916c3dc4e6e3793cc9796 +https://conda.anaconda.org/conda-forge/linux-64/brotlipy-0.7.0-py36h8f6f2f9_1001.tar.bz2#0f244e9624403e17430e9d959530b01c +https://conda.anaconda.org/conda-forge/linux-64/cftime-1.2.1-py36h68bb277_1.tar.bz2#86ea5650a50b9d59336edb2ec57959de +https://conda.anaconda.org/conda-forge/noarch/contextvars-2.4-py_0.tar.bz2#295fe9300971a6bd1dc4b18ad6509be2 +https://conda.anaconda.org/conda-forge/linux-64/cryptography-3.4.7-py36hb60f036_0.tar.bz2#e3f8fbf0f4037279847c8ab1551fe6f8 +https://conda.anaconda.org/conda-forge/noarch/dask-core-2021.3.0-pyhd8ed1ab_0.tar.bz2#e7a647c6320649dd7c80a1938f1a211c https://conda.anaconda.org/conda-forge/linux-64/editdistance-s-1.0.0-py36h605e78d_1.tar.bz2#89c5489b410421aabe2888e73154b9d3 https://conda.anaconda.org/conda-forge/noarch/importlib_metadata-4.0.1-hd8ed1ab_0.tar.bz2#50c48f1394fba9705a76163409924628 +https://conda.anaconda.org/conda-forge/noarch/jinja2-3.0.1-pyhd8ed1ab_0.tar.bz2#c647e77921fd3e245cdcc5b2d451a0f8 +https://conda.anaconda.org/conda-forge/linux-64/mo_pack-0.2.0-py36h92226af_1005.tar.bz2#b67300a68479aae643fa443c8073ad22 https://conda.anaconda.org/conda-forge/linux-64/netcdf-fortran-4.5.3-mpi_mpich_h196b126_4.tar.bz2#e058f42a78ea8c965cf7335e28143c59 +https://conda.anaconda.org/conda-forge/linux-64/pandas-1.1.5-py36h284efc9_0.tar.bz2#e5e3d1a5401c1c932ada9d4f0b6c8448 +https://conda.anaconda.org/conda-forge/linux-64/pango-1.48.5-hb8ff022_0.tar.bz2#f4e263c4dfa15b6a97349782793d1ee7 +https://conda.anaconda.org/conda-forge/linux-64/python-stratify-0.1.1-py36h4d9540e_1004.tar.bz2#372e9cdbd81fd5c280ce2b4064e92ffa +https://conda.anaconda.org/conda-forge/linux-64/pywavelets-1.1.1-py36h92226af_3.tar.bz2#ea7f1093a7ac3c449d7ea8984e6fd873 +https://conda.anaconda.org/conda-forge/linux-64/qt-5.12.9-hda022c4_4.tar.bz2#afebab1f5049d66baaaec67d9ce893f0 https://conda.anaconda.org/conda-forge/linux-64/scipy-1.5.3-py36h9e8f40b_0.tar.bz2#39502ad94bcb186c0cf4eb7532316d6a https://conda.anaconda.org/conda-forge/linux-64/setuptools-49.6.0-py36h5fab9bb_3.tar.bz2#0e5930ee136de4ecef3640f50b3037a2 -https://conda.anaconda.org/conda-forge/linux-64/shapely-1.7.1-py36h93b233e_4.tar.bz2#bf7457dee29298d9f958382cc2409489 +https://conda.anaconda.org/conda-forge/linux-64/shapely-1.7.1-py36h3bbdef7_4.tar.bz2#013d5541ce8da8bcf9e1df79d5074458 +https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-napoleon-0.7-py_0.tar.bz2#0bc25ff6f2e34af63ded59692df5f749 +https://conda.anaconda.org/conda-forge/linux-64/asv-0.4.2-py36hc4f0c31_2.tar.bz2#aba2d553655f81aa4ec683f0039104e1 +https://conda.anaconda.org/conda-forge/linux-64/bokeh-2.1.1-py36h9f0ad1d_0.tar.bz2#f2b02dad779533dc04af6698949f02d3 +https://conda.anaconda.org/conda-forge/linux-64/cf-units-2.1.5-py36h4d9540e_0.tar.bz2#95e04b7b16b85009509a9c93577d9dc0 +https://conda.anaconda.org/conda-forge/linux-64/distributed-2021.3.0-py36h5fab9bb_0.tar.bz2#d484e4c9daade19800341eedff55f1d2 https://conda.anaconda.org/conda-forge/linux-64/esmf-8.1.1-mpi_mpich_h3dcaa78_100.tar.bz2#5b4bab1017226f2c03ba0fe02b783316 https://conda.anaconda.org/conda-forge/noarch/flake8-3.9.2-pyhd8ed1ab_0.tar.bz2#37d685abea0a25c921431edda02ad143 +https://conda.anaconda.org/conda-forge/linux-64/gtk2-2.24.33-h539f30e_1.tar.bz2#606777b4da3664d5c9415f5f165349fd https://conda.anaconda.org/conda-forge/noarch/identify-2.2.4-pyhd8ed1ab_0.tar.bz2#86a6446475a53451a71e1515150cb532 +https://conda.anaconda.org/conda-forge/noarch/imagehash-4.2.0-pyhd8ed1ab_0.tar.bz2#e5a77472ae964f2835fce16355bbfe64 https://conda.anaconda.org/conda-forge/linux-64/importlib_resources-5.1.3-py36h5fab9bb_0.tar.bz2#a1871af16c51ad3ced8730e1ddb3b396 +https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.50.5-hc3c00ef_0.tar.bz2#1362366116e80bcfbe9c7cd99766ea40 https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.3.4-py36hd391965_0.tar.bz2#ed3c55ad68aa87ba9c2b2d5f6ede7f14 +https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.5.6-nompi_py36h3d597d4_103.tar.bz2#0d09a7bdf15e8efc761d54e0060ee189 https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.6.0-pyhd8ed1ab_0.tar.bz2#0941325bf48969e2b3b19d0951740950 +https://conda.anaconda.org/conda-forge/noarch/pip-21.1.1-pyhd8ed1ab_0.tar.bz2#e0bad1330c3575222d221e7679a36a0a https://conda.anaconda.org/conda-forge/linux-64/pluggy-0.13.1-py36h5fab9bb_4.tar.bz2#8b1835b504fd37558160c30103651403 +https://conda.anaconda.org/conda-forge/noarch/pygments-2.9.0-pyhd8ed1ab_0.tar.bz2#a2d9bba43c9b80a42b0ccb9afd7223c2 +https://conda.anaconda.org/conda-forge/noarch/pyopenssl-20.0.1-pyhd8ed1ab_0.tar.bz2#92371c25994d0f5d28a01c1fb75ebf86 +https://conda.anaconda.org/conda-forge/linux-64/pyqt-impl-5.12.3-py36h7ec31b9_7.tar.bz2#379005311c6e733b228723e67fc52fb2 https://conda.anaconda.org/conda-forge/linux-64/cartopy-0.19.0.post1-py36hbcbf2fa_0.tar.bz2#a880959967e1929884337b6b1be5dd78 +https://conda.anaconda.org/conda-forge/noarch/dask-2021.3.0-pyhd8ed1ab_0.tar.bz2#ad8913a398eedda25f6243d02c973f28 https://conda.anaconda.org/conda-forge/linux-64/esmpy-8.1.1-mpi_mpich_py36hcd78dbd_100.tar.bz2#cce5b688501b00de155299a82365852c https://conda.anaconda.org/conda-forge/noarch/flake8-docstrings-1.6.0-pyhd8ed1ab_0.tar.bz2#dea85c9bee98ef38c90fa7348f7fa333 https://conda.anaconda.org/conda-forge/noarch/flake8-import-order-0.18.1-py_0.tar.bz2#b3fca0d4b267be46b6d38fa77bf36f9a +https://conda.anaconda.org/conda-forge/linux-64/graphviz-2.47.1-h85b4f2f_1.tar.bz2#4b7c5657eac8ddfa0cae06f6f9a2a275 +https://conda.anaconda.org/conda-forge/noarch/nc-time-axis-1.2.0-py_1.tar.bz2#f3158a5d335f0f44f09cf05d3fb4107e +https://conda.anaconda.org/conda-forge/linux-64/pyqtchart-5.12-py36h7ec31b9_7.tar.bz2#69735aad86e57eb4de60cf7f208b8604 +https://conda.anaconda.org/conda-forge/linux-64/pyqtwebengine-5.12.1-py36h7ec31b9_7.tar.bz2#4d15c862d7989dcc1fa4f321d27a2d66 https://conda.anaconda.org/conda-forge/linux-64/pytest-6.2.4-py36h5fab9bb_0.tar.bz2#c14178266877b2b826f3a1f9210bbcea +https://conda.anaconda.org/conda-forge/noarch/urllib3-1.26.4-pyhd8ed1ab_0.tar.bz2#d7b20b328e23d993994ea02077c009c0 https://conda.anaconda.org/conda-forge/linux-64/virtualenv-20.4.6-py36h5fab9bb_0.tar.bz2#29e771278bffe7b3f119992635c5fc0d https://conda.anaconda.org/conda-forge/linux-64/pre-commit-2.12.1-py36h5fab9bb_0.tar.bz2#27613fa5d0a4871f4317f2b6e9786104 +https://conda.anaconda.org/conda-forge/linux-64/pyqt-5.12.3-py36h5fab9bb_7.tar.bz2#02315b51b199ef6700a53debf1bada5b +https://conda.anaconda.org/conda-forge/noarch/requests-2.25.1-pyhd3deb0d_0.tar.bz2#ae687aba31a1c400192a86a2e993ffdc +https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.3.4-py36h5fab9bb_0.tar.bz2#a3731e3e7e412e7b2f88593f92b74864 +https://conda.anaconda.org/conda-forge/noarch/sphinx-3.5.4-pyh44b312d_0.tar.bz2#0ebc444f001f73c4f6de01057b0be392 +https://conda.anaconda.org/conda-forge/noarch/sphinx-copybutton-0.3.1-pyhd8ed1ab_0.tar.bz2#decad13214a2a545944560eccf4a9815 +https://conda.anaconda.org/conda-forge/noarch/sphinx-gallery-0.9.0-pyhd8ed1ab_0.tar.bz2#5ef222a3e1b5904742e376e05046692b +https://conda.anaconda.org/conda-forge/noarch/sphinx-panels-0.5.2-pyhd3deb0d_0.tar.bz2#1a871a63c4be1bd47a7aa48b7417a426 +https://conda.anaconda.org/conda-forge/noarch/sphinx_rtd_theme-0.5.2-pyhd8ed1ab_1.tar.bz2#7434e891fc767cb0d39d90751720c8ec diff --git a/requirements/nox.lock/py37-linux-64.lock b/requirements/nox.lock/py37-linux-64.lock index 330b80b0..b29961be 100644 --- a/requirements/nox.lock/py37-linux-64.lock +++ b/requirements/nox.lock/py37-linux-64.lock @@ -1,61 +1,129 @@ # platform: linux-64 -# env_hash: d7216f5010469496a1f35a86e2690c1d81b8071599706f67d1970cbc47eefbe6 +# env_hash: 19592b2ceed3876b76de94d6ea4c1c0c36f92e13e488824bc5c7565d7d0cbfd2 @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-2020.12.5-ha878542_0.tar.bz2#7eb5d4ffeee663caa1635cd67071bc1b +https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2#0c96522c6bdaed4b1566d11387caaf45 +https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6 +https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb +https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-hab24e00_0.tar.bz2#19410c3df09dfb12d1206132a1d357c5 https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.35.1-hea4e1c9_2.tar.bz2#83610dba766a186bdc7a116053b782a4 https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-9.3.0-hff62375_19.tar.bz2#c2d8da3cb171e4aa642d20c6e4e42a04 https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-9.3.0-h6de172a_19.tar.bz2#cd9a24a8dde03ec0cf0e603b0bea85a1 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.23-ha770c72_2.tar.bz2#ce876d0c998e1e2eb1dc67b01937737f +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-9.3.0-hff62375_19.tar.bz2#aea379bd68fdcdf9499fa1453f852ac1 https://conda.anaconda.org/conda-forge/linux-64/libgomp-9.3.0-h2828fa1_19.tar.bz2#ab0a307912033126da02507b59e79ec9 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-9.3.0-h2828fa1_19.tar.bz2#9d5cdfc51476ee4dcdd96ed2dca3f943 +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.17.1-h7f98852_1.tar.bz2#ed1dc233ed5e3eaa9bfbaac64d130c5e +https://conda.anaconda.org/conda-forge/linux-64/expat-2.3.0-h9c3ff4c_0.tar.bz2#1fb8f0254eb78a2a0c120155e1b1a207 +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.9.1-h9c3ff4c_2.tar.bz2#b9a6d9422aed3ad84ec6ccee9bfcaa0f +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-68.1-h58526e2_0.tar.bz2#fc7a4271dc2a7f4fd78cd63695baf7c3 https://conda.anaconda.org/conda-forge/linux-64/jpeg-9d-h36c2ea0_0.tar.bz2#ea02ce6037dbe81803ae6123e5ba1568 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.3-h58526e2_2.tar.bz2#665369991d8dd290ac5ee92fce3e6bf5 -https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.15-pthreads_h8fe5266_0.tar.bz2#b4bdbbff92cacdd7b28b1b0a0366b24b +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/libogg-1.3.4-h7f98852_1.tar.bz2#6e8cc2173440d77708196c5b93771680 +https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.15-pthreads_h8fe5266_1.tar.bz2#bb5527a16584426a897f22643d9a36a6 +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-h58526e2_1007.tar.bz2#7f6569a0c2f27acb8fc90600b382e544 +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.0-h7f98852_2.tar.bz2#fb63a035a3b552c88a30d84b89ebf4c4 https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.9.3-h9c3ff4c_0.tar.bz2#4eb64ee0d5cd43096ffcf843c76b05d4 https://conda.anaconda.org/conda-forge/linux-64/mpich-3.4.1-h846660c_104.tar.bz2#94f01e56905a7af1479c9f72b00e9864 https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.2-h58526e2_4.tar.bz2#509f2a21c4a09214cd737a480dfd80c9 +https://conda.anaconda.org/conda-forge/linux-64/nspr-4.30-h9c3ff4c_0.tar.bz2#e6dc1f8f6e0bcebe8e3d8a5bca258dbe https://conda.anaconda.org/conda-forge/linux-64/openssl-1.1.1k-h7f98852_0.tar.bz2#07fae2cb088379c8441e0f3ffa1f4025 +https://conda.anaconda.org/conda-forge/linux-64/pcre-8.44-he1b5a44_0.tar.bz2#e647d89cd5cdf62760cf283a001841ff +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/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-h516909a_0.tar.bz2#03a530e925414902547cf48da7756db8 https://conda.anaconda.org/conda-forge/linux-64/zlib-1.2.11-h516909a_1010.tar.bz2#339cc5584e6d26bc73a875ba900028c3 -https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h10796ff_2.tar.bz2#c6f500917ce0d20043a7e3de53b0e5bd +https://conda.anaconda.org/conda-forge/linux-64/gettext-0.19.8.1-h0b5b191_1005.tar.bz2#ff6f69b593a9e74c0e6b61908ac513fa +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/libblas-3.9.0-9_openblas.tar.bz2#5f08755e98b2a43ca68124e629a5a0cb 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-hcdb4288_3.tar.bz2#d8f51405997093ff1799ded7650439c4 +https://conda.anaconda.org/conda-forge/linux-64/libllvm11-11.1.0-hf817b99_2.tar.bz2#646fa2f7c60b69ee8f918668e9c2fd31 https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.43.0-h812cca2_0.tar.bz2#1867d1e9658596b3fac8847a7702eef4 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.9.0-ha56f1ee_6.tar.bz2#f0dfb86444df325e599dbc3f4c0a3f5b +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_1003.tar.bz2#a9371e9e40aded194dcba1447606c9a1 +https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.9.12-h72842e0_0.tar.bz2#bd14fdf5b9ee5568056a40a6a2f41866 https://conda.anaconda.org/conda-forge/linux-64/libzip-1.7.3-h4de3113_0.tar.bz2#2568763f88009f95e9262cba837dbb82 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.10-h21135ba_1.tar.bz2#c647f70aa7e3d4cc4e029cc1c9a99953 -https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.0-ha95c52a_0.tar.bz2#b56f94865e2de36abf054e7bfa499034 +https://conda.anaconda.org/conda-forge/linux-64/udunits2-2.2.27.27-h975c496_1.tar.bz2#e663bd5dbc8cc4c1647d9f51cf25872c +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/zstd-1.4.9-ha95c52a_0.tar.bz2#b481dc9fda3af2a681d08a4d5cd1ea0b 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/krb5-1.19.1-hcc1bbae_0.tar.bz2#59b0695a515a6c54d45463dbf208ae38 https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-9_openblas.tar.bz2#edee85b4f83376ceae81e0975b8bffa2 +https://conda.anaconda.org/conda-forge/linux-64/libclang-11.1.0-default_ha53f305_1.tar.bz2#b9b71585ca4fcb5d442c5a9df5dd7e98 +https://conda.anaconda.org/conda-forge/linux-64/libglib-2.68.2-h3e27bee_0.tar.bz2#a48401ff2ecb708b8b08bf3547eff205 https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-9_openblas.tar.bz2#572d84ab07962986f6dd8e4637a475ca -https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.2.0-hf544144_3.tar.bz2#53798351da909e181243e17dccee41ad +https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.2.0-hbd63e13_2.tar.bz2#e3f034b29a122699b06da40c155f1a70 +https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.0.3-he3ba5ed_0.tar.bz2#f9dbabc7e01c459ed7a1d1d64b206e9b +https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-8.0.23-h935591d_2.tar.bz2#b36368d163fca85e110529ddc4c67985 https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.35.5-h74cdb3f_0.tar.bz2#e876c82c21e7074d299e13762d02466c +https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.7.1-h7f98852_0.tar.bz2#1759774cc5f1e965178c9cbab0b29a13 +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/fontconfig-2.13.1-hba837de_1005.tar.bz2#fd3611672eb91bc9d24fd6fb970037eb +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/glib-tools-2.68.2-h9c3ff4c_0.tar.bz2#3afedfe4c8f500ed953dcc488730908a +https://conda.anaconda.org/conda-forge/linux-64/gstreamer-1.18.4-h76c114f_2.tar.bz2#5db765d4974fa89f64c1544eb2a552cb +https://conda.anaconda.org/conda-forge/linux-64/gts-0.7.6-h64030ff_2.tar.bz2#112eb9b5b93f0c02e59aea4fd1967363 https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.12-hddcbb42_0.tar.bz2#797117394a4aa588de6d741b06fad80f https://conda.anaconda.org/conda-forge/linux-64/libcurl-7.76.1-h2574ce0_2.tar.bz2#cc7097ea31a80337c2203c22f08d3f03 -https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.4.0-hb52868f_1.tar.bz2#b7ad78ad2e9ee155f59e6428406ee824 +https://conda.anaconda.org/conda-forge/linux-64/libpq-13.3-hd57d9b9_0.tar.bz2#66ef2cacc483205b7d303f7b02601c3b +https://conda.anaconda.org/conda-forge/linux-64/libwebp-1.2.0-h3452ae3_0.tar.bz2#8f4e19a8988c38feec7db41bcd0bf0d0 +https://conda.anaconda.org/conda-forge/linux-64/nss-3.65-hb5efdd6_0.tar.bz2#645afd59bb8a540be3545850f586e028 https://conda.anaconda.org/conda-forge/linux-64/python-3.7.10-hffdb5ce_100_cpython.tar.bz2#7425fffa658971915f595e9110163c3c +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/appdirs-1.4.4-pyh9f0ad1d_0.tar.bz2#5f095bc6454094e96f146491fd03633b https://conda.anaconda.org/conda-forge/noarch/attrs-21.2.0-pyhd8ed1ab_0.tar.bz2#d2e1c7f388ac403df7079b411c37cc50 +https://conda.anaconda.org/conda-forge/linux-64/cairo-1.16.0-h6cf1ce9_1008.tar.bz2#a43fb47d15e116f8be4be7e6b17ab59f +https://conda.anaconda.org/conda-forge/noarch/cloudpickle-1.6.0-py_0.tar.bz2#76d764d8881719e305f6fa368dc2b65e +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.76.1-hea6ffbf_2.tar.bz2#0312ab411fff1aca55e45ea3f885a8ab https://conda.anaconda.org/conda-forge/noarch/dataclasses-0.8-pyhc8e2a94_1.tar.bz2#28e0de0ecba81334619a777fdc00febc https://conda.anaconda.org/conda-forge/noarch/distlib-0.3.1-pyh9f0ad1d_0.tar.bz2#db990401a267e2b15854af5f3f84f763 https://conda.anaconda.org/conda-forge/noarch/filelock-3.0.12-pyh9f0ad1d_0.tar.bz2#7544ed05bbbe9bb687bc9bcbe4d6cb46 +https://conda.anaconda.org/conda-forge/noarch/fsspec-2021.5.0-pyhd8ed1ab_0.tar.bz2#da823f51ebb876dafbed1c892fd80956 +https://conda.anaconda.org/conda-forge/linux-64/glib-2.68.2-h9c3ff4c_0.tar.bz2#a418792ba3a452bff1ab2ed4df628f80 +https://conda.anaconda.org/conda-forge/linux-64/gst-plugins-base-1.18.4-hf529b03_2.tar.bz2#526fadaa13ec264cb919436953bc2766 https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.10.6-mpi_mpich_h996c276_1014.tar.bz2#6af2e2e4dfb0ef36c35042cd69a1599d +https://conda.anaconda.org/conda-forge/noarch/heapdict-1.0.1-py_0.tar.bz2#77242bfb1e74a627fb06319b5a2d3b95 +https://conda.anaconda.org/conda-forge/noarch/idna-2.10-pyh9f0ad1d_0.tar.bz2#f95a12b4f435aae6680fe55ae2eb1b06 +https://conda.anaconda.org/conda-forge/noarch/imagesize-1.2.0-py_0.tar.bz2#5879bd2c4b399a5072468e5fe587bf1b https://conda.anaconda.org/conda-forge/noarch/iniconfig-1.1.1-pyh9f0ad1d_0.tar.bz2#39161f81cc5e5ca45b8226fbb06c6905 +https://conda.anaconda.org/conda-forge/noarch/iris-sample-data-2.3.0-pyh9f0ad1d_0.tar.bz2#e4a33192da1a6dc4967ba18c6c765945 +https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.2-h78a0170_0.tar.bz2#ac0c23e6f3bbb61569781f00b5666f97 +https://conda.anaconda.org/conda-forge/noarch/locket-0.2.0-py_2.tar.bz2#709e8671651c7ec3d1ad07800339ff1d https://conda.anaconda.org/conda-forge/noarch/mccabe-0.6.1-py_1.tar.bz2#a326cb400c1ccd91789f3e7d02124d61 https://conda.anaconda.org/conda-forge/noarch/more-itertools-8.7.0-pyhd8ed1ab_1.tar.bz2#d51aa0bff3e4da989b2ab3a7f3e52dfc +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/pathspec-0.8.1-pyhd3deb0d_0.tar.bz2#fcd2fbb062b55d14a77e664c89ee17a6 https://conda.anaconda.org/conda-forge/linux-64/proj-7.2.0-h277dcde_2.tar.bz2#db654ee11298d3463bad67445707654c @@ -63,50 +131,117 @@ https://conda.anaconda.org/conda-forge/noarch/py-1.10.0-pyhd3deb0d_0.tar.bz2#88c https://conda.anaconda.org/conda-forge/noarch/pycodestyle-2.7.0-pyhd8ed1ab_0.tar.bz2#0234673eb2ecfbdf4e54574ab4d95f81 https://conda.anaconda.org/conda-forge/noarch/pycparser-2.20-pyh9f0ad1d_2.tar.bz2#aa798d50ffd182a0f6f31478c7f434f6 https://conda.anaconda.org/conda-forge/noarch/pyflakes-2.3.1-pyhd8ed1ab_0.tar.bz2#01e9ada82bd261ee2b6366aa832018cc +https://conda.anaconda.org/conda-forge/noarch/pyke-1.1.1-pyhd8ed1ab_1004.tar.bz2#5f0236abfbb6d53826d1afed1e64f82e https://conda.anaconda.org/conda-forge/noarch/pyparsing-2.4.7-pyh9f0ad1d_0.tar.bz2#626c4f20d5bf06dcec9cf2eaa31725c7 https://conda.anaconda.org/conda-forge/noarch/pyshp-2.1.3-pyh44b312d_0.tar.bz2#2d1867b980785eb44b8122184d8b42a6 https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.7-1_cp37m.tar.bz2#658a5c3d766bfc6574480204b10a6f20 +https://conda.anaconda.org/conda-forge/noarch/pytz-2021.1-pyhd8ed1ab_0.tar.bz2#3af2e9424d5eb0063824a3f9b850d411 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.1.0-pyhd8ed1ab_0.tar.bz2#f1d64c0cf0eedf655a96ccdc1573c05a +https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_0.tar.bz2#6d6552722448103793743dabfbda532d +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-1.0.3-py_0.tar.bz2#4508a40465ebf0105e52f7194f299411 +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/sphinxcontrib-serializinghtml-1.1.4-py_0.tar.bz2#8ea6a8036e28dba8827d35c764709358 +https://conda.anaconda.org/conda-forge/noarch/tblib-1.7.0-pyhd8ed1ab_0.tar.bz2#3d4afc31302aa7be471feb6be048ed76 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.1-py_0.tar.bz2#d1e66b58cb00b3817ad9f05eec098c00 https://conda.anaconda.org/conda-forge/noarch/typing_extensions-3.7.4.3-py_0.tar.bz2#12b96e382730541a4b332420227055ae +https://conda.anaconda.org/conda-forge/noarch/wheel-0.36.2-pyhd3deb0d_0.tar.bz2#768bfbe026426d0e76b377997d1f2b98 https://conda.anaconda.org/conda-forge/noarch/zipp-3.4.1-pyhd8ed1ab_0.tar.bz2#a4fa30eb74a326092b3d8078b1f1aae1 +https://conda.anaconda.org/conda-forge/linux-64/antlr-python-runtime-4.7.2-py37h89c1867_1002.tar.bz2#cf3aeeb80dbd517761019a8edcd5b108 +https://conda.anaconda.org/conda-forge/noarch/babel-2.9.1-pyh44b312d_0.tar.bz2#74136ed39bfea0832d338df1e58d013e https://conda.anaconda.org/conda-forge/linux-64/certifi-2020.12.5-py37h89c1867_1.tar.bz2#fb121f213009359498ada17a9e6d775f https://conda.anaconda.org/conda-forge/linux-64/cffi-1.14.5-py37hc58025e_0.tar.bz2#e05f1fad0c52c21b6b92778d31f89cd0 https://conda.anaconda.org/conda-forge/noarch/cfgv-3.2.0-py_0.tar.bz2#4972efcb3e2cbd3954b24a17266be25c -https://conda.anaconda.org/conda-forge/linux-64/click-8.0.0-py37h89c1867_0.tar.bz2#ebfa897ed57877e45ce49f87e00944d9 +https://conda.anaconda.org/conda-forge/linux-64/chardet-4.0.0-py37h89c1867_1.tar.bz2#f4fbd4721b80f0d6b53b3a3374914068 https://conda.anaconda.org/conda-forge/noarch/cycler-0.10.0-py_2.tar.bz2#f6d7c7e6d8f42cbbec7e07a8d879f91c +https://conda.anaconda.org/conda-forge/linux-64/cython-0.29.23-py37hcd2ae1e_0.tar.bz2#1298b2e01d979e35e1e7a2840cf819e5 +https://conda.anaconda.org/conda-forge/linux-64/cytoolz-0.11.0-py37h5e8e339_3.tar.bz2#2e89a6f3baf5eeb13763f61ea3d0601f +https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h48d8840_2.tar.bz2#eba672c69baf366fdedd1c6f702dbb81 +https://conda.anaconda.org/conda-forge/linux-64/docutils-0.16-py37h89c1867_3.tar.bz2#3da23bcf1d502670cec18fd3a04f409b +https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-2.8.1-h83ec7ef_0.tar.bz2#654935b08e8bd4a8cbf6a4253e290c04 https://conda.anaconda.org/conda-forge/linux-64/importlib-metadata-4.0.1-py37h89c1867_0.tar.bz2#fdb45d5b60c896c95d25213e9e257d09 https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.3.1-py37h2527ec5_1.tar.bz2#61149814e0ea71cb5b44881c65d25f7b https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.8.0-mpi_mpich_hf07302c_2.tar.bz2#d76a3f327eb8e26b5ce6b042ac1abeb3 +https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.0.1-py37h5e8e339_0.tar.bz2#90ad307f6997784664de956e09ec689e https://conda.anaconda.org/conda-forge/linux-64/mpi4py-3.0.3-py37h1e5cb63_7.tar.bz2#1c0450be22dc0fbffaabab1f415705d5 +https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.0.2-py37h2527ec5_1.tar.bz2#07952b04eee02d873daa311a35b27454 https://conda.anaconda.org/conda-forge/linux-64/mypy_extensions-0.4.3-py37h89c1867_3.tar.bz2#b604f0897a7a207bddd7d05bf3284752 -https://conda.anaconda.org/conda-forge/linux-64/numpy-1.20.2-py37h038b26d_0.tar.bz2#795bd784e989b08ceb1ce8be539dc0b2 +https://conda.anaconda.org/conda-forge/linux-64/numpy-1.20.3-py37h038b26d_0.tar.bz2#c22231bc611c7d889d49cb78b351555e https://conda.anaconda.org/conda-forge/noarch/packaging-20.9-pyh44b312d_0.tar.bz2#be69a38e912054a62dc82cc3c7711a64 -https://conda.anaconda.org/conda-forge/linux-64/pillow-8.2.0-py37h4600e1f_1.tar.bz2#8998270d7ebbb22261ab440176d3a5cf -https://conda.anaconda.org/conda-forge/noarch/pydocstyle-6.1.0-pyhd8ed1ab_0.tar.bz2#7d17e3146e16c3313d937e031a14ca1f +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.2-py37h718be6c_0.tar.bz2#ecac4e308b87ff93d44ea5e56ab39084 +https://conda.anaconda.org/conda-forge/noarch/pockets-0.9.1-py_0.tar.bz2#1b52f0c42e8077e5a33e00fe72269364 +https://conda.anaconda.org/conda-forge/linux-64/psutil-5.8.0-py37h5e8e339_1.tar.bz2#2923250371b05e798f3732531cdb5300 +https://conda.anaconda.org/conda-forge/noarch/pydocstyle-6.1.1-pyhd8ed1ab_0.tar.bz2#e417954a987d382b3142886726ab3aad +https://conda.anaconda.org/conda-forge/linux-64/pyqt5-sip-4.19.18-py37hcd2ae1e_7.tar.bz2#f94e01aa4abd458b556d68fdb5f19b99 +https://conda.anaconda.org/conda-forge/linux-64/pysocks-1.7.1-py37h89c1867_3.tar.bz2#bd069d59ee91a2e26552cd7bb4c64032 https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.8.1-py_0.tar.bz2#0d0150ed9c2d25817f5324108d3f7571 +https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-2.0.2-py37h5e8e339_0.tar.bz2#3f1e633378cd3c8b1ce13b3f2f5eadd7 https://conda.anaconda.org/conda-forge/linux-64/pyyaml-5.4.1-py37h5e8e339_0.tar.bz2#090550b9425fe9a87dc1ec7fde201633 https://conda.anaconda.org/conda-forge/linux-64/regex-2021.4.4-py37h5e8e339_0.tar.bz2#58fcc3d7115356472761ca6d50bd9a34 https://conda.anaconda.org/conda-forge/linux-64/tornado-6.1-py37h5e8e339_1.tar.bz2#92449128c4639feae48d731ef2186099 https://conda.anaconda.org/conda-forge/linux-64/typed-ast-1.4.3-py37h5e8e339_0.tar.bz2#0e8dc105f8cf0a2a6456f7a18f34a712 -https://conda.anaconda.org/conda-forge/noarch/black-20.8b1-py_1.tar.bz2#e555d6b71ec916c3dc4e6e3793cc9796 +https://conda.anaconda.org/conda-forge/noarch/zict-2.0.0-py_0.tar.bz2#4750152be22f24d695b3004c5e1712d3 +https://conda.anaconda.org/conda-forge/linux-64/brotlipy-0.7.0-py37h5e8e339_1001.tar.bz2#871eed4ba322e7b3f200956a096b34e7 +https://conda.anaconda.org/conda-forge/linux-64/cftime-1.2.1-py37h161383b_1.tar.bz2#314ca8b00ed742f8c46a6cc68d84d90f +https://conda.anaconda.org/conda-forge/linux-64/click-8.0.1-py37h89c1867_0.tar.bz2#bb1ad97b5d8626f662b753f620c3c913 +https://conda.anaconda.org/conda-forge/linux-64/cryptography-3.4.7-py37h5d9358c_0.tar.bz2#d811fb6a96ae0cf8c0a17457a8e67ff4 +https://conda.anaconda.org/conda-forge/noarch/dask-core-2021.5.0-pyhd8ed1ab_0.tar.bz2#5a3c5c4242795ce5d69cfb5fe9613362 https://conda.anaconda.org/conda-forge/linux-64/editdistance-s-1.0.0-py37h2527ec5_1.tar.bz2#100918f43247cedad74f2cf8dcbda5bc https://conda.anaconda.org/conda-forge/noarch/importlib_metadata-4.0.1-hd8ed1ab_0.tar.bz2#50c48f1394fba9705a76163409924628 +https://conda.anaconda.org/conda-forge/noarch/jinja2-3.0.1-pyhd8ed1ab_0.tar.bz2#c647e77921fd3e245cdcc5b2d451a0f8 +https://conda.anaconda.org/conda-forge/linux-64/mo_pack-0.2.0-py37h902c9e0_1005.tar.bz2#40db532422636dd1e980154114486a00 https://conda.anaconda.org/conda-forge/linux-64/netcdf-fortran-4.5.3-mpi_mpich_h196b126_4.tar.bz2#e058f42a78ea8c965cf7335e28143c59 +https://conda.anaconda.org/conda-forge/linux-64/pandas-1.2.4-py37h219a48f_0.tar.bz2#fb33763b90acf1a4a7cffb7ab994a3cd +https://conda.anaconda.org/conda-forge/linux-64/pango-1.48.5-hb8ff022_0.tar.bz2#f4e263c4dfa15b6a97349782793d1ee7 +https://conda.anaconda.org/conda-forge/linux-64/python-stratify-0.1.1-py37h6f94858_1004.tar.bz2#42b37830a63405589fef3d13db505e7d +https://conda.anaconda.org/conda-forge/linux-64/pywavelets-1.1.1-py37h902c9e0_3.tar.bz2#104648a5a091a493046a62704eef5c49 +https://conda.anaconda.org/conda-forge/linux-64/qt-5.12.9-hda022c4_4.tar.bz2#afebab1f5049d66baaaec67d9ce893f0 https://conda.anaconda.org/conda-forge/linux-64/scipy-1.6.3-py37h29e03ee_0.tar.bz2#a469d02f72b9cef07f4408d419b17dcc https://conda.anaconda.org/conda-forge/linux-64/setuptools-49.6.0-py37h89c1867_3.tar.bz2#928c178bf6805b8ab71fabaa620e0234 -https://conda.anaconda.org/conda-forge/linux-64/shapely-1.7.1-py37hf7ed6d2_4.tar.bz2#0209359199915534c567ffc7fafdb6c4 +https://conda.anaconda.org/conda-forge/linux-64/shapely-1.7.1-py37h2d1e849_4.tar.bz2#98c91519669fac0bd793b0aea90468ba +https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-napoleon-0.7-py_0.tar.bz2#0bc25ff6f2e34af63ded59692df5f749 https://conda.anaconda.org/conda-forge/linux-64/virtualenv-20.4.6-py37h89c1867_0.tar.bz2#e66974ad67ebbb86a5fcbb9551641be1 +https://conda.anaconda.org/conda-forge/linux-64/asv-0.4.2-py37hcd2ae1e_2.tar.bz2#a539a23d322e3976dda4af86e59b31ce +https://conda.anaconda.org/conda-forge/noarch/black-20.8b1-py_1.tar.bz2#e555d6b71ec916c3dc4e6e3793cc9796 +https://conda.anaconda.org/conda-forge/linux-64/bokeh-2.1.1-py37hc8dfbb8_0.tar.bz2#0927f1a093279ba797f014c5e484a58f +https://conda.anaconda.org/conda-forge/linux-64/cf-units-2.1.5-py37h6f94858_0.tar.bz2#5ad0ed053fc1c3bf156cf181cdded003 +https://conda.anaconda.org/conda-forge/linux-64/distributed-2021.5.0-py37h89c1867_0.tar.bz2#790ad8468564c11a7f06927e04d334af https://conda.anaconda.org/conda-forge/linux-64/esmf-8.1.1-mpi_mpich_h3dcaa78_100.tar.bz2#5b4bab1017226f2c03ba0fe02b783316 https://conda.anaconda.org/conda-forge/noarch/flake8-3.9.2-pyhd8ed1ab_0.tar.bz2#37d685abea0a25c921431edda02ad143 +https://conda.anaconda.org/conda-forge/linux-64/gtk2-2.24.33-h539f30e_1.tar.bz2#606777b4da3664d5c9415f5f165349fd https://conda.anaconda.org/conda-forge/noarch/identify-2.2.4-pyhd8ed1ab_0.tar.bz2#86a6446475a53451a71e1515150cb532 -https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.4.2-py37hdd32ed1_0.tar.bz2#ee755b80aae171058a46c5d7badd08ff +https://conda.anaconda.org/conda-forge/noarch/imagehash-4.2.0-pyhd8ed1ab_0.tar.bz2#e5a77472ae964f2835fce16355bbfe64 +https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.50.5-hc3c00ef_0.tar.bz2#1362366116e80bcfbe9c7cd99766ea40 +https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.3.4-py37h0c9df89_0.tar.bz2#55cf4f52dea2415649f0dd15d4b8970c +https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.5.6-nompi_py37h946d57d_103.tar.bz2#600a3ba1f0b13ca31d4f593f1fa490b4 https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.6.0-pyhd8ed1ab_0.tar.bz2#0941325bf48969e2b3b19d0951740950 +https://conda.anaconda.org/conda-forge/noarch/pip-21.1.1-pyhd8ed1ab_0.tar.bz2#e0bad1330c3575222d221e7679a36a0a https://conda.anaconda.org/conda-forge/linux-64/pluggy-0.13.1-py37h89c1867_4.tar.bz2#fae2ad595398817ce166a1af18dfe295 +https://conda.anaconda.org/conda-forge/noarch/pygments-2.9.0-pyhd8ed1ab_0.tar.bz2#a2d9bba43c9b80a42b0ccb9afd7223c2 +https://conda.anaconda.org/conda-forge/noarch/pyopenssl-20.0.1-pyhd8ed1ab_0.tar.bz2#92371c25994d0f5d28a01c1fb75ebf86 +https://conda.anaconda.org/conda-forge/linux-64/pyqt-impl-5.12.3-py37he336c9b_7.tar.bz2#303251d6f2b9e60a0cd79480cf8507d2 https://conda.anaconda.org/conda-forge/linux-64/cartopy-0.19.0.post1-py37h0c48da3_0.tar.bz2#44add1e9550c8caf69ce64561ce73035 +https://conda.anaconda.org/conda-forge/noarch/dask-2021.5.0-pyhd8ed1ab_0.tar.bz2#dd5bcdcdc6c1f94f2a62c15fc6da65a3 https://conda.anaconda.org/conda-forge/linux-64/esmpy-8.1.1-mpi_mpich_py37hf719a8e_100.tar.bz2#d608536dd44b60da923950c60619583d https://conda.anaconda.org/conda-forge/noarch/flake8-docstrings-1.6.0-pyhd8ed1ab_0.tar.bz2#dea85c9bee98ef38c90fa7348f7fa333 https://conda.anaconda.org/conda-forge/noarch/flake8-import-order-0.18.1-py_0.tar.bz2#b3fca0d4b267be46b6d38fa77bf36f9a +https://conda.anaconda.org/conda-forge/linux-64/graphviz-2.47.1-h85b4f2f_1.tar.bz2#4b7c5657eac8ddfa0cae06f6f9a2a275 +https://conda.anaconda.org/conda-forge/noarch/nc-time-axis-1.2.0-py_1.tar.bz2#f3158a5d335f0f44f09cf05d3fb4107e https://conda.anaconda.org/conda-forge/linux-64/pre-commit-2.12.1-py37h89c1867_0.tar.bz2#6655c6fc0713a2ec76aa925a8aaf1549 +https://conda.anaconda.org/conda-forge/linux-64/pyqtchart-5.12-py37he336c9b_7.tar.bz2#2b1959f3a87b5ad66690340ef921323c +https://conda.anaconda.org/conda-forge/linux-64/pyqtwebengine-5.12.1-py37he336c9b_7.tar.bz2#15f5cbcafb4889bb41da2a0a0e338f2a https://conda.anaconda.org/conda-forge/linux-64/pytest-6.2.4-py37h89c1867_0.tar.bz2#0298517e14b0118206b0fa38b1f9883d +https://conda.anaconda.org/conda-forge/noarch/urllib3-1.26.4-pyhd8ed1ab_0.tar.bz2#d7b20b328e23d993994ea02077c009c0 +https://conda.anaconda.org/conda-forge/linux-64/pyqt-5.12.3-py37h89c1867_7.tar.bz2#1754ec587a9ac26e9507fea7eb6bebc2 +https://conda.anaconda.org/conda-forge/noarch/requests-2.25.1-pyhd3deb0d_0.tar.bz2#ae687aba31a1c400192a86a2e993ffdc +https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.3.4-py37h89c1867_0.tar.bz2#aa928e9c4e7ef59482775bbbf78e8bf5 +https://conda.anaconda.org/conda-forge/noarch/sphinx-3.5.4-pyh44b312d_0.tar.bz2#0ebc444f001f73c4f6de01057b0be392 +https://conda.anaconda.org/conda-forge/noarch/sphinx-copybutton-0.3.1-pyhd8ed1ab_0.tar.bz2#decad13214a2a545944560eccf4a9815 +https://conda.anaconda.org/conda-forge/noarch/sphinx-gallery-0.9.0-pyhd8ed1ab_0.tar.bz2#5ef222a3e1b5904742e376e05046692b +https://conda.anaconda.org/conda-forge/noarch/sphinx-panels-0.5.2-pyhd3deb0d_0.tar.bz2#1a871a63c4be1bd47a7aa48b7417a426 +https://conda.anaconda.org/conda-forge/noarch/sphinx_rtd_theme-0.5.2-pyhd8ed1ab_1.tar.bz2#7434e891fc767cb0d39d90751720c8ec diff --git a/requirements/nox.lock/py38-linux-64.lock b/requirements/nox.lock/py38-linux-64.lock index 1198a2d3..6e33d086 100644 --- a/requirements/nox.lock/py38-linux-64.lock +++ b/requirements/nox.lock/py38-linux-64.lock @@ -1,61 +1,129 @@ # platform: linux-64 -# env_hash: 3dbf032da5f191d2eed96486e3bd2e1681f8ba54605c2c7a3c6adeb71940c48a +# env_hash: dc1bd2ccaf71fc80c7236cc2698f4603d58aaac51fa1d39deb4bb9db9369fa76 @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-2020.12.5-ha878542_0.tar.bz2#7eb5d4ffeee663caa1635cd67071bc1b +https://conda.anaconda.org/conda-forge/noarch/font-ttf-dejavu-sans-mono-2.37-hab24e00_0.tar.bz2#0c96522c6bdaed4b1566d11387caaf45 +https://conda.anaconda.org/conda-forge/noarch/font-ttf-inconsolata-3.000-h77eed37_0.tar.bz2#34893075a5c9e55cdafac56607368fc6 +https://conda.anaconda.org/conda-forge/noarch/font-ttf-source-code-pro-2.038-h77eed37_0.tar.bz2#4d59c254e01d9cde7957100457e2d5fb +https://conda.anaconda.org/conda-forge/noarch/font-ttf-ubuntu-0.83-hab24e00_0.tar.bz2#19410c3df09dfb12d1206132a1d357c5 https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.35.1-hea4e1c9_2.tar.bz2#83610dba766a186bdc7a116053b782a4 https://conda.anaconda.org/conda-forge/linux-64/libgfortran5-9.3.0-hff62375_19.tar.bz2#c2d8da3cb171e4aa642d20c6e4e42a04 https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-ng-9.3.0-h6de172a_19.tar.bz2#cd9a24a8dde03ec0cf0e603b0bea85a1 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.23-ha770c72_2.tar.bz2#ce876d0c998e1e2eb1dc67b01937737f +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-9.3.0-hff62375_19.tar.bz2#aea379bd68fdcdf9499fa1453f852ac1 https://conda.anaconda.org/conda-forge/linux-64/libgomp-9.3.0-h2828fa1_19.tar.bz2#ab0a307912033126da02507b59e79ec9 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-9.3.0-h2828fa1_19.tar.bz2#9d5cdfc51476ee4dcdd96ed2dca3f943 +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.17.1-h7f98852_1.tar.bz2#ed1dc233ed5e3eaa9bfbaac64d130c5e +https://conda.anaconda.org/conda-forge/linux-64/expat-2.3.0-h9c3ff4c_0.tar.bz2#1fb8f0254eb78a2a0c120155e1b1a207 +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.9.1-h9c3ff4c_2.tar.bz2#b9a6d9422aed3ad84ec6ccee9bfcaa0f +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-68.1-h58526e2_0.tar.bz2#fc7a4271dc2a7f4fd78cd63695baf7c3 https://conda.anaconda.org/conda-forge/linux-64/jpeg-9d-h36c2ea0_0.tar.bz2#ea02ce6037dbe81803ae6123e5ba1568 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.3-h58526e2_2.tar.bz2#665369991d8dd290ac5ee92fce3e6bf5 -https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.15-pthreads_h8fe5266_0.tar.bz2#b4bdbbff92cacdd7b28b1b0a0366b24b +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/libogg-1.3.4-h7f98852_1.tar.bz2#6e8cc2173440d77708196c5b93771680 +https://conda.anaconda.org/conda-forge/linux-64/libopenblas-0.3.15-pthreads_h8fe5266_1.tar.bz2#bb5527a16584426a897f22643d9a36a6 +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-h58526e2_1007.tar.bz2#7f6569a0c2f27acb8fc90600b382e544 +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.0-h7f98852_2.tar.bz2#fb63a035a3b552c88a30d84b89ebf4c4 https://conda.anaconda.org/conda-forge/linux-64/lz4-c-1.9.3-h9c3ff4c_0.tar.bz2#4eb64ee0d5cd43096ffcf843c76b05d4 https://conda.anaconda.org/conda-forge/linux-64/mpich-3.4.1-h846660c_104.tar.bz2#94f01e56905a7af1479c9f72b00e9864 https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.2-h58526e2_4.tar.bz2#509f2a21c4a09214cd737a480dfd80c9 +https://conda.anaconda.org/conda-forge/linux-64/nspr-4.30-h9c3ff4c_0.tar.bz2#e6dc1f8f6e0bcebe8e3d8a5bca258dbe https://conda.anaconda.org/conda-forge/linux-64/openssl-1.1.1k-h7f98852_0.tar.bz2#07fae2cb088379c8441e0f3ffa1f4025 +https://conda.anaconda.org/conda-forge/linux-64/pcre-8.44-he1b5a44_0.tar.bz2#e647d89cd5cdf62760cf283a001841ff +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/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-h516909a_0.tar.bz2#03a530e925414902547cf48da7756db8 https://conda.anaconda.org/conda-forge/linux-64/zlib-1.2.11-h516909a_1010.tar.bz2#339cc5584e6d26bc73a875ba900028c3 -https://conda.anaconda.org/conda-forge/linux-64/hdf4-4.2.15-h10796ff_2.tar.bz2#c6f500917ce0d20043a7e3de53b0e5bd +https://conda.anaconda.org/conda-forge/linux-64/gettext-0.19.8.1-h0b5b191_1005.tar.bz2#ff6f69b593a9e74c0e6b61908ac513fa +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/libblas-3.9.0-9_openblas.tar.bz2#5f08755e98b2a43ca68124e629a5a0cb 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-hcdb4288_3.tar.bz2#d8f51405997093ff1799ded7650439c4 +https://conda.anaconda.org/conda-forge/linux-64/libllvm11-11.1.0-hf817b99_2.tar.bz2#646fa2f7c60b69ee8f918668e9c2fd31 https://conda.anaconda.org/conda-forge/linux-64/libnghttp2-1.43.0-h812cca2_0.tar.bz2#1867d1e9658596b3fac8847a7702eef4 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.9.0-ha56f1ee_6.tar.bz2#f0dfb86444df325e599dbc3f4c0a3f5b +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_1003.tar.bz2#a9371e9e40aded194dcba1447606c9a1 +https://conda.anaconda.org/conda-forge/linux-64/libxml2-2.9.12-h72842e0_0.tar.bz2#bd14fdf5b9ee5568056a40a6a2f41866 https://conda.anaconda.org/conda-forge/linux-64/libzip-1.7.3-h4de3113_0.tar.bz2#2568763f88009f95e9262cba837dbb82 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.10-h21135ba_1.tar.bz2#c647f70aa7e3d4cc4e029cc1c9a99953 -https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.0-ha95c52a_0.tar.bz2#b56f94865e2de36abf054e7bfa499034 +https://conda.anaconda.org/conda-forge/linux-64/udunits2-2.2.27.27-h975c496_1.tar.bz2#e663bd5dbc8cc4c1647d9f51cf25872c +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/zstd-1.4.9-ha95c52a_0.tar.bz2#b481dc9fda3af2a681d08a4d5cd1ea0b 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/krb5-1.19.1-hcc1bbae_0.tar.bz2#59b0695a515a6c54d45463dbf208ae38 https://conda.anaconda.org/conda-forge/linux-64/libcblas-3.9.0-9_openblas.tar.bz2#edee85b4f83376ceae81e0975b8bffa2 +https://conda.anaconda.org/conda-forge/linux-64/libclang-11.1.0-default_ha53f305_1.tar.bz2#b9b71585ca4fcb5d442c5a9df5dd7e98 +https://conda.anaconda.org/conda-forge/linux-64/libglib-2.68.2-h3e27bee_0.tar.bz2#a48401ff2ecb708b8b08bf3547eff205 https://conda.anaconda.org/conda-forge/linux-64/liblapack-3.9.0-9_openblas.tar.bz2#572d84ab07962986f6dd8e4637a475ca -https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.2.0-hf544144_3.tar.bz2#53798351da909e181243e17dccee41ad +https://conda.anaconda.org/conda-forge/linux-64/libtiff-4.2.0-hbd63e13_2.tar.bz2#e3f034b29a122699b06da40c155f1a70 +https://conda.anaconda.org/conda-forge/linux-64/libxkbcommon-1.0.3-he3ba5ed_0.tar.bz2#f9dbabc7e01c459ed7a1d1d64b206e9b +https://conda.anaconda.org/conda-forge/linux-64/mysql-libs-8.0.23-h935591d_2.tar.bz2#b36368d163fca85e110529ddc4c67985 https://conda.anaconda.org/conda-forge/linux-64/sqlite-3.35.5-h74cdb3f_0.tar.bz2#e876c82c21e7074d299e13762d02466c +https://conda.anaconda.org/conda-forge/linux-64/xorg-libx11-1.7.1-h7f98852_0.tar.bz2#1759774cc5f1e965178c9cbab0b29a13 +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/fontconfig-2.13.1-hba837de_1005.tar.bz2#fd3611672eb91bc9d24fd6fb970037eb +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/glib-tools-2.68.2-h9c3ff4c_0.tar.bz2#3afedfe4c8f500ed953dcc488730908a +https://conda.anaconda.org/conda-forge/linux-64/gstreamer-1.18.4-h76c114f_2.tar.bz2#5db765d4974fa89f64c1544eb2a552cb +https://conda.anaconda.org/conda-forge/linux-64/gts-0.7.6-h64030ff_2.tar.bz2#112eb9b5b93f0c02e59aea4fd1967363 https://conda.anaconda.org/conda-forge/linux-64/lcms2-2.12-hddcbb42_0.tar.bz2#797117394a4aa588de6d741b06fad80f https://conda.anaconda.org/conda-forge/linux-64/libcurl-7.76.1-h2574ce0_2.tar.bz2#cc7097ea31a80337c2203c22f08d3f03 -https://conda.anaconda.org/conda-forge/linux-64/openjpeg-2.4.0-hb52868f_1.tar.bz2#b7ad78ad2e9ee155f59e6428406ee824 +https://conda.anaconda.org/conda-forge/linux-64/libpq-13.3-hd57d9b9_0.tar.bz2#66ef2cacc483205b7d303f7b02601c3b +https://conda.anaconda.org/conda-forge/linux-64/libwebp-1.2.0-h3452ae3_0.tar.bz2#8f4e19a8988c38feec7db41bcd0bf0d0 +https://conda.anaconda.org/conda-forge/linux-64/nss-3.65-hb5efdd6_0.tar.bz2#645afd59bb8a540be3545850f586e028 https://conda.anaconda.org/conda-forge/linux-64/python-3.8.10-h49503c6_1_cpython.tar.bz2#69f7d6ef1f00c3a109b1b06279e6d6a9 +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/appdirs-1.4.4-pyh9f0ad1d_0.tar.bz2#5f095bc6454094e96f146491fd03633b https://conda.anaconda.org/conda-forge/noarch/attrs-21.2.0-pyhd8ed1ab_0.tar.bz2#d2e1c7f388ac403df7079b411c37cc50 +https://conda.anaconda.org/conda-forge/linux-64/cairo-1.16.0-h6cf1ce9_1008.tar.bz2#a43fb47d15e116f8be4be7e6b17ab59f +https://conda.anaconda.org/conda-forge/noarch/cloudpickle-1.6.0-py_0.tar.bz2#76d764d8881719e305f6fa368dc2b65e +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.76.1-hea6ffbf_2.tar.bz2#0312ab411fff1aca55e45ea3f885a8ab https://conda.anaconda.org/conda-forge/noarch/dataclasses-0.8-pyhc8e2a94_1.tar.bz2#28e0de0ecba81334619a777fdc00febc https://conda.anaconda.org/conda-forge/noarch/distlib-0.3.1-pyh9f0ad1d_0.tar.bz2#db990401a267e2b15854af5f3f84f763 https://conda.anaconda.org/conda-forge/noarch/filelock-3.0.12-pyh9f0ad1d_0.tar.bz2#7544ed05bbbe9bb687bc9bcbe4d6cb46 +https://conda.anaconda.org/conda-forge/noarch/fsspec-2021.5.0-pyhd8ed1ab_0.tar.bz2#da823f51ebb876dafbed1c892fd80956 +https://conda.anaconda.org/conda-forge/linux-64/glib-2.68.2-h9c3ff4c_0.tar.bz2#a418792ba3a452bff1ab2ed4df628f80 +https://conda.anaconda.org/conda-forge/linux-64/gst-plugins-base-1.18.4-hf529b03_2.tar.bz2#526fadaa13ec264cb919436953bc2766 https://conda.anaconda.org/conda-forge/linux-64/hdf5-1.10.6-mpi_mpich_h996c276_1014.tar.bz2#6af2e2e4dfb0ef36c35042cd69a1599d +https://conda.anaconda.org/conda-forge/noarch/heapdict-1.0.1-py_0.tar.bz2#77242bfb1e74a627fb06319b5a2d3b95 +https://conda.anaconda.org/conda-forge/noarch/idna-2.10-pyh9f0ad1d_0.tar.bz2#f95a12b4f435aae6680fe55ae2eb1b06 +https://conda.anaconda.org/conda-forge/noarch/imagesize-1.2.0-py_0.tar.bz2#5879bd2c4b399a5072468e5fe587bf1b https://conda.anaconda.org/conda-forge/noarch/iniconfig-1.1.1-pyh9f0ad1d_0.tar.bz2#39161f81cc5e5ca45b8226fbb06c6905 +https://conda.anaconda.org/conda-forge/noarch/iris-sample-data-2.3.0-pyh9f0ad1d_0.tar.bz2#e4a33192da1a6dc4967ba18c6c765945 +https://conda.anaconda.org/conda-forge/linux-64/libgd-2.3.2-h78a0170_0.tar.bz2#ac0c23e6f3bbb61569781f00b5666f97 +https://conda.anaconda.org/conda-forge/noarch/locket-0.2.0-py_2.tar.bz2#709e8671651c7ec3d1ad07800339ff1d https://conda.anaconda.org/conda-forge/noarch/mccabe-0.6.1-py_1.tar.bz2#a326cb400c1ccd91789f3e7d02124d61 https://conda.anaconda.org/conda-forge/noarch/more-itertools-8.7.0-pyhd8ed1ab_1.tar.bz2#d51aa0bff3e4da989b2ab3a7f3e52dfc +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/pathspec-0.8.1-pyhd3deb0d_0.tar.bz2#fcd2fbb062b55d14a77e664c89ee17a6 https://conda.anaconda.org/conda-forge/linux-64/proj-7.2.0-h277dcde_2.tar.bz2#db654ee11298d3463bad67445707654c @@ -63,49 +131,115 @@ https://conda.anaconda.org/conda-forge/noarch/py-1.10.0-pyhd3deb0d_0.tar.bz2#88c https://conda.anaconda.org/conda-forge/noarch/pycodestyle-2.7.0-pyhd8ed1ab_0.tar.bz2#0234673eb2ecfbdf4e54574ab4d95f81 https://conda.anaconda.org/conda-forge/noarch/pycparser-2.20-pyh9f0ad1d_2.tar.bz2#aa798d50ffd182a0f6f31478c7f434f6 https://conda.anaconda.org/conda-forge/noarch/pyflakes-2.3.1-pyhd8ed1ab_0.tar.bz2#01e9ada82bd261ee2b6366aa832018cc +https://conda.anaconda.org/conda-forge/noarch/pyke-1.1.1-pyhd8ed1ab_1004.tar.bz2#5f0236abfbb6d53826d1afed1e64f82e https://conda.anaconda.org/conda-forge/noarch/pyparsing-2.4.7-pyh9f0ad1d_0.tar.bz2#626c4f20d5bf06dcec9cf2eaa31725c7 https://conda.anaconda.org/conda-forge/noarch/pyshp-2.1.3-pyh44b312d_0.tar.bz2#2d1867b980785eb44b8122184d8b42a6 https://conda.anaconda.org/conda-forge/linux-64/python_abi-3.8-1_cp38.tar.bz2#8d05152d6fb3012b27a0e6fbcc14bea1 +https://conda.anaconda.org/conda-forge/noarch/pytz-2021.1-pyhd8ed1ab_0.tar.bz2#3af2e9424d5eb0063824a3f9b850d411 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.1.0-pyhd8ed1ab_0.tar.bz2#f1d64c0cf0eedf655a96ccdc1573c05a +https://conda.anaconda.org/conda-forge/noarch/sortedcontainers-2.4.0-pyhd8ed1ab_0.tar.bz2#6d6552722448103793743dabfbda532d +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-1.0.3-py_0.tar.bz2#4508a40465ebf0105e52f7194f299411 +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/sphinxcontrib-serializinghtml-1.1.4-py_0.tar.bz2#8ea6a8036e28dba8827d35c764709358 +https://conda.anaconda.org/conda-forge/noarch/tblib-1.7.0-pyhd8ed1ab_0.tar.bz2#3d4afc31302aa7be471feb6be048ed76 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.1-py_0.tar.bz2#d1e66b58cb00b3817ad9f05eec098c00 https://conda.anaconda.org/conda-forge/noarch/typing_extensions-3.7.4.3-py_0.tar.bz2#12b96e382730541a4b332420227055ae +https://conda.anaconda.org/conda-forge/noarch/wheel-0.36.2-pyhd3deb0d_0.tar.bz2#768bfbe026426d0e76b377997d1f2b98 https://conda.anaconda.org/conda-forge/noarch/zipp-3.4.1-pyhd8ed1ab_0.tar.bz2#a4fa30eb74a326092b3d8078b1f1aae1 +https://conda.anaconda.org/conda-forge/linux-64/antlr-python-runtime-4.7.2-py38h578d9bd_1002.tar.bz2#2b2207e2c8a05fc0bc5b62fc32c355e6 +https://conda.anaconda.org/conda-forge/noarch/babel-2.9.1-pyh44b312d_0.tar.bz2#74136ed39bfea0832d338df1e58d013e https://conda.anaconda.org/conda-forge/linux-64/certifi-2020.12.5-py38h578d9bd_1.tar.bz2#be470d89b0678991fd4ba67d4a35bc80 https://conda.anaconda.org/conda-forge/linux-64/cffi-1.14.5-py38ha65f79e_0.tar.bz2#386057f231a571b75bfa7307c9acd5f6 https://conda.anaconda.org/conda-forge/noarch/cfgv-3.2.0-py_0.tar.bz2#4972efcb3e2cbd3954b24a17266be25c -https://conda.anaconda.org/conda-forge/linux-64/click-8.0.0-py38h578d9bd_0.tar.bz2#f3965c54a43c12c28f8bdaf05ad1d730 +https://conda.anaconda.org/conda-forge/linux-64/chardet-4.0.0-py38h578d9bd_1.tar.bz2#9294a5e2c7545a2f67ac348aadd53344 +https://conda.anaconda.org/conda-forge/linux-64/click-8.0.1-py38h578d9bd_0.tar.bz2#45426acde32f0ddd94dcee3478fd13e3 https://conda.anaconda.org/conda-forge/noarch/cycler-0.10.0-py_2.tar.bz2#f6d7c7e6d8f42cbbec7e07a8d879f91c +https://conda.anaconda.org/conda-forge/linux-64/cytoolz-0.11.0-py38h497a2fe_3.tar.bz2#45568bae22c3825f22b631101ecbad35 +https://conda.anaconda.org/conda-forge/linux-64/dbus-1.13.6-h48d8840_2.tar.bz2#eba672c69baf366fdedd1c6f702dbb81 +https://conda.anaconda.org/conda-forge/linux-64/docutils-0.16-py38h578d9bd_3.tar.bz2#a7866449fb9e5e4008a02df276549d34 +https://conda.anaconda.org/conda-forge/linux-64/harfbuzz-2.8.1-h83ec7ef_0.tar.bz2#654935b08e8bd4a8cbf6a4253e290c04 https://conda.anaconda.org/conda-forge/linux-64/importlib-metadata-4.0.1-py38h578d9bd_0.tar.bz2#df6fae32b464e9fb5010c4784ce2efb0 https://conda.anaconda.org/conda-forge/linux-64/kiwisolver-1.3.1-py38h1fd1430_1.tar.bz2#01488c80daae318ed5c17e7bb12af64e https://conda.anaconda.org/conda-forge/linux-64/libnetcdf-4.8.0-mpi_mpich_hf07302c_2.tar.bz2#d76a3f327eb8e26b5ce6b042ac1abeb3 +https://conda.anaconda.org/conda-forge/linux-64/markupsafe-2.0.1-py38h497a2fe_0.tar.bz2#d075babffd68330d81b0488a45435698 https://conda.anaconda.org/conda-forge/linux-64/mpi4py-3.0.3-py38he865349_7.tar.bz2#afbbb1e0ce578e537b2ec82563988417 +https://conda.anaconda.org/conda-forge/linux-64/msgpack-python-1.0.2-py38h1fd1430_1.tar.bz2#5854c568e0d341313fb0a6487f1c687e https://conda.anaconda.org/conda-forge/linux-64/mypy_extensions-0.4.3-py38h578d9bd_3.tar.bz2#2821575c3884edf40e02cf1966e1c504 -https://conda.anaconda.org/conda-forge/linux-64/numpy-1.20.2-py38h9894fe3_0.tar.bz2#d159afa37685037c73461e8f2fa22e50 +https://conda.anaconda.org/conda-forge/linux-64/numpy-1.20.3-py38h9894fe3_0.tar.bz2#6478b300fcbbaea253b4c94422583e8d https://conda.anaconda.org/conda-forge/noarch/packaging-20.9-pyh44b312d_0.tar.bz2#be69a38e912054a62dc82cc3c7711a64 -https://conda.anaconda.org/conda-forge/linux-64/pillow-8.2.0-py38ha0e1e83_1.tar.bz2#46a555b3ce021469f7f62d72fe6ebb4b +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.2-py38h9776b28_0.tar.bz2#bd527d652ba06fb2aae61640bcf7c435 https://conda.anaconda.org/conda-forge/linux-64/pluggy-0.13.1-py38h578d9bd_4.tar.bz2#aa7cecc2200bf4109dbd7676b9213e30 -https://conda.anaconda.org/conda-forge/noarch/pydocstyle-6.1.0-pyhd8ed1ab_0.tar.bz2#7d17e3146e16c3313d937e031a14ca1f +https://conda.anaconda.org/conda-forge/noarch/pockets-0.9.1-py_0.tar.bz2#1b52f0c42e8077e5a33e00fe72269364 +https://conda.anaconda.org/conda-forge/linux-64/psutil-5.8.0-py38h497a2fe_1.tar.bz2#3c465545aa3cec37f8f1341546677956 +https://conda.anaconda.org/conda-forge/noarch/pydocstyle-6.1.1-pyhd8ed1ab_0.tar.bz2#e417954a987d382b3142886726ab3aad +https://conda.anaconda.org/conda-forge/linux-64/pyqt5-sip-4.19.18-py38h709712a_7.tar.bz2#e012838bbbe92f6a458c2584634830f1 +https://conda.anaconda.org/conda-forge/linux-64/pysocks-1.7.1-py38h578d9bd_3.tar.bz2#8284bab4783fd6fdd11b695958945614 https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.8.1-py_0.tar.bz2#0d0150ed9c2d25817f5324108d3f7571 +https://conda.anaconda.org/conda-forge/linux-64/python-xxhash-2.0.2-py38h497a2fe_0.tar.bz2#27b37e3f79205080b573442445ed727b https://conda.anaconda.org/conda-forge/linux-64/pyyaml-5.4.1-py38h497a2fe_0.tar.bz2#36d6e06148013694eb943576cd305f67 https://conda.anaconda.org/conda-forge/linux-64/regex-2021.4.4-py38h497a2fe_0.tar.bz2#fd8c69b8da2edc1dbdf6ff619257ee00 https://conda.anaconda.org/conda-forge/linux-64/tornado-6.1-py38h497a2fe_1.tar.bz2#e772c8383768280af283e814e2126663 https://conda.anaconda.org/conda-forge/linux-64/typed-ast-1.4.3-py38h497a2fe_0.tar.bz2#f4a5eeaccf5c5bab10491071ea2e32b1 https://conda.anaconda.org/conda-forge/linux-64/virtualenv-20.4.6-py38h578d9bd_0.tar.bz2#021bbd21b02800d2ff9c7a05b3e77631 +https://conda.anaconda.org/conda-forge/noarch/zict-2.0.0-py_0.tar.bz2#4750152be22f24d695b3004c5e1712d3 https://conda.anaconda.org/conda-forge/noarch/black-20.8b1-py_1.tar.bz2#e555d6b71ec916c3dc4e6e3793cc9796 +https://conda.anaconda.org/conda-forge/linux-64/brotlipy-0.7.0-py38h497a2fe_1001.tar.bz2#56753dd777a6517b34966ddcb39af734 +https://conda.anaconda.org/conda-forge/linux-64/cftime-1.2.1-py38hab2c0dc_1.tar.bz2#777186ded2d850f3eab4ce7131c6c17c +https://conda.anaconda.org/conda-forge/linux-64/cryptography-3.4.7-py38ha5dfef3_0.tar.bz2#a8b014aba670157256dabdc885f71af4 +https://conda.anaconda.org/conda-forge/noarch/dask-core-2021.5.0-pyhd8ed1ab_0.tar.bz2#5a3c5c4242795ce5d69cfb5fe9613362 https://conda.anaconda.org/conda-forge/linux-64/editdistance-s-1.0.0-py38h1fd1430_1.tar.bz2#03bbd69539712a691b0a43bd4a49976e +https://conda.anaconda.org/conda-forge/noarch/jinja2-3.0.1-pyhd8ed1ab_0.tar.bz2#c647e77921fd3e245cdcc5b2d451a0f8 +https://conda.anaconda.org/conda-forge/linux-64/mo_pack-0.2.0-py38h5c078b8_1005.tar.bz2#d318a411c4cb595d5adb60ec7b4a46f0 https://conda.anaconda.org/conda-forge/linux-64/netcdf-fortran-4.5.3-mpi_mpich_h196b126_4.tar.bz2#e058f42a78ea8c965cf7335e28143c59 +https://conda.anaconda.org/conda-forge/linux-64/pandas-1.2.4-py38h1abd341_0.tar.bz2#91150ede50b13d34a03e9ef51b7b379f +https://conda.anaconda.org/conda-forge/linux-64/pango-1.48.5-hb8ff022_0.tar.bz2#f4e263c4dfa15b6a97349782793d1ee7 https://conda.anaconda.org/conda-forge/linux-64/pytest-6.2.4-py38h578d9bd_0.tar.bz2#6473929e82e54da032bcca9d965e4e58 +https://conda.anaconda.org/conda-forge/linux-64/python-stratify-0.2.post0-py38hb5d20a5_0.tar.bz2#cc6852249c01884469560082943b689f +https://conda.anaconda.org/conda-forge/linux-64/pywavelets-1.1.1-py38h5c078b8_3.tar.bz2#dafeef887e68bd18ec84681747ca0fd5 +https://conda.anaconda.org/conda-forge/linux-64/qt-5.12.9-hda022c4_4.tar.bz2#afebab1f5049d66baaaec67d9ce893f0 https://conda.anaconda.org/conda-forge/linux-64/scipy-1.6.3-py38h7b17777_0.tar.bz2#8055079ed82e1ada1cc4714c26d04802 https://conda.anaconda.org/conda-forge/linux-64/setuptools-49.6.0-py38h578d9bd_3.tar.bz2#59c561cd1be0db9cf1c83f7d7cc74f4d -https://conda.anaconda.org/conda-forge/linux-64/shapely-1.7.1-py38h4fc1155_4.tar.bz2#4f81f5126a2ff5bff8ee84574d82ecac +https://conda.anaconda.org/conda-forge/linux-64/shapely-1.7.1-py38haeee4fe_4.tar.bz2#b94adbb3ce5a62a6894d7757005e6ad8 +https://conda.anaconda.org/conda-forge/noarch/sphinxcontrib-napoleon-0.7-py_0.tar.bz2#0bc25ff6f2e34af63ded59692df5f749 +https://conda.anaconda.org/conda-forge/linux-64/asv-0.4.2-py38h709712a_2.tar.bz2#4659f315fc42e671606fbcd1b9234f75 +https://conda.anaconda.org/conda-forge/linux-64/bokeh-2.1.1-py38h32f6830_0.tar.bz2#896f192315a5a04c878febacd67e64cd +https://conda.anaconda.org/conda-forge/linux-64/cf-units-2.1.5-py38hb5d20a5_0.tar.bz2#b45764dbfe02c18f80458bb241e37955 +https://conda.anaconda.org/conda-forge/linux-64/distributed-2021.5.0-py38h578d9bd_0.tar.bz2#4f32bf3fe6ff875d0afbaab19f3da3e9 https://conda.anaconda.org/conda-forge/linux-64/esmf-8.1.1-mpi_mpich_h3dcaa78_100.tar.bz2#5b4bab1017226f2c03ba0fe02b783316 https://conda.anaconda.org/conda-forge/noarch/flake8-3.9.2-pyhd8ed1ab_0.tar.bz2#37d685abea0a25c921431edda02ad143 +https://conda.anaconda.org/conda-forge/linux-64/gtk2-2.24.33-h539f30e_1.tar.bz2#606777b4da3664d5c9415f5f165349fd https://conda.anaconda.org/conda-forge/noarch/identify-2.2.4-pyhd8ed1ab_0.tar.bz2#86a6446475a53451a71e1515150cb532 -https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.4.2-py38hcc49a3a_0.tar.bz2#4bfb6818a1fce6d4129fdf121f788505 +https://conda.anaconda.org/conda-forge/noarch/imagehash-4.2.0-pyhd8ed1ab_0.tar.bz2#e5a77472ae964f2835fce16355bbfe64 +https://conda.anaconda.org/conda-forge/linux-64/librsvg-2.50.5-hc3c00ef_0.tar.bz2#1362366116e80bcfbe9c7cd99766ea40 +https://conda.anaconda.org/conda-forge/linux-64/matplotlib-base-3.3.4-py38h0efea84_0.tar.bz2#9818b095ff2ddceadb7553b0d56d219f +https://conda.anaconda.org/conda-forge/linux-64/netcdf4-1.5.6-nompi_py38h5e9db54_103.tar.bz2#72a5656daeee23c80e22b936bef0ceb3 https://conda.anaconda.org/conda-forge/noarch/nodeenv-1.6.0-pyhd8ed1ab_0.tar.bz2#0941325bf48969e2b3b19d0951740950 +https://conda.anaconda.org/conda-forge/noarch/pip-21.1.1-pyhd8ed1ab_0.tar.bz2#e0bad1330c3575222d221e7679a36a0a +https://conda.anaconda.org/conda-forge/noarch/pygments-2.9.0-pyhd8ed1ab_0.tar.bz2#a2d9bba43c9b80a42b0ccb9afd7223c2 +https://conda.anaconda.org/conda-forge/noarch/pyopenssl-20.0.1-pyhd8ed1ab_0.tar.bz2#92371c25994d0f5d28a01c1fb75ebf86 +https://conda.anaconda.org/conda-forge/linux-64/pyqt-impl-5.12.3-py38h7400c14_7.tar.bz2#8fe28c949b01e3d69c2b357b5abf3916 https://conda.anaconda.org/conda-forge/linux-64/cartopy-0.19.0.post1-py38hc9c980b_0.tar.bz2#65e97172e139d3465895eb07a1fd52f2 +https://conda.anaconda.org/conda-forge/noarch/dask-2021.5.0-pyhd8ed1ab_0.tar.bz2#dd5bcdcdc6c1f94f2a62c15fc6da65a3 https://conda.anaconda.org/conda-forge/linux-64/esmpy-8.1.1-mpi_mpich_py38h7f78e9f_100.tar.bz2#ce0ac0d6f5e6c5e7e0c613b08b3a0960 https://conda.anaconda.org/conda-forge/noarch/flake8-docstrings-1.6.0-pyhd8ed1ab_0.tar.bz2#dea85c9bee98ef38c90fa7348f7fa333 https://conda.anaconda.org/conda-forge/noarch/flake8-import-order-0.18.1-py_0.tar.bz2#b3fca0d4b267be46b6d38fa77bf36f9a +https://conda.anaconda.org/conda-forge/linux-64/graphviz-2.47.1-h85b4f2f_1.tar.bz2#4b7c5657eac8ddfa0cae06f6f9a2a275 +https://conda.anaconda.org/conda-forge/noarch/nc-time-axis-1.2.0-py_1.tar.bz2#f3158a5d335f0f44f09cf05d3fb4107e https://conda.anaconda.org/conda-forge/linux-64/pre-commit-2.12.1-py38h578d9bd_0.tar.bz2#c26dbf85b3b8699f6a551e1f63bf9580 +https://conda.anaconda.org/conda-forge/linux-64/pyqtchart-5.12-py38h7400c14_7.tar.bz2#3003444b4f41742a33b7afdeb3260cbc +https://conda.anaconda.org/conda-forge/linux-64/pyqtwebengine-5.12.1-py38h7400c14_7.tar.bz2#1c17944e118b314ff4d0bfc05f03a5e1 +https://conda.anaconda.org/conda-forge/noarch/urllib3-1.26.4-pyhd8ed1ab_0.tar.bz2#d7b20b328e23d993994ea02077c009c0 +https://conda.anaconda.org/conda-forge/linux-64/pyqt-5.12.3-py38h578d9bd_7.tar.bz2#7166890c160d0441f59973a40b74f6e5 +https://conda.anaconda.org/conda-forge/noarch/requests-2.25.1-pyhd3deb0d_0.tar.bz2#ae687aba31a1c400192a86a2e993ffdc +https://conda.anaconda.org/conda-forge/linux-64/matplotlib-3.3.4-py38h578d9bd_0.tar.bz2#2ad11624aec829f58f86a231bbdf3990 +https://conda.anaconda.org/conda-forge/noarch/sphinx-3.5.4-pyh44b312d_0.tar.bz2#0ebc444f001f73c4f6de01057b0be392 +https://conda.anaconda.org/conda-forge/noarch/sphinx-copybutton-0.3.1-pyhd8ed1ab_0.tar.bz2#decad13214a2a545944560eccf4a9815 +https://conda.anaconda.org/conda-forge/noarch/sphinx-gallery-0.9.0-pyhd8ed1ab_0.tar.bz2#5ef222a3e1b5904742e376e05046692b +https://conda.anaconda.org/conda-forge/noarch/sphinx-panels-0.5.2-pyhd3deb0d_0.tar.bz2#1a871a63c4be1bd47a7aa48b7417a426 +https://conda.anaconda.org/conda-forge/noarch/sphinx_rtd_theme-0.5.2-pyhd8ed1ab_1.tar.bz2#7434e891fc767cb0d39d90751720c8ec From cec03fb2e268539dd7907ff6a3482dc1972d78d8 Mon Sep 17 00:00:00 2001 From: stephenworsley <49274989+stephenworsley@users.noreply.github.com> Date: Fri, 18 Jun 2021 14:59:00 +0100 Subject: [PATCH 10/11] Unstructured scheme integration test (#66) * add support for iris test data to CI * fix CI * fix CI * fix CI * tidy CI * fill out test * blackify --- .cirrus.yml | 20 ++++++++ .../integration/experimental/__init__.py | 1 + .../unstructured_scheme/__init__.py | 1 + ...test_regrid_unstructured_to_rectilinear.py | 50 +++++++++++++++++++ 4 files changed, 72 insertions(+) create mode 100644 esmf_regrid/tests/integration/experimental/__init__.py create mode 100644 esmf_regrid/tests/integration/experimental/unstructured_scheme/__init__.py create mode 100644 esmf_regrid/tests/integration/experimental/unstructured_scheme/test_regrid_unstructured_to_rectilinear.py diff --git a/.cirrus.yml b/.cirrus.yml index 8a51e1a7..4470d972 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -32,6 +32,25 @@ env: CONDA_CACHE_PACKAGES: "nox pip pyyaml" # Use specific custom iris source feature branch. IRIS_SOURCE: "github:mesh-data-model" + # Git commit hash for iris test data. + IRIS_TEST_DATA_VERSION: "2.2" + # Base directory for the iris-test-data. + IRIS_TEST_DATA_DIR: ${HOME}/iris-test-data + OVERRIDE_TEST_DATA_REPOSITORY: ${IRIS_TEST_DATA_DIR}/test_data + + +# +# YAML alias for the iris-test-data cache. +# +iris_test_data_template: &IRIS_TEST_DATA_TEMPLATE + data_cache: + folder: ${IRIS_TEST_DATA_DIR} + fingerprint_script: + - echo "iris-test-data v${IRIS_TEST_DATA_VERSION}" + populate_script: + - wget --quiet https://github.com/SciTools/iris-test-data/archive/v${IRIS_TEST_DATA_VERSION}.zip -O iris-test-data.zip + - unzip -q iris-test-data.zip + - mv iris-test-data-${IRIS_TEST_DATA_VERSION} ${IRIS_TEST_DATA_DIR} # @@ -97,6 +116,7 @@ test_task: - echo "${CIRRUS_TASK_NAME}" - echo "${NOX_CACHE_BUILD}" - if [ -n "${IRIS_SOURCE}" ]; then echo "${IRIS_SOURCE}"; fi + << : *IRIS_TEST_DATA_TEMPLATE test_script: - export CONDA_OVERRIDE_LINUX="$(uname -r | cut -d'+' -f1)" - nox --session tests -- --verbose diff --git a/esmf_regrid/tests/integration/experimental/__init__.py b/esmf_regrid/tests/integration/experimental/__init__.py new file mode 100644 index 00000000..00ac6516 --- /dev/null +++ b/esmf_regrid/tests/integration/experimental/__init__.py @@ -0,0 +1 @@ +"""Integration tests for :mod:`esmf_regrid.experimental`.""" diff --git a/esmf_regrid/tests/integration/experimental/unstructured_scheme/__init__.py b/esmf_regrid/tests/integration/experimental/unstructured_scheme/__init__.py new file mode 100644 index 00000000..8819d721 --- /dev/null +++ b/esmf_regrid/tests/integration/experimental/unstructured_scheme/__init__.py @@ -0,0 +1 @@ +"""Integration tests for :mod:`esmf_regrid.experimental.unstructured_scheme`.""" diff --git a/esmf_regrid/tests/integration/experimental/unstructured_scheme/test_regrid_unstructured_to_rectilinear.py b/esmf_regrid/tests/integration/experimental/unstructured_scheme/test_regrid_unstructured_to_rectilinear.py new file mode 100644 index 00000000..d7aafe31 --- /dev/null +++ b/esmf_regrid/tests/integration/experimental/unstructured_scheme/test_regrid_unstructured_to_rectilinear.py @@ -0,0 +1,50 @@ +"""Integration tests for :func:`esmf_regrid.experimental.unstructured_scheme.regrid_unstructured_to_rectilinear`.""" + + +import os + +import iris +from iris.experimental.ugrid import PARSE_UGRID_ON_LOAD +import numpy as np + +from esmf_regrid.experimental.unstructured_scheme import ( + regrid_unstructured_to_rectilinear, +) + + +def test_real_data(): + """ + Test for :func:`esmf_regrid.experimental.unstructured_scheme.regrid_unstructured_to_rectilinear`. + + Tests with cubes derived from realistic data. + """ + # Load source cube. + test_data_dir = iris.config.TEST_DATA_DIR + src_fn = os.path.join( + test_data_dir, "NetCDF", "unstructured_grid", "lfric_surface_mean.nc" + ) + with PARSE_UGRID_ON_LOAD.context(): + src = iris.load_cube(src_fn, "rainfall_flux") + + # Load target grid cube. + tgt_fn = os.path.join( + test_data_dir, "NetCDF", "global", "xyt", "SMALL_hires_wind_u_for_ipcc4.nc" + ) + tgt = iris.load_cube(tgt_fn) + + # Perform regridding. + result = regrid_unstructured_to_rectilinear(src, tgt) + + # Check data. + assert result.shape == (1, 160, 320) + assert np.isclose(result.data.mean(), 2.93844e-5) + assert np.isclose(result.data.std(), 2.71724e-5) + + # Check metadata. + assert result.metadata == src.metadata + assert result.coord("time") == src.coord("time") + assert result.coord("latitude") == tgt.coord("latitude") + assert result.coord("longitude") == tgt.coord("longitude") + assert result.coord_dims("time") == (0,) + assert result.coord_dims("latitude") == (1,) + assert result.coord_dims("longitude") == (2,) From eecbc91b070c939d330bc40800b1975e5dc63c65 Mon Sep 17 00:00:00 2001 From: stephenworsley <49274989+stephenworsley@users.noreply.github.com> Date: Tue, 27 Jul 2021 16:38:58 +0100 Subject: [PATCH 11/11] Add grid to mesh scheme (#96) * add grid to mesh regridder * add tests * add tests * lint fixes * update comments * extend tests with extra dimensions * add multidimensional coords to tests * fix test * fix test * fix test * address some review comments * add error handling and test * lint fix * test mask handling * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix test * lint fix * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .../experimental/unstructured_scheme.py | 252 +++++++++++++++++- .../test_GridToMeshESMFRegridder.py | 198 ++++++++++++++ ...test_regrid_rectilinear_to_unstructured.py | 164 ++++++++++++ 3 files changed, 612 insertions(+), 2 deletions(-) create mode 100644 esmf_regrid/tests/unit/experimental/unstructured_scheme/test_GridToMeshESMFRegridder.py create mode 100644 esmf_regrid/tests/unit/experimental/unstructured_scheme/test_regrid_rectilinear_to_unstructured.py diff --git a/esmf_regrid/experimental/unstructured_scheme.py b/esmf_regrid/experimental/unstructured_scheme.py index ad3b2128..737227fd 100644 --- a/esmf_regrid/experimental/unstructured_scheme.py +++ b/esmf_regrid/experimental/unstructured_scheme.py @@ -86,7 +86,7 @@ def _create_cube(data, src_cube, mesh_dim, grid_x, grid_y): The regridded data as an N-dimensional NumPy array. src_cube : cube The source Cube. - mes_dim : int + mesh_dim : int The dimension of the mesh within the source Cube. grid_x : DimCoord The :class:`iris.coords.DimCoord` for the new grid's X @@ -187,7 +187,7 @@ def _regrid_unstructured_to_rectilinear__perform(src_cube, regrid_info, mdtol): """ Second (regrid) part of 'regrid_unstructured_to_rectilinear'. - Perform the prepared regrid calculation on a single 2d cube. + Perform the prepared regrid calculation on a single cube. """ mesh_dim, grid_x, grid_y, regridder = regrid_info @@ -324,3 +324,251 @@ def __call__(self, cube): return _regrid_unstructured_to_rectilinear__perform( cube, regrid_info, self.mdtol ) + + +def _regrid_along_grid_dims(regridder, data, grid_x_dim, grid_y_dim, mdtol): + # The mesh will be assigned to the first dimension associated with the + # grid, whether that is associated with the x or y coordinate. + tgt_mesh_dim = min(grid_x_dim, grid_y_dim) + data = np.moveaxis(data, [grid_x_dim, grid_y_dim], [-1, -2]) + result = regridder.regrid(data, mdtol=mdtol) + + result = np.moveaxis(result, -1, tgt_mesh_dim) + return result + + +def _create_mesh_cube(data, src_cube, grid_x_dim, grid_y_dim, mesh): + """ + Return a new cube for the result of regridding. + + Returned cube represents the result of regridding the source cube + onto the new mesh. + All the metadata and coordinates of the result cube are copied from + the source cube, with two exceptions: + - Grid dimension coordinates are copied from the grid cube. + - Auxiliary coordinates which span the mesh dimension are + ignored. + + Parameters + ---------- + data : array + The regridded data as an N-dimensional NumPy array. + src_cube : cube + The source Cube. + grid_x_dim : int + The dimension of the x dimension on the source Cube. + grid_y_dim : int + The dimension of the y dimension on the source Cube. + mesh : Mesh + The :class:`iris.experimental.ugrid.Mesh` for the new + Cube. + + Returns + ------- + cube + A new iris.cube.Cube instance. + + """ + new_cube = iris.cube.Cube(data) + + min_grid_dim = min(grid_x_dim, grid_y_dim) + max_grid_dim = max(grid_x_dim, grid_y_dim) + for coord in mesh.to_MeshCoords("face"): + new_cube.add_aux_coord(coord, min_grid_dim) + + new_cube.metadata = copy.deepcopy(src_cube.metadata) + + def copy_coords(src_coords, add_method): + for coord in src_coords: + dims = src_cube.coord_dims(coord) + if grid_x_dim in dims or grid_y_dim in dims: + continue + # Since the 2D grid will be replaced by a 1D mesh, dims which are + # beyond the max_grid_dim are decreased by one. + dims = [dim if dim < max_grid_dim else dim - 1 for dim in dims] + result_coord = coord.copy() + # Add result_coord to the owner of add_method. + add_method(result_coord, dims) + + copy_coords(src_cube.dim_coords, new_cube.add_dim_coord) + copy_coords(src_cube.aux_coords, new_cube.add_aux_coord) + + return new_cube + + +def _regrid_rectilinear_to_unstructured__prepare(src_grid_cube, target_mesh_cube): + """ + First (setup) part of 'regrid_rectilinear_to_unstructured'. + + Check inputs and calculate the sparse regrid matrix and related info. + The 'regrid info' returned can be re-used over many 2d slices. + + """ + grid_x, grid_y = get_xy_dim_coords(src_grid_cube) + mesh = target_mesh_cube.mesh + # TODO: Improve the checking of mesh validity. Check the mesh location and + # raise appropriate error messages. + assert mesh is not None + grid_x_dim = src_grid_cube.coord_dims(grid_x)[0] + grid_y_dim = src_grid_cube.coord_dims(grid_y)[0] + + meshinfo = _mesh_to_MeshInfo(mesh) + gridinfo = _cube_to_GridInfo(src_grid_cube) + + regridder = Regridder(gridinfo, meshinfo) + + regrid_info = (grid_x_dim, grid_y_dim, grid_x, grid_y, mesh, regridder) + + return regrid_info + + +def _regrid_rectilinear_to_unstructured__perform(src_cube, regrid_info, mdtol): + """ + Second (regrid) part of 'regrid_rectilinear_to_unstructured'. + + Perform the prepared regrid calculation on a single cube. + + """ + grid_x_dim, grid_y_dim, grid_x, grid_y, mesh, regridder = regrid_info + + # Perform regridding with realised data for the moment. This may be changed + # in future to handle src_cube.lazy_data. + new_data = _regrid_along_grid_dims( + regridder, src_cube.data, grid_x_dim, grid_y_dim, mdtol + ) + + new_cube = _create_mesh_cube( + new_data, + src_cube, + grid_x_dim, + grid_y_dim, + mesh, + ) + return new_cube + + +def regrid_rectilinear_to_unstructured(src_cube, mesh_cube, mdtol=0): + """ + Regrid rectilinear cube onto unstructured mesh. + + Return a new cube with data values calculated using the area weighted + mean of data values from rectilinear cube src_cube regridded onto the + horizontal mesh of mesh_cube. The dimensions on the cube associated + with the grid will replaced by a dimension associated with the mesh. + That dimension will be the the first of the grid dimensions, whether + it is associated with the x or y coordinate. Since two dimensions are + being replaced by one, coordinates associated with dimensions after + the grid will become associated with dimensions one lower. + This function requires that the horizontal dimension of mesh_cube is + described by a 2D mesh with data located on the faces of that mesh. + This function requires that the horizontal grid of src_cube is + rectilinear (i.e. expressed in terms of two orthogonal 1D coordinates). + This function also requires that the coordinates describing the + horizontal grid have bounds. + + Parameters + ---------- + src_cube : cube + A rectilinear instance of iris.cube.Cube that supplies the data, + metadata and coordinates. + mesh_cube : cube + An unstructured instance of iris.cube.Cube that supplies the desired + horizontal mesh definition. + mdtol : float, optional + Tolerance of missing data. The value returned in each element of the + returned cube's data array will be masked if the fraction of masked + data in the overlapping cells of the source cube exceeds mdtol. This + fraction is calculated based on the area of masked cells within each + target cell. mdtol=0 means no missing data is tolerated while mdtol=1 + will mean the resulting element will be masked if and only if all the + overlapping cells of the source cube are masked. Defaults to 0. + + Returns + ------- + cube + A new iris.cube.Cube instance. + + """ + regrid_info = _regrid_rectilinear_to_unstructured__prepare(src_cube, mesh_cube) + result = _regrid_rectilinear_to_unstructured__perform(src_cube, regrid_info, mdtol) + return result + + +class GridToMeshESMFRegridder: + """Regridder class for rectilinear to unstructured cubes.""" + + def __init__(self, src_mesh_cube, target_grid_cube, mdtol=1): + """ + Create regridder for conversions between source grid and target mesh. + + Parameters + ---------- + src_grid_cube : cube + The unstructured iris cube providing the source grid. + target_grid_cube : cube + The rectilinear iris cube providing the target mesh. + mdtol : float, optional + Tolerance of missing data. The value returned in each element of + the returned array will be masked if the fraction of masked data + exceeds mdtol. mdtol=0 means no missing data is tolerated while + mdtol=1 will mean the resulting element will be masked if and only + if all the contributing elements of data are masked. + Defaults to 1. + + """ + # Missing data tolerance. + # Code directly copied from iris. + if not (0 <= mdtol <= 1): + msg = "Value for mdtol must be in range 0 - 1, got {}." + raise ValueError(msg.format(mdtol)) + self.mdtol = mdtol + + partial_regrid_info = _regrid_rectilinear_to_unstructured__prepare( + src_mesh_cube, target_grid_cube + ) + + # Store regrid info. + _, _, self.grid_x, self.grid_y, self.mesh, self.regridder = partial_regrid_info + + def __call__(self, cube): + """ + Regrid this cube onto the target mesh of this regridder instance. + + The given cube must be defined with the same grid as the source + cube used to create this MeshToGridESMFRegridder instance. + + Parameters + ---------- + cube : cube + A iris.cube.Cube instance to be regridded. + + Returns + ------- + A cube defined with the horizontal dimensions of the target + and the other dimensions from this cube. The data values of + this cube will be converted to values on the new grid using + area-weighted regridding via ESMF generated weights. + + """ + grid_x, grid_y = get_xy_dim_coords(cube) + if (grid_x != self.grid_x) or (grid_y != self.grid_y): + raise ValueError( + "The given cube is not defined on the same " + "source grid as this regridder." + ) + + grid_x_dim = cube.coord_dims(grid_x)[0] + grid_y_dim = cube.coord_dims(grid_y)[0] + + regrid_info = ( + grid_x_dim, + grid_y_dim, + self.grid_x, + self.grid_y, + self.mesh, + self.regridder, + ) + + return _regrid_rectilinear_to_unstructured__perform( + cube, regrid_info, self.mdtol + ) diff --git a/esmf_regrid/tests/unit/experimental/unstructured_scheme/test_GridToMeshESMFRegridder.py b/esmf_regrid/tests/unit/experimental/unstructured_scheme/test_GridToMeshESMFRegridder.py new file mode 100644 index 00000000..7bb756ad --- /dev/null +++ b/esmf_regrid/tests/unit/experimental/unstructured_scheme/test_GridToMeshESMFRegridder.py @@ -0,0 +1,198 @@ +"""Unit tests for :func:`esmf_regrid.experimental.unstructured_scheme.GridToMeshESMFRegridder`.""" + +from iris.coords import AuxCoord, DimCoord +from iris.cube import Cube +import numpy as np +from numpy import ma +import pytest + +from esmf_regrid.experimental.unstructured_scheme import ( + GridToMeshESMFRegridder, +) +from esmf_regrid.tests.unit.experimental.unstructured_scheme.test__cube_to_GridInfo import ( + _grid_cube, +) +from esmf_regrid.tests.unit.experimental.unstructured_scheme.test__regrid_unstructured_to_rectilinear__prepare import ( + _flat_mesh_cube, +) + + +def test_flat_cubes(): + """ + Basic test for :func:`esmf_regrid.experimental.unstructured_scheme.GridToMeshESMFRegridder`. + + Tests with flat cubes as input (a 2D grid cube and a 1D mesh cube). + """ + tgt = _flat_mesh_cube() + + n_lons = 6 + n_lats = 5 + lon_bounds = (-180, 180) + lat_bounds = (-90, 90) + src = _grid_cube(n_lons, n_lats, lon_bounds, lat_bounds, circular=True) + # Ensure data in the target grid is different to the expected data. + # i.e. target grid data is all zero, expected data is all one + tgt.data[:] = 0 + + def _add_metadata(cube): + result = cube.copy() + result.units = "K" + result.attributes = {"a": 1} + result.standard_name = "air_temperature" + scalar_height = AuxCoord([5], units="m", standard_name="height") + scalar_time = DimCoord([10], units="s", standard_name="time") + result.add_aux_coord(scalar_height) + result.add_aux_coord(scalar_time) + return result + + src = _add_metadata(src) + src.data[:] = 1 # Ensure all data in the source is one. + regridder = GridToMeshESMFRegridder(src, tgt) + result = regridder(src) + src_T = src.copy() + src_T.transpose() + result_transposed = regridder(src_T) + + expected_data = np.ones([n_lats, n_lons]) + expected_cube = _add_metadata(tgt) + + # Lenient check for data. + assert np.allclose(expected_data, result.data) + assert np.allclose(expected_data, result_transposed.data) + + # Check metadata and scalar coords. + expected_cube.data = result.data + assert expected_cube == result + expected_cube.data = result_transposed.data + assert expected_cube == result_transposed + + +def test_multidim_cubes(): + """ + Test for :func:`esmf_regrid.experimental.unstructured_scheme.regrid_rectilinear_to_unstructured`. + + Tests with multidimensional cubes. The source cube contains + coordinates on the dimensions before and after the grid dimensions. + """ + tgt = _flat_mesh_cube() + mesh = tgt.mesh + mesh_length = mesh.connectivity(contains_face=True).shape[0] + n_lons = 6 + n_lats = 5 + lon_bounds = (-180, 180) + lat_bounds = (-90, 90) + grid = _grid_cube(n_lons, n_lats, lon_bounds, lat_bounds, circular=True) + + h = 2 + t = 3 + height = DimCoord(np.arange(h), standard_name="height") + time = DimCoord(np.arange(t), standard_name="time") + + src_data = np.empty([t, n_lats, n_lons, h]) + src_data[:] = np.arange(t * h).reshape([t, h])[:, np.newaxis, np.newaxis, :] + cube = Cube(src_data) + cube.add_dim_coord(grid.coord("latitude"), 1) + cube.add_dim_coord(grid.coord("longitude"), 2) + cube.add_dim_coord(time, 0) + cube.add_dim_coord(height, 3) + + regridder = GridToMeshESMFRegridder(grid, tgt) + result = regridder(cube) + + # Lenient check for data. + expected_data = np.empty([t, mesh_length, h]) + expected_data[:] = np.arange(t * h).reshape(t, h)[:, np.newaxis, :] + assert np.allclose(expected_data, result.data) + + mesh_coord_x, mesh_coord_y = mesh.to_MeshCoords("face") + expected_cube = Cube(expected_data) + expected_cube.add_dim_coord(time, 0) + expected_cube.add_aux_coord(mesh_coord_x, 1) + expected_cube.add_aux_coord(mesh_coord_y, 1) + expected_cube.add_dim_coord(height, 2) + + # Check metadata and scalar coords. + result.data = expected_data + assert expected_cube == result + + +def test_invalid_mdtol(): + """ + Test initialisation of :func:`esmf_regrid.experimental.unstructured_scheme.GridToMeshESMFRegridder`. + + Checks that an error is raised when mdtol is out of range. + """ + tgt = _flat_mesh_cube() + + n_lons = 6 + n_lats = 5 + lon_bounds = (-180, 180) + lat_bounds = (-90, 90) + src = _grid_cube(n_lons, n_lats, lon_bounds, lat_bounds, circular=True) + + with pytest.raises(ValueError): + _ = GridToMeshESMFRegridder(src, tgt, mdtol=2) + with pytest.raises(ValueError): + _ = GridToMeshESMFRegridder(src, tgt, mdtol=-1) + + +def test_mismatched_grids(): + """ + Test error handling in calling of :func:`esmf_regrid.experimental.unstructured_scheme.GridToMeshESMFRegridder`. + + Checks that an error is raised when the regridder is called with a + cube whose grid does not match with the one used when initialising + the regridder. + """ + tgt = _flat_mesh_cube() + n_lons = 6 + n_lats = 5 + lon_bounds = (-180, 180) + lat_bounds = (-90, 90) + src = _grid_cube(n_lons, n_lats, lon_bounds, lat_bounds, circular=True) + + regridder = GridToMeshESMFRegridder(src, tgt) + + n_lons_other = 3 + n_lats_other = 10 + src_other = _grid_cube( + n_lons_other, n_lats_other, lon_bounds, lat_bounds, circular=True + ) + with pytest.raises(ValueError): + _ = regridder(src_other) + + +def test_mask_handling(): + """ + Test masked data handling for :func:`esmf_regrid.experimental.unstructured_scheme.GridToMeshESMFRegridder`. + + Tests masked data handling for multiple valid values for mdtol. + """ + tgt = _flat_mesh_cube() + + n_lons = 6 + n_lats = 5 + lon_bounds = (-180, 180) + lat_bounds = (-90, 90) + src = _grid_cube(n_lons, n_lats, lon_bounds, lat_bounds, circular=True) + + data = np.ones([n_lats, n_lons]) + mask = np.zeros([n_lats, n_lons]) + mask[0, 0] = 1 + masked_data = ma.array(data, mask=mask) + src.data = masked_data + regridder_0 = GridToMeshESMFRegridder(src, tgt, mdtol=0) + regridder_05 = GridToMeshESMFRegridder(src, tgt, mdtol=0.05) + regridder_1 = GridToMeshESMFRegridder(src, tgt, mdtol=1) + result_0 = regridder_0(src) + result_05 = regridder_05(src) + result_1 = regridder_1(src) + + expected_data = np.ones(tgt.shape) + expected_0 = ma.array(expected_data) + expected_05 = ma.array(expected_data, mask=[0, 0, 1, 0, 0, 0]) + expected_1 = ma.array(expected_data, mask=[1, 0, 1, 0, 0, 0]) + + assert ma.allclose(expected_0, result_0.data) + assert ma.allclose(expected_05, result_05.data) + assert ma.allclose(expected_1, result_1.data) diff --git a/esmf_regrid/tests/unit/experimental/unstructured_scheme/test_regrid_rectilinear_to_unstructured.py b/esmf_regrid/tests/unit/experimental/unstructured_scheme/test_regrid_rectilinear_to_unstructured.py new file mode 100644 index 00000000..d0406e62 --- /dev/null +++ b/esmf_regrid/tests/unit/experimental/unstructured_scheme/test_regrid_rectilinear_to_unstructured.py @@ -0,0 +1,164 @@ +"""Unit tests for :func:`esmf_regrid.experimental.unstructured_scheme.regrid_rectilinear_to_unstructured`.""" + +from iris.coords import AuxCoord, DimCoord +from iris.cube import Cube +import numpy as np +from numpy import ma + +from esmf_regrid.experimental.unstructured_scheme import ( + regrid_rectilinear_to_unstructured, +) +from esmf_regrid.tests.unit.experimental.unstructured_scheme.test__cube_to_GridInfo import ( + _grid_cube, +) +from esmf_regrid.tests.unit.experimental.unstructured_scheme.test__regrid_unstructured_to_rectilinear__prepare import ( + _flat_mesh_cube, +) + + +def test_flat_cubes(): + """ + Basic test for :func:`esmf_regrid.experimental.unstructured_scheme.regrid_rectilinear_to_unstructured`. + + Tests with flat cubes as input (a 2D grid cube and a 1D mesh cube). + """ + tgt = _flat_mesh_cube() + + n_lons = 6 + n_lats = 5 + lon_bounds = (-180, 180) + lat_bounds = (-90, 90) + src = _grid_cube(n_lons, n_lats, lon_bounds, lat_bounds, circular=True) + # Ensure data in the target grid is different to the expected data. + # i.e. target grid data is all zero, expected data is all one + tgt.data[:] = 0 + + def _add_metadata(cube): + result = cube.copy() + result.units = "K" + result.attributes = {"a": 1} + result.standard_name = "air_temperature" + scalar_height = AuxCoord([5], units="m", standard_name="height") + scalar_time = DimCoord([10], units="s", standard_name="time") + result.add_aux_coord(scalar_height) + result.add_aux_coord(scalar_time) + return result + + src = _add_metadata(src) + src.data[:] = 1 # Ensure all data in the source is one. + result = regrid_rectilinear_to_unstructured(src, tgt) + src_T = src.copy() + src_T.transpose() + result_transposed = regrid_rectilinear_to_unstructured(src_T, tgt) + + expected_data = np.ones([n_lats, n_lons]) + expected_cube = _add_metadata(tgt) + + # Lenient check for data. + assert np.allclose(expected_data, result.data) + assert np.allclose(expected_data, result_transposed.data) + + # Check metadata and scalar coords. + expected_cube.data = result.data + assert expected_cube == result + expected_cube.data = result_transposed.data + assert expected_cube == result_transposed + + +def test_multidim_cubes(): + """ + Test for :func:`esmf_regrid.experimental.unstructured_scheme.regrid_rectilinear_to_unstructured`. + + Tests with multidimensional cubes. The source cube contains + coordinates on the dimensions before and after the grid dimensions. + """ + tgt = _flat_mesh_cube() + mesh = tgt.mesh + mesh_length = mesh.connectivity(contains_face=True).shape[0] + n_lons = 6 + n_lats = 5 + lon_bounds = (-180, 180) + lat_bounds = (-90, 90) + grid = _grid_cube(n_lons, n_lats, lon_bounds, lat_bounds, circular=True) + + h = 2 + p = 4 + t = 3 + height = DimCoord(np.arange(h), standard_name="height") + pressure = DimCoord(np.arange(p), standard_name="air_pressure") + time = DimCoord(np.arange(t), standard_name="time") + spanning = AuxCoord(np.ones([t, p, h]), long_name="spanning dim") + ignore = AuxCoord(np.ones([n_lats, h]), long_name="ignore") + + src_data = np.empty([t, n_lats, p, n_lons, h]) + src_data[:] = np.arange(t * p * h).reshape([t, p, h])[ + :, np.newaxis, :, np.newaxis, : + ] + cube = Cube(src_data) + cube.add_dim_coord(grid.coord("latitude"), 1) + cube.add_dim_coord(grid.coord("longitude"), 3) + cube.add_dim_coord(time, 0) + cube.add_dim_coord(pressure, 2) + cube.add_dim_coord(height, 4) + cube.add_aux_coord(spanning, [0, 2, 4]) + cube.add_aux_coord(ignore, [1, 4]) + + result = regrid_rectilinear_to_unstructured(cube, tgt) + + cube_transposed = cube.copy() + cube_transposed.transpose([0, 3, 2, 1, 4]) + result_transposed = regrid_rectilinear_to_unstructured(cube_transposed, tgt) + + # Lenient check for data. + expected_data = np.empty([t, mesh_length, p, h]) + expected_data[:] = np.arange(t * p * h).reshape(t, p, h)[:, np.newaxis, :, :] + assert np.allclose(expected_data, result.data) + assert np.allclose(expected_data, result_transposed.data) + + mesh_coord_x, mesh_coord_y = mesh.to_MeshCoords("face") + expected_cube = Cube(expected_data) + expected_cube.add_dim_coord(time, 0) + expected_cube.add_aux_coord(mesh_coord_x, 1) + expected_cube.add_aux_coord(mesh_coord_y, 1) + expected_cube.add_dim_coord(pressure, 2) + expected_cube.add_dim_coord(height, 3) + expected_cube.add_aux_coord(spanning, [0, 2, 3]) + + # Check metadata and scalar coords. + result.data = expected_data + assert expected_cube == result + result_transposed.data = expected_data + assert expected_cube == result_transposed + + +def test_mask_handling(): + """ + Test masked data handling for :func:`esmf_regrid.experimental.unstructured_scheme.regrid_rectilinear_to_unstructured`. + + Tests masked data handling for multiple valid values for mdtol. + """ + tgt = _flat_mesh_cube() + + n_lons = 6 + n_lats = 5 + lon_bounds = (-180, 180) + lat_bounds = (-90, 90) + src = _grid_cube(n_lons, n_lats, lon_bounds, lat_bounds, circular=True) + + data = np.ones([n_lats, n_lons]) + mask = np.zeros([n_lats, n_lons]) + mask[0, 0] = 1 + masked_data = ma.array(data, mask=mask) + src.data = masked_data + result_0 = regrid_rectilinear_to_unstructured(src, tgt, mdtol=0) + result_05 = regrid_rectilinear_to_unstructured(src, tgt, mdtol=0.05) + result_1 = regrid_rectilinear_to_unstructured(src, tgt, mdtol=1) + + expected_data = np.ones(tgt.shape) + expected_0 = ma.array(expected_data) + expected_05 = ma.array(expected_data, mask=[0, 0, 1, 0, 0, 0]) + expected_1 = ma.array(expected_data, mask=[1, 0, 1, 0, 0, 0]) + + assert ma.allclose(expected_0, result_0.data) + assert ma.allclose(expected_05, result_05.data) + assert ma.allclose(expected_1, result_1.data)