Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
3 changes: 3 additions & 0 deletions Changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 missing external cell measures data
(https://github.com/NCAS-CMS/cf-python/issues/885)
* New dependency: ``distributed>=2025.5.1``

----
Expand Down
93 changes: 53 additions & 40 deletions cf/field.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -5472,52 +5477,60 @@ 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
value of *scale* may be modified depending on the
value of *measure*.
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,
great_circle=great_circle)``.

.. note:: By default *weights* is `None`, resulting in
**unweighted calculations**.
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**.

.. 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',
.. 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*
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.

.. 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.

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**.
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
Expand Down
16 changes: 10 additions & 6 deletions cf/weights.py
Original file line number Diff line number Diff line change
Expand Up @@ -1508,15 +1508,16 @@ 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:
if auto:
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()
Expand All @@ -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)
Expand All @@ -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"
)

Expand Down
Loading