From 5d44b6fb7b9312882a16e4680a55dbc06a92516c Mon Sep 17 00:00:00 2001 From: David Hassell Date: Tue, 5 Aug 2025 21:07:52 +0100 Subject: [PATCH 1/5] dev --- cf/field.py | 97 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 37 deletions(-) diff --git a/cf/field.py b/cf/field.py index 994eb66fa6..ae1f5d0982 100644 --- a/cf/field.py +++ b/cf/field.py @@ -4919,6 +4919,7 @@ def collapse( scale=None, radius="earth", great_circle=False, + cell_measures=True, verbose=None, remove_vertical_crs=True, _create_zero_size_cell_bounds=False, @@ -5472,52 +5473,61 @@ def collapse( weights: optional Specify the weights for the collapse axes. The weights - are, in general, those that would be returned by this - call of the field construct's `weights` method: - ``f.weights(weights, axes=axes, measure=measure, - scale=scale, radius=radius, great_circle=great_circle, - components=True)``. See the *axes*, *measure*, - *scale*, *radius* and *great_circle* parameters and - `cf.Field.weights` for details, and note that the + are created internaly as the output of this call of + the field construct's `weights` method: + ``f.weights(weights, components=True, axes=axes, + measure=measure, scale=scale, radius=radius, + great_circle=great_circle, + cell_measures=cell_measures)``. + + See the *axes*, *measure*, *scale*, *radius*, + *great_circle*, and *cell_measures* parameters, and + `cf.Field.weights` for details; and note that the value of *scale* may be modified depending on the value of *measure*. - .. note:: By default *weights* is `None`, resulting in - **unweighted calculations**. + .. warning:: By default *weights* is `None`, resulting + in **unweighted calculations**. + + .. note:: Setting *weights* to `True` is generally a + good way to ensure that all collapses are + appropriately weighted according to the + field construct's metadata. In this case, if + it is not possible to create weights for any + axis then an exception will be raised. .. note:: Unless the *method* is ``'integral'``, the units of the weights are not combined with the field's units in the collapsed field. - If the alternative form of providing the collapse method - and axes combined as a CF cell methods-like string via the - *method* parameter has been used, then the *axes* - parameter is ignored and the axes are derived from the - *method* parameter. For example, if *method* is ``'T: - area: minimum'`` then this defines axes of ``['T', - 'area']``. If *method* specifies multiple collapses, - e.g. ``'T: minimum area: mean'`` then this implies axes of - ``'T'`` for the first collapse, and axes of ``'area'`` for - the second collapse. - - .. note:: Setting *weights* to `True` is generally a good - way to ensure that all collapses are - appropriately weighted according to the field - construct's metadata. In this case, if it is not - possible to create weights for any axis then an - exception will be raised. + .. note:: A pre-calculated weights array may also be + provided as the *weights* parameter. See + `cf.Field.weights` for details - However, care needs to be taken if *weights* is - `True` when cell volume weights are desired. The - volume weights will be taken from a "volume" - cell measure construct if one exists, otherwise - the cell volumes will be calculated as being - proportional to the sizes of one-dimensional - vertical coordinate cells. In the latter case - **if the vertical dimension coordinates do not - define the actual height or depth thickness of - every cell in the domain then the weights will - be incorrect**. + If the collapse method and axes have been provided as + a CF cell methods-like string via the *method* + parameter, then the *axes* parameter is ignored and + the axes for weights instead inferred from that + string. For instance, if *method* is ``'T: xarea: + minimum'`` then this defines axes of ``['T', + 'area']``. If *method* specifies multiple collapses, + e.g. ``'T: minimum area: mean'`` then this implies + axes of ``'T'`` for the first collapse, and axes of + ``'area'`` for the second collapse. + + .. warning:: Care needs to be taken if *weights* is + set to `True` when cell volume weights + are desired. The volume weights will be + taken from a "volume" cell measure + construct if one exists, otherwise the + cell volumes will be calculated as being + proportional to the sizes of + one-dimensional vertical coordinate + cells. In the latter case **if the + vertical dimension coordinates do not + define the actual height or depth + thickness of every cell in the domain + then the weights will be incorrect**. *Parameter example:* To specify weights based on the field construct's @@ -5609,6 +5619,15 @@ def collapse( .. versionadded:: 3.2.0 + cell_measures: `bool`, optional + If True, the default, then area and volume cell + measure constructs are considered for weights creation + when *weights* is `True`, ``'area'``, or + ``'volume'``. If False then cell measure constructs + are ignored for these *weights*. + + .. versionadded:: NEXTVERSION + squeeze: `bool`, optional If True then size 1 collapsed axes are removed from the output data array. By default the axes which are collapsed @@ -6714,6 +6733,7 @@ def collapse( measure=measure, radius=radius, great_circle=great_circle, + cell_measures=cell_measures, ) if g_weights: # For grouped collapses, bring the weights @@ -6748,6 +6768,7 @@ def collapse( group_by=group_by, axis_in=axes_in[0], verbose=verbose, + cell_measures=cell_measures, ) if regroup: @@ -6818,6 +6839,7 @@ def collapse( measure=measure, radius=radius, great_circle=great_circle, + cell_measures=cell_measures, ) if d_weights: d_kwargs["weights"] = d_weights @@ -7091,6 +7113,7 @@ def _collapse_grouped( coordinate=None, measure=False, weights=None, + cell_measures=True, squeeze=None, group_by=None, axis_in=None, From a06723f8284135167b38453a6e8ed202ef1d05f0 Mon Sep 17 00:00:00 2001 From: David Hassell Date: Wed, 6 Aug 2025 19:07:28 +0100 Subject: [PATCH 2/5] dev --- Changelog.rst | 3 +++ cf/field.py | 40 +++++++++++++++------------------------- cf/weights.py | 16 ++++++++++------ 3 files changed, 28 insertions(+), 31 deletions(-) diff --git a/Changelog.rst b/Changelog.rst index 9b1ce2931c..f6da7f0782 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -9,6 +9,9 @@ Version NEXTVERSION * Allow multiple conditions for the same axis in `cf.Field.subspace` and `cf.Field.indices` (https://github.com/NCAS-CMS/cf-python/issues/881) +* Fix bug in `cf.Field.collapse` that causes an Exception to be raised + for external cell measures data + (https://github.com/NCAS-CMS/cf-python/issues/885) * New dependency: ``distributed>=2025.5.1`` ---- diff --git a/cf/field.py b/cf/field.py index ae1f5d0982..8657e0c8d7 100644 --- a/cf/field.py +++ b/cf/field.py @@ -3708,7 +3708,12 @@ def weights( weights_axes.discard(xaxis) weights_axes.discard(yaxis) if not Weights.cell_measure( - self, "area", comp, weights_axes, methods=methods + self, + "area", + comp, + weights_axes, + methods=methods, + auto=True, ): Weights.area_XY( self, @@ -4919,7 +4924,6 @@ def collapse( scale=None, radius="earth", great_circle=False, - cell_measures=True, verbose=None, remove_vertical_crs=True, _create_zero_size_cell_bounds=False, @@ -5477,14 +5481,12 @@ def collapse( the field construct's `weights` method: ``f.weights(weights, components=True, axes=axes, measure=measure, scale=scale, radius=radius, - great_circle=great_circle, - cell_measures=cell_measures)``. + great_circle=great_circle)``. - See the *axes*, *measure*, *scale*, *radius*, - *great_circle*, and *cell_measures* parameters, and - `cf.Field.weights` for details; and note that the - value of *scale* may be modified depending on the - value of *measure*. + See the cf.Field.weights` and the *axes*, *measure*, + *scale*, *radius*, and *great_circle* parameters for + details; and note that the value of *scale* may be + modified depending on the value of *measure*. .. warning:: By default *weights* is `None`, resulting in **unweighted calculations**. @@ -5500,9 +5502,10 @@ def collapse( units of the weights are not combined with the field's units in the collapsed field. - .. note:: A pre-calculated weights array may also be - provided as the *weights* parameter. See - `cf.Field.weights` for details + .. note:: A pre-calculated weights array or dictionary + may also be provided as the *weights* + parameter. See `cf.Field.weights` for + details If the collapse method and axes have been provided as a CF cell methods-like string via the *method* @@ -5619,15 +5622,6 @@ def collapse( .. versionadded:: 3.2.0 - cell_measures: `bool`, optional - If True, the default, then area and volume cell - measure constructs are considered for weights creation - when *weights* is `True`, ``'area'``, or - ``'volume'``. If False then cell measure constructs - are ignored for these *weights*. - - .. versionadded:: NEXTVERSION - squeeze: `bool`, optional If True then size 1 collapsed axes are removed from the output data array. By default the axes which are collapsed @@ -6733,7 +6727,6 @@ def collapse( measure=measure, radius=radius, great_circle=great_circle, - cell_measures=cell_measures, ) if g_weights: # For grouped collapses, bring the weights @@ -6768,7 +6761,6 @@ def collapse( group_by=group_by, axis_in=axes_in[0], verbose=verbose, - cell_measures=cell_measures, ) if regroup: @@ -6839,7 +6831,6 @@ def collapse( measure=measure, radius=radius, great_circle=great_circle, - cell_measures=cell_measures, ) if d_weights: d_kwargs["weights"] = d_weights @@ -7113,7 +7104,6 @@ def _collapse_grouped( coordinate=None, measure=False, weights=None, - cell_measures=True, squeeze=None, group_by=None, axis_in=None, diff --git a/cf/weights.py b/cf/weights.py index 24a63aa8f7..c0d50f3fbc 100644 --- a/cf/weights.py +++ b/cf/weights.py @@ -1508,7 +1508,7 @@ def cell_measure( return False raise ValueError( - f"Can't find weights: No {measure!r} cell measure" + f"Can't find weights for {f!r}: No {measure!r} cell measure" ) elif len_m > 1: @@ -1516,7 +1516,8 @@ def cell_measure( return False raise ValueError( - f"Can't find weights: Multiple {measure!r} cell measures" + f"Can't find weights for {f!r}: Multiple {measure!r} cell " + "measures" ) key, clm = m.popitem() @@ -1526,9 +1527,11 @@ def cell_measure( return False raise ValueError( - f"Can't find weights: Cell measure {m!r} has no data, " - "possibly because it is external. " - "Consider setting cell_measures=False" + f"Can't find weights for {f!r}: Cell measure {clm!r} has no " + "data, possibly because it is in a missing external file. " + "Consider setting cell_measures=False or, if applicable, " + "re-reading the dataset whilst providing the external " + "file (see cf.read for details)." ) clm_axes0 = f.get_data_axes(key) @@ -1543,7 +1546,8 @@ def cell_measure( return False raise ValueError( - "Multiple weights specifications for " + f"Can't find weights for {f!r}: Multiple weights " + "specifications for " f"{f.constructs.domain_axis_identity(axis)!r} axis" ) From f13a655fc140c406db93030537366382c0f5d668 Mon Sep 17 00:00:00 2001 From: David Hassell Date: Thu, 7 Aug 2025 08:21:30 +0100 Subject: [PATCH 3/5] dev --- Changelog.rst | 2 +- cf/field.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Changelog.rst b/Changelog.rst index f6da7f0782..de3ffce50a 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -10,7 +10,7 @@ Version NEXTVERSION and `cf.Field.indices` (https://github.com/NCAS-CMS/cf-python/issues/881) * Fix bug in `cf.Field.collapse` that causes an Exception to be raised - for external cell measures data + for missing external cell measures data (https://github.com/NCAS-CMS/cf-python/issues/885) * New dependency: ``distributed>=2025.5.1`` diff --git a/cf/field.py b/cf/field.py index 8657e0c8d7..7fff1f30e4 100644 --- a/cf/field.py +++ b/cf/field.py @@ -5477,7 +5477,7 @@ def collapse( weights: optional Specify the weights for the collapse axes. The weights - are created internaly as the output of this call of + are created internally as the output of this call of the field construct's `weights` method: ``f.weights(weights, components=True, axes=axes, measure=measure, scale=scale, radius=radius, From 56f2684dae11aa01520162e5f0cc3919d26d5759 Mon Sep 17 00:00:00 2001 From: David Hassell Date: Thu, 7 Aug 2025 17:41:18 +0100 Subject: [PATCH 4/5] Typos Co-authored-by: Sadie L. Bartholomew --- cf/field.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cf/field.py b/cf/field.py index 7fff1f30e4..937b4c383a 100644 --- a/cf/field.py +++ b/cf/field.py @@ -5483,7 +5483,7 @@ def collapse( measure=measure, scale=scale, radius=radius, great_circle=great_circle)``. - See the cf.Field.weights` and the *axes*, *measure*, + See the `cf.Field.weights` and the *axes*, *measure*, *scale*, *radius*, and *great_circle* parameters for details; and note that the value of *scale* may be modified depending on the value of *measure*. @@ -5511,7 +5511,7 @@ def collapse( a CF cell methods-like string via the *method* parameter, then the *axes* parameter is ignored and the axes for weights instead inferred from that - string. For instance, if *method* is ``'T: xarea: + string. For instance, if *method* is ``'T: area: minimum'`` then this defines axes of ``['T', 'area']``. If *method* specifies multiple collapses, e.g. ``'T: minimum area: mean'`` then this implies From 96c0f170c27208d19152fb94ade782a782e71d15 Mon Sep 17 00:00:00 2001 From: David Hassell Date: Thu, 7 Aug 2025 17:41:33 +0100 Subject: [PATCH 5/5] Update Changelog.rst Co-authored-by: Sadie L. Bartholomew --- Changelog.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changelog.rst b/Changelog.rst index de3ffce50a..71cd3c52ae 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -9,7 +9,7 @@ Version NEXTVERSION * Allow multiple conditions for the same axis in `cf.Field.subspace` and `cf.Field.indices` (https://github.com/NCAS-CMS/cf-python/issues/881) -* Fix bug in `cf.Field.collapse` that causes an Exception to be raised +* Fix bug in `cf.Field.collapse` that causes a ``ValueError`` to be raised for missing external cell measures data (https://github.com/NCAS-CMS/cf-python/issues/885) * New dependency: ``distributed>=2025.5.1``