Skip to content

Commit

Permalink
Ensure volume units to be m3
Browse files Browse the repository at this point in the history
  • Loading branch information
enekomartinmartinez committed Feb 28, 2024
1 parent a6b7e61 commit a44c193
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 2 deletions.
4 changes: 3 additions & 1 deletion doc/recipe/preprocessor.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2141,7 +2141,9 @@ The required supplementary variable ``volcello``, or ``areacello`` in its
absence, can be attached to the main dataset as described in
:ref:`supplementary_variables`.

No depth coordinate is required as this is determined by Iris.
No depth coordinate is required as this is determined by Iris. However, to
compute the volume automatically when ``volcello`` is not provided, the depth
coordinate units should be convertible to meters.

Parameters:
* `operator`: Operation to apply.
Expand Down
25 changes: 24 additions & 1 deletion esmvalcore/preprocessor/_volume.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,14 @@ def calculate_volume(cube: Cube) -> da.core.Array:
"""Calculate volume from a cube.
This function is used when the 'ocean_volume' cell measure can't be found.
The output data will be given in cubic meters (m3).
Note
----
It gets the cell_area from the cube if it is available. If not, it
calculates it from the grid. This only works if the grid cell areas can
be calculated (i.e., latitude and longitude are 1D).
be calculated (i.e., latitude and longitude are 1D). The depth coordinate
units should be convertible to meters.
Parameters
----------
Expand Down Expand Up @@ -138,15 +140,29 @@ def calculate_volume(cube: Cube) -> da.core.Array:
"Bounds should be 2 in the last dimension to compute the "
"thickness.")

# Convert units to get the thickness in meters
original_units = depth.units
try:
depth.convert_units('m')
except ValueError as err:
raise ValueError(
'Cannot compute volume using the Z-axis. %s' % err) from err

Check notice on line 149 in esmvalcore/preprocessor/_volume.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

esmvalcore/preprocessor/_volume.py#L149

Formatting a regular string which could be a f-string (consider-using-f-string)

# Calculate Z-direction thickness
thickness = depth.core_bounds()[..., 1] - depth.core_bounds()[..., 0]

# Convert units back
depth.convert_units(original_units)

# Get or calculate the horizontal areas of the cube
has_cell_measure = bool(cube.cell_measures('cell_area'))
_try_adding_calculated_cell_area(cube)
area = cube.cell_measure('cell_area')
area_dim = cube.cell_measure_dims(area)

# Ensure cell area is in square meters as the units
area.convert_units('m2')

# Make sure input cube has not been modified
if not has_cell_measure:
cube.remove_cell_measure('cell_area')
Expand Down Expand Up @@ -207,6 +223,8 @@ def volume_statistics(
can be computed using :func:`iris.analysis.cartography.area_weights`.
The volume will be computed from the area multiplied by the
thickness, computed from the bounds of the vertical coordinate.
In that case, vertical coordinate units should be convertible to
meters.
operator:
The operation. Used to determine the :class:`iris.analysis.Aggregator`
object used to calculate the statistics. Currently, only `mean` is
Expand All @@ -220,6 +238,11 @@ def volume_statistics(
Optional keyword arguments for the :class:`iris.analysis.Aggregator`
object defined by `operator`.
Note
----
This preprocessor has been designed for oceanic variables, but it might
be applicable to atmospheric data as well.
Returns
-------
iris.cube.Cube
Expand Down
24 changes: 24 additions & 0 deletions tests/unit/preprocessor/_volume/test_volume.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ def setUp(self):
[25., 250.]],
units='m',
attributes={'positive': 'down'})
scoord = iris.coords.DimCoord([36., 36.5, 37.],
long_name='ocean_sigma_coordinate',
bounds=[[35.5, 36.25], [36.25, 36.75],
[36.75, 37.5]],
units='kg m-3',
attributes={'positive': 'down'})
zcoord_nobounds = iris.coords.DimCoord([0.5, 5., 50.],
long_name='zcoord',
units='m',
Expand Down Expand Up @@ -111,6 +117,13 @@ def setUp(self):
units='kg m-3',
)

coords_spec4_sigma = [(time, 0), (scoord, 1), (lats2, 2), (lons2, 3)]
self.grid_4d_sigma_space = iris.cube.Cube(
data2,
dim_coords_and_dims=coords_spec4_sigma,
units='kg m-3',
)

coords_spec5 = [(time2, 0), (zcoord, 1), (lats2, 2), (lons2, 3)]
self.grid_4d_2 = iris.cube.Cube(
data3,
Expand Down Expand Up @@ -538,6 +551,17 @@ def test_volume_statistics_invalid_bounds(self):
str(err.exception)
)

def test_volume_statistics_invalid_units(self):
"""Test z-axis units cannot be converted to m"""

with self.assertRaises(ValueError) as err:
volume_statistics(self.grid_4d_sigma_space, 'mean')
self.assertIn(
"Cannot compute volume using the Z-axis. "
"Unable to convert from 'Unit('kg m-3')' to 'Unit('m')'.",
str(err.exception)
)

def test_volume_statistics_z_axis_time_error(self):
# Fails because depth z-axis coord depends on time dimensions
# which would aggregate also along that dimension
Expand Down

0 comments on commit a44c193

Please sign in to comment.