Repository: NCAS-CMS/cf-python

Issue:
Creating time bounds - differing time units causes uncaptured error when collapsing
Hi Both!

  Another minor issue - it arose when working with some datasets that use an auxiliary time axis, I have a work-around but I thought it was worth highlighting as it causes errors in `collapse` that are not captured.

If I create a field with 2 time points, but no bounds, then I create month-wide bounds using cf.M() and then add these to the field.
I get a `cf.field` that has a time coordinate that looks like this:

```
Dimension coordinate: time
    calendar = '360_day'
    long_name = 'Time axis'
    standard_name = 'time'
    time_origin = '1900-01-01 00:00:00'
    units = 'seconds since 1900-01-01 00:00:00'
    Data(time(2)) = [1851-06-01 00:00:00, 1852-06-01 00:00:00] 360_day
    Bounds:calendar = '360_day'
    Bounds:units = 'days since 1851-6-1'
    Bounds:Data(time(2), 2) = [[1851-06-01 00:00:00, ..., 1852-07-01 00:00:00]] 360_day

```

As you can see, the time dimension has units of seconds, but the bounds has units of days.

If I do `field.collapse('time: mean')` I get:

```
Dimension coordinate: time
    calendar = '360_day'
    long_name = 'Time axis'
    standard_name = 'time'
    time_origin = '1900-01-01 00:00:00'
    units = 'seconds since 1900-01-01 00:00:00'
    Data(time(1)) = [1900-01-01 00:03:15] 360_day
    Bounds:calendar = '360_day'
    Bounds:units = 'seconds since 1900-01-01 00:00:00'
    Bounds:Data(time(1), 2) = [[1900-01-01 00:00:00, 1900-01-01 00:06:30]] 360_day

```

Clearly something has gone wrong here!

It seems that the bounds data (in days) has been used, but the time units (seconds) have been retained:

```
time_mean.coord('time').array
array([195.])
```

The attached code reproduces this behaviour.
[time_bounds_bug2.py.gz](https://github.com/NCAS-CMS/cf-python/files/8975955/time_bounds_bug2.py.gz)

This is probably not a major bug, most people are not going to create bounds like this! But I thought I'd draw it to you attention, just in case it might cause other problems.

Cheers,
Dan







Discussion:

Patch:
diff --git a/Changelog.rst b/Changelog.rst
index 94db42567..e62e85d31 100644
--- a/Changelog.rst
+++ b/Changelog.rst
@@ -1,3 +1,13 @@
+version 3.13.1
+--------------
+
+**2022-??-??**
+
+* Fix bug when setting coordinate bounds with different units to their
+  parent coordinates
+  (https://github.com/NCAS-CMS/cf-python/issues/417)
+
+----
 version 3.13.0
 --------------
 
diff --git a/cf/__init__.py b/cf/__init__.py
index 16543689e..e70621265 100644
--- a/cf/__init__.py
+++ b/cf/__init__.py
@@ -74,8 +74,8 @@ installation and source code.
 
 """
 __Conventions__ = "CF-1.9"
-__date__ = "2022-06-23"
-__version__ = "3.13.0"
+__date__ = "2022-??-??"
+__version__ = "3.13.1"
 
 _requires = (
     "numpy",
diff --git a/cf/mixin/propertiesdatabounds.py b/cf/mixin/propertiesdatabounds.py
index 6a66d4a20..003397a24 100644
--- a/cf/mixin/propertiesdatabounds.py
+++ b/cf/mixin/propertiesdatabounds.py
@@ -2625,11 +2625,13 @@ class PropertiesDataBounds(PropertiesData):
         units = bounds.Units
         self_units = self.Units
 
-        if data is not None and units and not units.equivalent(self_units):
-            raise ValueError(
-                f"Can't set bounds: {bounds!r} units of {bounds.Units!r} are "
-                f"not equivalent to {self.Units!r}, the units of {self!r}"
-            )
+        if data is not None and units:
+            if not units.equivalent(self_units):
+                raise ValueError(
+                    f"Can't set bounds: {bounds!r} units of {bounds.Units!r} "
+                    f"are not equivalent to {self.Units!r}, the units of "
+                    f"{self!r}"
+                )
 
             bounds.Units = self_units
 


Test patch:
diff --git a/cf/test/test_DimensionCoordinate.py b/cf/test/test_DimensionCoordinate.py
index f82cdbf89..4a3140bf6 100644
--- a/cf/test/test_DimensionCoordinate.py
+++ b/cf/test/test_DimensionCoordinate.py
@@ -462,6 +462,28 @@ class DimensionCoordinateTest(unittest.TestCase):
         self.assertTrue(d.data.equals(-e.data, verbose=3))
         self.assertTrue(d.bounds.equals(self.dim.bounds, verbose=3))
 
+    def test_DimensionCoordinate_set_bounds(self):
+        c = cf.DimensionCoordinate()
+        data = cf.Data(
+            [1296000.0], units="seconds since 1900-01-01", calendar="360_day"
+        )
+        c.set_data(data)
+
+        bounds = cf.Bounds()
+        data = cf.Data(
+            [[0.0, 30]], units="days since 1900-01-01", calendar="360_day"
+        )
+        bounds.set_data(data)
+
+        c.set_bounds(bounds)
+
+        self.assertEqual(
+            c.bounds.Units,
+            cf.Units(units="seconds since 1900-01-01", calendar="360_day"),
+        )
+
+        self.assertTrue((c.bounds.data.array == [0, 2592000]).all())
+
 
 if __name__ == "__main__":
     print("Run date:", datetime.datetime.now())