Skip to content

Commit 1022a78

Browse files
committed
fix merge conflicts
2 parents 136ba10 + d63b11b commit 1022a78

22 files changed

Lines changed: 215 additions & 32 deletions

Changelog.rst

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
version 3.17.0
1+
version 3.16.1
22
--------------
33

4-
**2024-??-??**
4+
**2024-03-01**
55

6+
* Allow DSG tractories with identical `trajectory_id` values to be
7+
aggregated (https://github.com/NCAS-CMS/cf-python/issues/723)
68
* New methods: `cf.Field.pad_missing` and `cf.Data.pad_missing`
79
(https://github.com/NCAS-CMS/cf-python/issues/717)
810
* Fix occasional bug when calculating UGRID cell areas when

DOCUMENTATION.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
sphinx-copybutton==0.5.1
66
sphinx-toggleprompt==0.2.0
77
sphinxcontrib-spelling==4.3.0
8-
serializinghtml-serializinghtml==1.1.5
8+
sphinxcontrib.serializinghtml==1.1.5
99
sphinx==2.4.5
1010
1111
* The `.py` files to generate recipes are stored in `docs/source/recipes/`.

cf/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@
7474
"""
7575

7676
__Conventions__ = "CF-1.11"
77-
__date__ = "2023-12-06"
78-
__version__ = "3.16.0"
77+
__date__ = "2023-03-01"
78+
__version__ = "3.16.1"
7979

8080
_requires = (
8181
"numpy",

cf/aggregate.py

Lines changed: 94 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
"add_offset",
5555
"calendar",
5656
"cell_methods",
57+
"featureType",
5758
"_FillValue",
5859
"flag_masks",
5960
"flag_meanings",
@@ -210,6 +211,7 @@ class _Meta:
210211
(
211212
"Type",
212213
"Identity",
214+
"featureType",
213215
"Units",
214216
"Cell_methods",
215217
"Data",
@@ -389,6 +391,10 @@ def __init__(
389391
if field_identity:
390392
self.identity = f.get_property(field_identity, None)
391393

394+
# Set the DSG featureType
395+
featureType = f.get_property("featureType", None)
396+
self.featureType = featureType
397+
392398
construct_axes = f.constructs.data_axes()
393399

394400
# ------------------------------------------------------------
@@ -502,6 +508,7 @@ def __init__(
502508
"identity": dim_identity,
503509
"key": dim_coord_key,
504510
"units": units,
511+
"cf_role": None,
505512
"hasdata": dim_coord.has_data(),
506513
"hasbounds": hasbounds,
507514
"coordrefs": self.find_coordrefs(axis),
@@ -539,11 +546,18 @@ def __init__(
539546
aux_coord, aux_identity, relaxed_units=relaxed_units
540547
)
541548

549+
# Set the cf_role for DSGs
550+
if not featureType:
551+
cf_role = None
552+
else:
553+
cf_role = aux_coord.get_property("cf_role", None)
554+
542555
info_aux.append(
543556
{
544557
"identity": aux_identity,
545558
"key": key,
546559
"units": units,
560+
"cf_role": cf_role,
547561
"hasdata": aux_coord.has_data(),
548562
"hasbounds": aux_coord.has_bounds(),
549563
"coordrefs": self.find_coordrefs(key),
@@ -586,12 +600,14 @@ def __init__(
586600

587601
return
588602

603+
identity = f"ncvar%{identity}"
589604
size = domain_axis.get_size()
590605

591606
axis_identities = {
592607
"ids": "identity",
593608
"keys": "key",
594609
"units": "units",
610+
"cf_role": "cf_role",
595611
"hasdata": "hasdata",
596612
"hasbounds": "hasbounds",
597613
"coordrefs": "coordrefs",
@@ -1773,6 +1789,9 @@ def structural_signature(self):
17731789
Cell_methods = self.cell_methods
17741790
Data = self.has_field_data
17751791

1792+
# DSG FeatureType
1793+
featureType = self.featureType
1794+
17761795
# Properties
17771796
Properties = self.properties
17781797

@@ -1812,6 +1831,7 @@ def structural_signature(self):
18121831
]
18131832
),
18141833
),
1834+
("cf_role", axis[identity]["cf_role"]),
18151835
("hasdata", axis[identity]["hasdata"]),
18161836
("hasbounds", axis[identity]["hasbounds"]),
18171837
("coordrefs", axis[identity]["coordrefs"]),
@@ -1918,6 +1938,7 @@ def structural_signature(self):
19181938
self.signature = self._structural_signature(
19191939
Type=Type,
19201940
Identity=Identity,
1941+
featureType=featureType,
19211942
Units=Units,
19221943
Cell_methods=Cell_methods,
19231944
Data=Data,
@@ -4147,6 +4168,15 @@ def _hash_values(m):
41474168

41484169
hash0 = hash1
41494170

4171+
# If 'count' is 0 then all of the 1-d coordinates have the
4172+
# same values across fields. However, for a DSG featureType
4173+
# axis we can still aggregate it, because it's OK to aggregate
4174+
# featureTypes with the timeseries_id, profile_id, or
4175+
# trajectory_id.
4176+
if not count and dsg_feature_type_axis(m0, axis):
4177+
a_identity = axis
4178+
count = 1
4179+
41504180
if count == 1:
41514181
# --------------------------------------------------------
41524182
# Exactly one axis has different 1-d coordinate values
@@ -4248,10 +4278,22 @@ def _hash_values(m):
42484278
# aggregate anything in this entire group.
42494279
# --------------------------------------------------------
42504280
if info:
4251-
meta[
4252-
0
4253-
].message = (
4254-
"Some fields have identical sets of 1-d coordinates."
4281+
coord_ids = []
4282+
for k, v in m0.axis.items():
4283+
coord_ids.extend([repr(i) for i in v["ids"]])
4284+
4285+
if len(coord_ids) > 1:
4286+
coord_ids = (
4287+
f"{', '.join(coord_ids[:-1])} and {coord_ids[-1]}"
4288+
)
4289+
elif coord_ids:
4290+
coord_ids = coord_ids[0]
4291+
else:
4292+
coord_ids = ""
4293+
4294+
meta[0].message = (
4295+
f"Some fields have identical sets of 1-d {coord_ids} "
4296+
"coordinates."
42554297
)
42564298

42574299
return ()
@@ -4839,6 +4881,19 @@ def _aggregate_2_fields(
48394881

48404882

48414883
def f_identity(meta):
4884+
"""Return the field identity for logging strings.
4885+
4886+
:Parameters:
4887+
4888+
meta: `_Meta`
4889+
The `_Meta` instance containing the field.
4890+
4891+
:Returns:
4892+
4893+
`str`
4894+
The identity.
4895+
4896+
"""
48424897
identity = meta.identity
48434898
f_identity = meta.field.identity()
48444899
if f_identity == identity:
@@ -4847,3 +4902,38 @@ def f_identity(meta):
48474902
identity = f"{meta.identity!r} ({f_identity})"
48484903

48494904
return identity
4905+
4906+
4907+
def dsg_feature_type_axis(meta, axis):
4908+
"""True if the given axis is a DSG featureType axis.
4909+
4910+
A DSG featureType axis has no dimension coordinates and at least
4911+
one 1-d auxiliary coordinate with a ``cf-role`` property.
4912+
4913+
:Parameters:
4914+
4915+
meta: `_Meta`
4916+
The `_Meta` instance
4917+
4918+
axis: `str`
4919+
One of the axes in ``meta.axis_ids``.
4920+
4921+
:Returns:
4922+
4923+
`bool`
4924+
`True` if the given axis is a DSG featureType axis.
4925+
4926+
"""
4927+
if not meta.featureType:
4928+
# The field/domain is not a DSG
4929+
return False
4930+
4931+
coords = meta.axis[axis]
4932+
if coords["dim_coord_index"] is not None:
4933+
# The axis has dimension coordinates
4934+
return False
4935+
4936+
# Return True if one of the 1-d auxiliary coordinates has a
4937+
# cf_role property
4938+
cf_role = coords["cf_role"]
4939+
return cf_role.count(None) != len(cf_role)

cf/field.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2385,27 +2385,27 @@ def cell_area(
23852385
constructs are considered for cell area
23862386
creation. Otherwise they are ignored.
23872387

2388-
.. versionadded:: 3.17.0
2388+
.. versionadded:: 3.16.1
23892389

23902390
coordinates: `bool`, optional
23912391
If True, the default, then coordinate constructs are
23922392
considered for cell area creation. Otherwise they are
23932393
ignored.
23942394

2395-
.. versionadded:: 3.17.0
2395+
.. versionadded:: 3.16.1
23962396

23972397
methods: `bool`, optional
23982398
If True, then return a dictionary describing the method
23992399
used to create the cell areas instead of the default,
24002400
a field construct.
24012401

2402-
.. versionadded:: 3.17.0
2402+
.. versionadded:: 3.16.1
24032403

24042404
return_cell_measure: `bool`, optional
24052405
If True, then return a cell measure construct instead
24062406
of the default, a field construct.
24072407

2408-
.. versionadded:: 3.17.0
2408+
.. versionadded:: 3.16.1
24092409

24102410
insert: deprecated at version 3.0.0
24112411

@@ -3507,15 +3507,15 @@ def weights(
35073507
``'volume'``. If False then cell measure constructs
35083508
are ignored for these *weights*.
35093509

3510-
.. versionadded:: 3.17.0
3510+
.. versionadded:: 3.16.1
35113511

35123512
coordinates: `bool`, optional
35133513
If True, the default, then coordinate constructs are
35143514
considered for weights creation for *weights* of
35153515
`True` or ``'area'``. If False then coordinate
35163516
constructs are ignored for these *weights*.
35173517

3518-
.. versionadded:: 3.17.0
3518+
.. versionadded:: 3.16.1
35193519

35203520
kwargs: deprecated at version 3.0.0.
35213521

@@ -8719,7 +8719,7 @@ def insert_dimension(
87198719
metadata constructs that don't already include it. By
87208720
default, metadata constructs are not changed.
87218721

8722-
.. versionadded:: 3.17.0
8722+
.. versionadded:: 3.16.1
87238723

87248724
{{inplace: `bool`, optional}}
87258725

@@ -11960,7 +11960,7 @@ def pad_missing(self, axis, pad_width=None, to_size=None, inplace=False):
1196011960
The field's data and all metadata constructs that span the
1196111961
axis are padded.
1196211962

11963-
.. versionadded:: 3.17.0
11963+
.. versionadded:: 3.16.1
1196411964

1196511965
:Parameters:
1196611966

@@ -11978,13 +11978,13 @@ def pad_missing(self, axis, pad_width=None, to_size=None, inplace=False):
1197811978

1197911979
{{inplace: `bool`, optional}}
1198011980

11981-
:Returns:
11981+
:Returns:
1198211982

11983-
`Field` or `None`
11984-
The padded field construct, or `None` if the operation
11985-
was in-place.
11983+
`Field` or `None`
11984+
The padded field construct, or `None` if the operation
11985+
was in-place.
1198611986

11987-
**Examples*
11987+
**Examples**
1198811988

1198911989
>>> f = cf.example_field(6)
1199011990
>>> print(f)

cf/regrid/regrid.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1707,6 +1707,12 @@ def create_esmpy_mesh(grid, mask=None):
17071707
node_count = node_ids.size
17081708
node_owners = np.zeros(node_count)
17091709

1710+
# Make sure that node IDs are >= 1, as needed by newer versions of
1711+
# esmpy
1712+
min_id = node_ids.min()
1713+
if min_id < 1:
1714+
node_ids += min_id + 1
1715+
17101716
# Add nodes. This must be done before `add_elements`.
17111717
esmpy_mesh.add_nodes(
17121718
node_count=node_count,

cf/test/test_aggregate.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,21 @@ def test_aggregate_ugrid(self):
649649
d += 0.1
650650
self.assertEqual(len(cf.aggregate([f, g])), 2)
651651

652+
def test_aggregate_trajectory(self):
653+
"""Test DSG trajectory aggregation"""
654+
# Test that aggregation occurs when the tractory_id axes have
655+
# identical 1-d auxiliary coordinates
656+
f = cf.example_field(11)
657+
g = cf.aggregate([f, f], relaxed_identities=True)
658+
self.assertEqual(len(g), 1)
659+
660+
g = g[0]
661+
self.assertTrue(
662+
g.subspace(**{"cf_role=trajectory_id": [0]}).equals(
663+
g.subspace(**{"cf_role=trajectory_id": [1]})
664+
)
665+
)
666+
652667

653668
if __name__ == "__main__":
654669
print("Run date:", datetime.datetime.now())

docs/source/class/cf.AuxiliaryCoordinate.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ Data
262262
:template: method.rst
263263

264264
~cf.AuxiliaryCoordinate.halo
265+
~cf.AuxiliaryCoordinate.pad_missing
265266

266267
.. rubric:: *Data array mask*
267268

docs/source/class/cf.Bounds.rst

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,16 @@ Data
170170
~cf.Bounds.swapaxes
171171
~cf.Bounds.transpose
172172

173+
.. rubric:: *Expanding the data*
174+
175+
.. autosummary::
176+
:nosignatures:
177+
:toctree: ../method/
178+
:template: method.rst
179+
180+
~cf.Bounds.halo
181+
~cf.Bounds.pad_missing
182+
173183
.. rubric:: *Data array mask*
174184

175185
.. autosummary::
@@ -205,7 +215,6 @@ Data
205215
:template: method.rst
206216

207217
~cf.Bounds.__setitem__
208-
~cf.Bounds.halo
209218
~cf.Bounds.masked_invalid
210219
~cf.Bounds.subspace
211220
~cf.Bounds.where

0 commit comments

Comments
 (0)