Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allowing exemption to axis guessing on coords #5551

Merged
merged 32 commits into from
Nov 17, 2023
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
6b49b10
allowing excemption to axis guessing on coords
HGWright Oct 25, 2023
093372c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 25, 2023
eafa1d0
updating pr
HGWright Nov 7, 2023
d42a2b7
merge commit
HGWright Nov 7, 2023
e942b32
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 7, 2023
875f827
remove from metadata
HGWright Nov 9, 2023
056defa
merge conflict
HGWright Nov 9, 2023
8cd64b2
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 9, 2023
047f58b
remove merge clash
HGWright Nov 9, 2023
247feb1
Merge branch 'guess_coord' of github.com:HGWright/iris into guess_coord
HGWright Nov 9, 2023
724d05f
adding review comments
HGWright Nov 9, 2023
3d183f5
more review changes
HGWright Nov 9, 2023
c901c12
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 9, 2023
a121ec4
parametrise and add tests
HGWright Nov 9, 2023
a7e6aa9
precommit changes
HGWright Nov 9, 2023
b5da396
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 9, 2023
6020a63
fix last test
HGWright Nov 9, 2023
cffa5b6
Merge branch 'guess_coord' of github.com:HGWright/iris into guess_coord
HGWright Nov 9, 2023
a9a45d1
addressing review comments
HGWright Nov 17, 2023
4151a7a
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 17, 2023
c35c3d1
fix test failure
HGWright Nov 17, 2023
3e6fba8
Merge branch 'guess_coord' of github.com:HGWright/iris into guess_coord
HGWright Nov 17, 2023
d643915
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 17, 2023
5033391
add whatsnew and conftest files
HGWright Nov 17, 2023
faf4188
fix merge conflict
HGWright Nov 17, 2023
ed4c4c2
fix sentence
HGWright Nov 17, 2023
0180f58
Merge branch 'main' into guess_coord
HGWright Nov 17, 2023
cfbe34f
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Nov 17, 2023
6af7f81
fix flake8
HGWright Nov 17, 2023
c8854d3
Merge branch 'guess_coord' of github.com:HGWright/iris into guess_coord
HGWright Nov 17, 2023
19dd66e
fix last test
HGWright Nov 17, 2023
b01f3f8
update whatsnew
HGWright Nov 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/src/whatsnew/latest.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ This document explains the changes made to Iris for this release
✨ Features
===========

#. `@rcomer`_ rewrote :func:`~iris.util.broadcast_to_shape` so it now handles
lazy data. (:pull:`5307`)

bjlittle marked this conversation as resolved.
Show resolved Hide resolved
#. `@trexfeathers`_ and `@HGWright`_ (reviewer) sub-categorised all Iris'
:class:`UserWarning`\s for richer filtering. The full index of
sub-categories can be seen here: :mod:`iris.exceptions` . (:pull:`5498`)
Expand All @@ -44,6 +47,10 @@ This document explains the changes made to Iris for this release
Winter - December to February) will be assigned to the preceding year (e.g.
the year of December) instead of the following year (the default behaviour).
(:pull:`5573`)

#. `@HGWright`_ added :func:`~iris.coords.Coord.ignore_axis` to allow manual
bjlittle marked this conversation as resolved.
Show resolved Hide resolved
intervention preventing :func:`~iris.util.guess_coord_axis` from acting on a
coordinate. (:pull:`5551`)


🐛 Bugs Fixed
Expand Down
38 changes: 35 additions & 3 deletions lib/iris/coords.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
import iris.time
import iris.util

#: The default value for ignore_axis which controls guess_coord_axis' behaviour
DEFAULT_IGNORE_AXIS = False


class _DimensionalMetadata(CFVariableMixin, metaclass=ABCMeta):
"""
Expand Down Expand Up @@ -860,7 +863,6 @@ def xml_element(self, doc):
element.setAttribute(
"climatological", str(self.climatological)
)

if self.attributes:
attributes_element = doc.createElement("attributes")
for name in sorted(self.attributes.keys()):
Expand Down Expand Up @@ -1593,6 +1595,8 @@ def __init__(
self.bounds = bounds
self.climatological = climatological

self._ignore_axis = DEFAULT_IGNORE_AXIS

def copy(self, points=None, bounds=None):
"""
Returns a copy of this coordinate.
Expand Down Expand Up @@ -1625,6 +1629,10 @@ def copy(self, points=None, bounds=None):
# self.
new_coord.bounds = bounds

# The state of ignore_axis is controlled by the coordinate rather than
# the metadata manager
new_coord.ignore_axis = self.ignore_axis

return new_coord

@classmethod
Expand All @@ -1644,7 +1652,14 @@ def from_coord(cls, coord):
if issubclass(cls, DimCoord):
# DimCoord introduces an extra constructor keyword.
kwargs["circular"] = getattr(coord, "circular", False)
return cls(**kwargs)

new_coord = cls(**kwargs)

# The state of ignore_axis is controlled by the coordinate rather than
# the metadata manager
new_coord.ignore_axis = coord.ignore_axis

return new_coord

@property
def points(self):
Expand Down Expand Up @@ -1736,6 +1751,24 @@ def climatological(self, value):

self._metadata_manager.climatological = value

@property
def ignore_axis(self):
"""
A boolean that controls whether guess_coord_axis acts on this
coordinate.

Defaults to False, and when set to True it will be skipped by
guess_coord_axis.
"""
return self._ignore_axis

@ignore_axis.setter
def ignore_axis(self, value):
if not isinstance(value, bool):
emsg = "'ignore_axis' can only be set to 'True' or 'False'"
raise ValueError(emsg)
self._ignore_axis = value

def lazy_points(self):
"""
Return a lazy array representing the coord points.
Expand Down Expand Up @@ -2694,7 +2727,6 @@ def __init__(
Will set to True when a climatological time axis is loaded
from NetCDF.
Always False if no bounds exist.

"""
# Configure the metadata manager.
self._metadata_manager = metadata_manager_factory(DimCoordMetadata)
Expand Down
14 changes: 14 additions & 0 deletions lib/iris/tests/unit/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Copyright Iris contributors
#
# This file is part of Iris and is released under the BSD license.
# See LICENSE in the root of the repository for full licensing details.
"""Unit tests fixture infra-structure."""
import pytest

import iris


@pytest.fixture
def sample_coord():
sample_coord = iris.coords.DimCoord(points=(1, 2, 3, 4, 5))
return sample_coord
34 changes: 34 additions & 0 deletions lib/iris/tests/unit/coords/test_Coord.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import dask.array as da
import numpy as np
import pytest

import iris
from iris.coords import AuxCoord, Coord, DimCoord
Expand Down Expand Up @@ -1149,6 +1150,39 @@ def test_change_units(self):
self.assertFalse(coord.climatological)


class TestIgnoreAxis:
def test_default(self, sample_coord):
assert sample_coord.ignore_axis is False

def test_set_true(self, sample_coord):
sample_coord.ignore_axis = True
assert sample_coord.ignore_axis is True

def test_set_random_value(self, sample_coord):
with pytest.raises(
ValueError,
match=r"'ignore_axis' can only be set to 'True' or 'False'",
):
sample_coord.ignore_axis = "foo"

@pytest.mark.parametrize(
"ignore_axis, copy_or_from, result",
[
(True, "copy", True),
(True, "from_coord", True),
(False, "copy", False),
(False, "from_coord", False),
],
)
def test_copy_coord(self, ignore_axis, copy_or_from, result, sample_coord):
sample_coord.ignore_axis = ignore_axis
if copy_or_from == "copy":
new_coord = sample_coord.copy()
elif copy_or_from == "from_coord":
new_coord = sample_coord.from_coord(sample_coord)
assert new_coord.ignore_axis is result


class Test___init____abstractmethod(tests.IrisTest):
def test(self):
emsg = (
Expand Down
50 changes: 50 additions & 0 deletions lib/iris/tests/unit/util/test_guess_coord_axis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Copyright Iris contributors
#
# This file is part of Iris and is released under the BSD license.
# See LICENSE in the root of the repository for full licensing details.
"""Test function :func:`iris.util.guess_coord_axis`."""

import pytest

from iris.util import guess_coord_axis


class TestGuessCoord:
@pytest.mark.parametrize(
"coordinate, axis",
[
("longitude", "X"),
("grid_longitude", "X"),
("projection_x_coordinate", "X"),
("latitude", "Y"),
("grid_latitude", "Y"),
("projection_y_coordinate", "Y"),
],
)
def test_coord(self, coordinate, axis, sample_coord):
sample_coord.standard_name = coordinate
assert guess_coord_axis(sample_coord) == axis

@pytest.mark.parametrize(
"units, axis",
[
("hPa", "Z"),
("days since 1970-01-01 00:00:00", "T"),
],
)
def test_units(self, units, axis, sample_coord):
sample_coord.units = units
assert guess_coord_axis(sample_coord) == axis

@pytest.mark.parametrize(
"ignore_axis, result",
[
(True, None),
(False, "X"),
],
)
def test_ignore_axis(self, ignore_axis, result, sample_coord):
sample_coord.standard_name = "longitude"
sample_coord.ignore_axis = ignore_axis

assert guess_coord_axis(sample_coord) == result
9 changes: 8 additions & 1 deletion lib/iris/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -257,10 +257,17 @@ def guess_coord_axis(coord):
This function maintains laziness when called; it does not realise data.
See more at :doc:`/userguide/real_and_lazy_data`.

The ``guess_coord_axis`` behaviour can be skipped by setting the coordinate property ``ignore_axis``
to ``False``.

"""

axis = None

if coord.standard_name in (
if hasattr(coord, "ignore_axis") and coord.ignore_axis is True:
return axis

elif coord.standard_name in (
"longitude",
"grid_longitude",
"projection_x_coordinate",
Expand Down
Loading