Skip to content

Commit

Permalink
Promote unknown units to dimensionless in aux factories (#3965)
Browse files Browse the repository at this point in the history
* promote unknown to dimensionless units in aux factories

* patch aux factories to promote unknown to dimensionless units for formula terms

* add whatnew PR for entry
  • Loading branch information
bjlittle authored Jan 27, 2021
1 parent 8222f65 commit f468da3
Show file tree
Hide file tree
Showing 13 changed files with 636 additions and 5 deletions.
4 changes: 2 additions & 2 deletions docs/iris/src/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ def autolog(message):
if iris.__version__ == "dev":
version = "dev"
else:
# major.feature(.minor)-dev -> major.minor
version = ".".join(iris.__version__.split("-")[0].split(".")[:2])
# major.minor.patch-dev -> major.minor.patch
version = ".".join(iris.__version__.split("-")[0].split(".")[:3])
# The full version, including alpha/beta/rc tags.
release = iris.__version__

Expand Down
520 changes: 520 additions & 0 deletions docs/iris/src/whatsnew/3.0.1.rst

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions docs/iris/src/whatsnew/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Iris versions.
.. toctree::
:maxdepth: 1

3.0.1.rst
3.0.rst
2.4.rst
2.3.rst
Expand Down
2 changes: 1 addition & 1 deletion lib/iris/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ def callback(cube, field, filename):


# Iris revision.
__version__ = "3.0.0"
__version__ = "3.0.1"

# Restrict the names imported when using "from iris import *"
__all__ = [
Expand Down
25 changes: 25 additions & 0 deletions lib/iris/aux_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from abc import ABCMeta, abstractmethod
import warnings

import cf_units
import dask.array as da
import numpy as np

Expand Down Expand Up @@ -619,6 +620,10 @@ def _check_dependencies(delta, sigma, surface_air_pressure):
warnings.warn(msg, UserWarning, stacklevel=2)

# Check units.
if sigma is not None and sigma.units.is_unknown():
# Be graceful, and promote unknown to dimensionless units.
sigma.units = cf_units.Unit("1")

if sigma is not None and not sigma.units.is_dimensionless():
raise ValueError("Invalid units: sigma must be dimensionless.")
if (
Expand Down Expand Up @@ -863,6 +868,10 @@ def _check_dependencies(sigma, eta, depth, depth_c, nsigma, zlev):
)
raise ValueError(msg)

if sigma is not None and sigma.units.is_unknown():
# Be graceful, and promote unknown to dimensionless units.
sigma.units = cf_units.Unit("1")

if sigma is not None and not sigma.units.is_dimensionless():
msg = (
"Invalid units: sigma coordinate {!r} "
Expand Down Expand Up @@ -1127,6 +1136,10 @@ def _check_dependencies(sigma, eta, depth):
warnings.warn(msg, UserWarning, stacklevel=2)

# Check units.
if sigma is not None and sigma.units.is_unknown():
# Be graceful, and promote unknown to dimensionless units.
sigma.units = cf_units.Unit("1")

if sigma is not None and not sigma.units.is_dimensionless():
msg = (
"Invalid units: sigma coordinate {!r} "
Expand Down Expand Up @@ -1335,6 +1348,10 @@ def _check_dependencies(s, c, eta, depth, depth_c):
# Check units.
coords = ((s, "s"), (c, "c"))
for coord, term in coords:
if coord is not None and coord.units.is_unknown():
# Be graceful, and promote unknown to dimensionless units.
coord.units = cf_units.Unit("1")

if coord is not None and not coord.units.is_dimensionless():
msg = (
"Invalid units: {} coordinate {!r} "
Expand Down Expand Up @@ -1551,6 +1568,10 @@ def _check_dependencies(s, eta, depth, a, b, depth_c):
raise ValueError(msg)

# Check units.
if s is not None and s.units.is_unknown():
# Be graceful, and promote unknown to dimensionless units.
s.units = cf_units.Unit("1")

if s is not None and not s.units.is_dimensionless():
msg = (
"Invalid units: s coordinate {!r} "
Expand Down Expand Up @@ -1776,6 +1797,10 @@ def _check_dependencies(s, c, eta, depth, depth_c):
# Check units.
coords = ((s, "s"), (c, "c"))
for coord, term in coords:
if coord is not None and coord.units.is_unknown():
# Be graceful, and promote unknown to dimensionless units.
coord.units = cf_units.Unit("1")

if coord is not None and not coord.units.is_dimensionless():
msg = (
"Invalid units: {} coordinate {!r} "
Expand Down
3 changes: 3 additions & 0 deletions lib/iris/fileformats/netcdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,9 @@ def coord_from_term(term):
warnings.warn(msg)
coord_a = coord_from_term("a")
if coord_a is not None:
if coord_a.units.is_unknown():
# Be graceful, and promote unknown to dimensionless units.
coord_a.units = "1"
delta = coord_a * coord_p0.points[0]
delta.units = coord_a.units * coord_p0.units
delta.rename("vertical pressure")
Expand Down
9 changes: 9 additions & 0 deletions lib/iris/tests/unit/aux_factory/test_HybridPressureFactory.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,15 @@ def test_factory_metadata(self):
self.assertIsNone(factory.coord_system)
self.assertEqual(factory.attributes, {})

def test_promote_sigma_units_unknown_to_dimensionless(self):
sigma = mock.Mock(units=cf_units.Unit("unknown"), nbounds=0)
factory = HybridPressureFactory(
delta=self.delta,
sigma=sigma,
surface_air_pressure=self.surface_air_pressure,
)
self.assertEqual("1", factory.dependencies["sigma"].units)


class Test_dependencies(tests.IrisTest):
def setUp(self):
Expand Down
6 changes: 6 additions & 0 deletions lib/iris/tests/unit/aux_factory/test_OceanSFactory.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,12 @@ def test_depth_incompatible_units(self):
with self.assertRaises(ValueError):
OceanSFactory(**self.kwargs)

def test_promote_s_units_unknown_to_dimensionless(self):
s = mock.Mock(units=Unit("unknown"), nbounds=0)
self.kwargs["s"] = s
factory = OceanSFactory(**self.kwargs)
self.assertEqual("1", factory.dependencies["s"].units)


class Test_dependencies(tests.IrisTest):
def setUp(self):
Expand Down
9 changes: 9 additions & 0 deletions lib/iris/tests/unit/aux_factory/test_OceanSg1Factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,15 @@ def test_depth_incompatible_units(self):
with self.assertRaises(ValueError):
OceanSg1Factory(**self.kwargs)

def test_promote_c_and_s_units_unknown_to_dimensionless(self):
c = mock.Mock(units=Unit("unknown"), nbounds=0)
s = mock.Mock(units=Unit("unknown"), nbounds=0)
self.kwargs["c"] = c
self.kwargs["s"] = s
factory = OceanSg1Factory(**self.kwargs)
self.assertEqual("1", factory.dependencies["c"].units)
self.assertEqual("1", factory.dependencies["s"].units)


class Test_dependencies(tests.IrisTest):
def setUp(self):
Expand Down
9 changes: 9 additions & 0 deletions lib/iris/tests/unit/aux_factory/test_OceanSg2Factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,15 @@ def test_depth_incompatible_units(self):
with self.assertRaises(ValueError):
OceanSg2Factory(**self.kwargs)

def test_promote_c_and_s_units_unknown_to_dimensionless(self):
c = mock.Mock(units=Unit("unknown"), nbounds=0)
s = mock.Mock(units=Unit("unknown"), nbounds=0)
self.kwargs["c"] = c
self.kwargs["s"] = s
factory = OceanSg2Factory(**self.kwargs)
self.assertEqual("1", factory.dependencies["c"].units)
self.assertEqual("1", factory.dependencies["s"].units)


class Test_dependencies(tests.IrisTest):
def setUp(self):
Expand Down
6 changes: 6 additions & 0 deletions lib/iris/tests/unit/aux_factory/test_OceanSigmaFactory.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ def test_depth_incompatible_units(self):
with self.assertRaises(ValueError):
OceanSigmaFactory(**self.kwargs)

def test_promote_sigma_units_unknown_to_dimensionless(self):
sigma = mock.Mock(units=Unit("unknown"), nbounds=0)
self.kwargs["sigma"] = sigma
factory = OceanSigmaFactory(**self.kwargs)
self.assertEqual("1", factory.dependencies["sigma"].units)


class Test_dependencies(tests.IrisTest):
def setUp(self):
Expand Down
6 changes: 6 additions & 0 deletions lib/iris/tests/unit/aux_factory/test_OceanSigmaZFactory.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,12 @@ def test_depth_incompatible_units(self):
with self.assertRaises(ValueError):
OceanSigmaZFactory(**self.kwargs)

def test_promote_sigma_units_unknown_to_dimensionless(self):
sigma = mock.Mock(units=Unit("unknown"), nbounds=0)
self.kwargs["sigma"] = sigma
factory = OceanSigmaZFactory(**self.kwargs)
self.assertEqual("1", factory.dependencies["sigma"].units)


class Test_dependencies(tests.IrisTest):
def setUp(self):
Expand Down
41 changes: 39 additions & 2 deletions lib/iris/tests/unit/fileformats/netcdf/test__load_aux_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,44 @@ def test_formula_terms_ap(self):
self.assertEqual(factory.surface_air_pressure, self.ps)

def test_formula_terms_a_p0(self):
coord_a = DimCoord(np.arange(5), units="Pa")
coord_p0 = DimCoord(10, units="1")
coord_a = DimCoord(np.arange(5), units="1")
coord_p0 = DimCoord(10, units="Pa")
coord_expected = DimCoord(
np.arange(5) * 10,
units="Pa",
long_name="vertical pressure",
var_name="ap",
)
self.cube_parts["coordinates"].extend(
[(coord_a, "a"), (coord_p0, "p0")]
)
self.requires["formula_terms"] = dict(a="a", b="b", ps="ps", p0="p0")
_load_aux_factory(self.engine, self.cube)
# Check cube.coord_dims method.
self.assertEqual(self.cube.coord_dims.call_count, 1)
args, _ = self.cube.coord_dims.call_args
self.assertEqual(len(args), 1)
self.assertIs(args[0], coord_a)
# Check cube.add_aux_coord method.
self.assertEqual(self.cube.add_aux_coord.call_count, 1)
args, _ = self.cube.add_aux_coord.call_args
self.assertEqual(len(args), 2)
self.assertEqual(args[0], coord_expected)
self.assertIsInstance(args[1], mock.Mock)
# Check cube.add_aux_factory method.
self.assertEqual(self.cube.add_aux_factory.call_count, 1)
args, _ = self.cube.add_aux_factory.call_args
self.assertEqual(len(args), 1)
factory = args[0]
self.assertEqual(factory.delta, coord_expected)
self.assertEqual(factory.sigma, mock.sentinel.b)
self.assertEqual(factory.surface_air_pressure, self.ps)

def test_formula_terms_a_p0__promote_a_units_unknown_to_dimensionless(
self,
):
coord_a = DimCoord(np.arange(5), units="unknown")
coord_p0 = DimCoord(10, units="Pa")
coord_expected = DimCoord(
np.arange(5) * 10,
units="Pa",
Expand All @@ -71,6 +107,7 @@ def test_formula_terms_a_p0(self):
args, _ = self.cube.coord_dims.call_args
self.assertEqual(len(args), 1)
self.assertIs(args[0], coord_a)
self.assertEqual("1", args[0].units)
# Check cube.add_aux_coord method.
self.assertEqual(self.cube.add_aux_coord.call_count, 1)
args, _ = self.cube.add_aux_coord.call_args
Expand Down

0 comments on commit f468da3

Please sign in to comment.