Skip to content

Commit 25b8752

Browse files
add location argument to mesh.coords (#6055)
* add location argument to mesh.coords * add tests and error checking * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * remove old coord selection behaviour * remove additional reference to include_nodes * remove additional references to include_ * fix bug * fix bug * change code in coord manager filters * address review comments * add review suggestion * address review comment --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent c60afa8 commit 25b8752

File tree

7 files changed

+82
-151
lines changed

7 files changed

+82
-151
lines changed

benchmarks/benchmarks/unit_style/ugrid.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,8 @@ def setup(self, n_faces, lazy=False):
9393
)
9494

9595
def get_coords_and_axes(location):
96-
search_kwargs = {f"include_{location}s": True}
9796
return [
98-
(source_mesh.coord(axis=axis, **search_kwargs), axis)
97+
(source_mesh.coord(axis=axis, location=location), axis)
9998
for axis in ("x", "y")
10099
]
101100

@@ -114,7 +113,7 @@ def get_coords_and_axes(location):
114113
self.node_x = self.object.node_coords.node_x
115114
# Kwargs for reuse in search and remove methods.
116115
self.connectivities_kwarg = dict(cf_role="edge_node_connectivity")
117-
self.coords_kwarg = dict(include_faces=True)
116+
self.coords_kwarg = dict(location="face")
118117

119118
# TODO: an opportunity for speeding up runtime if needed, since
120119
# eq_object is not needed for all benchmarks. Just don't generate it

docs/src/whatsnew/latest.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ This document explains the changes made to Iris for this release
8282
more flexible parent class (:class:`~iris.experimental.ugrid.mesh.Mesh`).
8383
(:issue:`6052` :pull:`6056`)
8484

85+
#. `@stephenworsley`_ replaced the ``include_nodes``, ``include_edges`` and
86+
``include_faces`` arguments with a single ``location`` argument in the
87+
:class:`~iris.experimental.ugrid.Mesh` methods
88+
:meth:`~iris.experimental.ugrid.Mesh.coord`, :meth:`~iris.experimental.ugrid.Mesh.coords`
89+
and :meth:`~iris.experimental.ugrid.Mesh.remove_coords`. (:pull:`6055`)
90+
8591

8692
🚀 Performance Enhancements
8793
===========================

lib/iris/experimental/ugrid/mesh.py

Lines changed: 46 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1042,8 +1042,7 @@ def line(text, i_indent=0):
10421042
main_conn_string = main_conn.summary(shorten=True, linewidth=0)
10431043
line(f"{main_conn_name}: {main_conn_string}", 2)
10441044
# Print coords
1045-
include_key = f"include_{element}s"
1046-
coords = self.coords(**{include_key: True})
1045+
coords = self.coords(location=element)
10471046
if coords:
10481047
line(f"{element} coordinates", 2)
10491048
for coord in coords:
@@ -1527,9 +1526,7 @@ def coord(
15271526
var_name=None,
15281527
attributes=None,
15291528
axis=None,
1530-
include_nodes=None,
1531-
include_edges=None,
1532-
include_faces=None,
1529+
location=None,
15331530
):
15341531
"""Return a single :class:`~iris.coords.AuxCoord` coordinate.
15351532
@@ -1577,12 +1574,8 @@ def coord(
15771574
The desired coordinate axis, see :func:`~iris.util.guess_coord_axis`.
15781575
If ``None``, does not check for ``axis``. Accepts the values ``X``,
15791576
``Y``, ``Z`` and ``T`` (case-insensitive).
1580-
include_node : bool, optional
1581-
Include all ``node`` coordinates in the list of objects to be matched.
1582-
include_edge : bool, optional
1583-
Include all ``edge`` coordinates in the list of objects to be matched.
1584-
include_face : bool, optional
1585-
Include all ``face`` coordinates in the list of objects to be matched.
1577+
location : str, optional
1578+
The desired location. Accepts the values ``node``, ``edge`` or ``face``.
15861579
15871580
Returns
15881581
-------
@@ -1598,9 +1591,7 @@ def coord(
15981591
var_name=var_name,
15991592
attributes=attributes,
16001593
axis=axis,
1601-
include_nodes=include_nodes,
1602-
include_edges=include_edges,
1603-
include_faces=include_faces,
1594+
location=location,
16041595
)
16051596
return list(result.values())[0]
16061597

@@ -1612,9 +1603,7 @@ def coords(
16121603
var_name=None,
16131604
attributes=None,
16141605
axis=None,
1615-
include_nodes=None,
1616-
include_edges=None,
1617-
include_faces=None,
1606+
location=None,
16181607
):
16191608
"""Return all :class:`~iris.coords.AuxCoord` coordinates from the :class:`MeshXY`.
16201609
@@ -1657,12 +1646,8 @@ def coords(
16571646
The desired coordinate axis, see :func:`~iris.util.guess_coord_axis`.
16581647
If ``None``, does not check for ``axis``. Accepts the values ``X``,
16591648
``Y``, ``Z`` and ``T`` (case-insensitive).
1660-
include_node : bool, optional
1661-
Include all ``node`` coordinates in the list of objects to be matched.
1662-
include_edge : bool, optional
1663-
Include all ``edge`` coordinates in the list of objects to be matched.
1664-
include_face : bool, optional
1665-
Include all ``face`` coordinates in the list of objects to be matched.
1649+
location : str, optional
1650+
The desired location. Accepts the values ``node``, ``edge`` or ``face``.
16661651
16671652
Returns
16681653
-------
@@ -1678,9 +1663,7 @@ def coords(
16781663
var_name=var_name,
16791664
attributes=attributes,
16801665
axis=axis,
1681-
include_nodes=include_nodes,
1682-
include_edges=include_edges,
1683-
include_faces=include_faces,
1666+
location=location,
16841667
)
16851668
return list(result.values())
16861669

@@ -1778,9 +1761,7 @@ def remove_coords(
17781761
var_name=None,
17791762
attributes=None,
17801763
axis=None,
1781-
include_nodes=None,
1782-
include_edges=None,
1783-
include_faces=None,
1764+
location=None,
17841765
):
17851766
"""Remove one or more :class:`~iris.coords.AuxCoord` from the :class:`MeshXY`.
17861767
@@ -1819,15 +1800,8 @@ def remove_coords(
18191800
The desired coordinate axis, see :func:`~iris.util.guess_coord_axis`.
18201801
If ``None``, does not check for ``axis``. Accepts the values ``X``,
18211802
``Y``, ``Z`` and ``T`` (case-insensitive).
1822-
include_node : bool, optional
1823-
Include all ``node`` coordinates in the list of objects to be matched
1824-
for potential removal.
1825-
include_edge : bool, optional
1826-
Include all ``edge`` coordinates in the list of objects to be matched
1827-
for potential removal.
1828-
include_face : bool, optional
1829-
Include all ``face`` coordinates in the list of objects to be matched
1830-
for potential removal.
1803+
location : str, optional
1804+
The desired location. Accepts the values ``node``, ``edge`` or ``face``.
18311805
18321806
Returns
18331807
-------
@@ -1836,22 +1810,17 @@ def remove_coords(
18361810
the :class:`MeshXY` that matched the given criteria.
18371811
18381812
"""
1839-
# Filter out absent arguments - only expecting face coords sometimes,
1840-
# same will be true of volumes in future.
1841-
kwargs = {
1842-
"item": item,
1843-
"standard_name": standard_name,
1844-
"long_name": long_name,
1845-
"var_name": var_name,
1846-
"attributes": attributes,
1847-
"axis": axis,
1848-
"include_nodes": include_nodes,
1849-
"include_edges": include_edges,
1850-
"include_faces": include_faces,
1851-
}
1852-
kwargs = {k: v for k, v in kwargs.items() if v}
1813+
result = self._coord_manager.remove(
1814+
item=item,
1815+
standard_name=standard_name,
1816+
long_name=long_name,
1817+
var_name=var_name,
1818+
attributes=attributes,
1819+
axis=axis,
1820+
location=location,
1821+
)
18531822

1854-
return self._coord_manager.remove(**kwargs)
1823+
return result
18551824

18561825
def xml_element(self, doc):
18571826
"""Create the :class:`xml.dom.minidom.Element` that describes this :class:`MeshXY`.
@@ -2243,21 +2212,21 @@ def filters(
22432212
var_name=None,
22442213
attributes=None,
22452214
axis=None,
2246-
include_nodes=None,
2247-
include_edges=None,
2248-
include_faces=None,
2215+
location=None,
22492216
):
22502217
# TBD: support coord_systems?
22512218

2252-
# Preserve original argument before modifying.
2253-
face_requested = include_faces
2254-
2255-
# Rationalise the tri-state behaviour.
2256-
args = [include_nodes, include_edges, include_faces]
2257-
state = not any(set(filter(lambda arg: arg is not None, args)))
2258-
include_nodes, include_edges, include_faces = map(
2259-
lambda arg: arg if arg is not None else state, args
2260-
)
2219+
# Determine locations to include.
2220+
if location is not None:
2221+
if location not in ["node", "edge", "face"]:
2222+
raise ValueError(
2223+
f"Expected location to be one of `node`, `edge` or `face`, got `{location}`"
2224+
)
2225+
include_nodes = location == "node"
2226+
include_edges = location == "edge"
2227+
include_faces = location == "face"
2228+
else:
2229+
include_nodes = include_edges = include_faces = True
22612230

22622231
def populated_coords(coords_tuple):
22632232
return list(filter(None, list(coords_tuple)))
@@ -2270,7 +2239,7 @@ def populated_coords(coords_tuple):
22702239
if hasattr(self, "face_coords"):
22712240
if include_faces:
22722241
members += populated_coords(self.face_coords)
2273-
elif face_requested:
2242+
elif location == "face":
22742243
dmsg = "Ignoring request to filter non-existent 'face_coords'"
22752244
logger.debug(dmsg, extra=dict(cls=self.__class__.__name__))
22762245

@@ -2297,8 +2266,7 @@ def remove(
22972266
var_name=None,
22982267
attributes=None,
22992268
axis=None,
2300-
include_nodes=None,
2301-
include_edges=None,
2269+
location=None,
23022270
):
23032271
return self._remove(
23042272
item=item,
@@ -2307,8 +2275,7 @@ def remove(
23072275
var_name=var_name,
23082276
attributes=attributes,
23092277
axis=axis,
2310-
include_nodes=include_nodes,
2311-
include_edges=include_edges,
2278+
location=location,
23122279
)
23132280

23142281

@@ -2383,9 +2350,7 @@ def remove(
23832350
var_name=None,
23842351
attributes=None,
23852352
axis=None,
2386-
include_nodes=None,
2387-
include_edges=None,
2388-
include_faces=None,
2353+
location=None,
23892354
):
23902355
return self._remove(
23912356
item=item,
@@ -2394,9 +2359,7 @@ def remove(
23942359
var_name=var_name,
23952360
attributes=attributes,
23962361
axis=axis,
2397-
include_nodes=include_nodes,
2398-
include_edges=include_edges,
2399-
include_faces=include_faces,
2362+
location=location,
24002363
)
24012364

24022365

@@ -2771,14 +2734,13 @@ def __init__(
27712734
raise ValueError(msg)
27722735

27732736
# Get the 'coord identity' metadata from the relevant node-coordinate.
2774-
node_coord = self.mesh.coord(include_nodes=True, axis=self.axis)
2737+
node_coord = self.mesh.coord(location="node", axis=self.axis)
27752738
node_metadict = node_coord.metadata._asdict()
27762739
# Use node metadata, unless location is face/edge.
27772740
use_metadict = node_metadict.copy()
27782741
if location != "node":
27792742
# Location is either "edge" or "face" - get the relevant coord.
2780-
kwargs = {f"include_{location}s": True, "axis": axis}
2781-
location_coord = self.mesh.coord(**kwargs)
2743+
location_coord = self.mesh.coord(location=location, axis=axis)
27822744

27832745
# Take the MeshCoord metadata from the 'location' coord.
27842746
use_metadict = location_coord.metadata._asdict()
@@ -2860,16 +2822,12 @@ def coord_system(self):
28602822
"""
28612823
# This matches where the coord metadata is drawn from.
28622824
# See : https://github.com/SciTools/iris/issues/4860
2863-
select_kwargs = {
2864-
f"include_{self.location}s": True,
2865-
"axis": self.axis,
2866-
}
28672825
try:
28682826
# NOTE: at present, a MeshCoord *always* references the relevant location
28692827
# coordinate in the mesh, from which its points are taken.
28702828
# However this might change in future ..
28712829
# see : https://github.com/SciTools/iris/discussions/4438#bounds-no-points
2872-
location_coord = self.mesh.coord(**select_kwargs)
2830+
location_coord = self.mesh.coord(location=self.location, axis=self.axis)
28732831
coord_system = location_coord.coord_system
28742832
except CoordinateNotFoundError:
28752833
# No such coord : possible in UGRID, but probably not Iris (at present).
@@ -3078,17 +3036,14 @@ def _construct_access_arrays(self):
30783036
30793037
"""
30803038
mesh, location, axis = self.mesh, self.location, self.axis
3081-
node_coord = self.mesh.coord(include_nodes=True, axis=axis)
3039+
node_coord = mesh.coord(location="node", axis=axis)
30823040

30833041
if location == "node":
30843042
points_coord = node_coord
30853043
bounds_connectivity = None
3086-
elif location == "edge":
3087-
points_coord = self.mesh.coord(include_edges=True, axis=axis)
3088-
bounds_connectivity = mesh.edge_node_connectivity
3089-
elif location == "face":
3090-
points_coord = self.mesh.coord(include_faces=True, axis=axis)
3091-
bounds_connectivity = mesh.face_node_connectivity
3044+
else:
3045+
points_coord = mesh.coord(location=location, axis=axis)
3046+
bounds_connectivity = getattr(mesh, f"{location}_node_connectivity")
30923047

30933048
# The points output is the points of the relevant element-type coord.
30943049
points = points_coord.core_points()

lib/iris/fileformats/netcdf/saver.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1204,7 +1204,7 @@ def record_dimension(names_list, dim_name, length, matching_coords=None):
12041204
if location == "node":
12051205
# For nodes, identify the dim with a coordinate variable.
12061206
# Selecting the X-axis one for definiteness.
1207-
dim_coords = mesh.coords(include_nodes=True, axis="x")
1207+
dim_coords = mesh.coords(location="node", axis="x")
12081208
else:
12091209
# For face/edge, use the relevant "optionally required"
12101210
# connectivity variable.

lib/iris/tests/integration/experimental/test_meshcoord_coordsys.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def test_assigned_mesh_cs(tmp_path):
9797
make_file(nc_path)
9898
with PARSE_UGRID_ON_LOAD.context():
9999
cube = iris.load_cube(nc_path, "node_data")
100-
nodeco_x = cube.mesh.coord(include_nodes=True, axis="x")
100+
nodeco_x = cube.mesh.coord(location="node", axis="x")
101101
meshco_x, meshco_y = [cube.coord(axis=ax) for ax in ("x", "y")]
102102
assert nodeco_x.coord_system is None
103103
assert meshco_x.coord_system is None
@@ -118,7 +118,7 @@ def test_meshcoord_coordsys_copy(tmp_path):
118118
make_file(nc_path)
119119
with PARSE_UGRID_ON_LOAD.context():
120120
cube = iris.load_cube(nc_path, "node_data")
121-
node_coord = cube.mesh.coord(include_nodes=True, axis="x")
121+
node_coord = cube.mesh.coord(location="node", axis="x")
122122
assigned_cs = GeogCS(1.0)
123123
node_coord.coord_system = assigned_cs
124124
mesh_coord = cube.coord(axis="x")

0 commit comments

Comments
 (0)