Repository: NCAS-CMS/cf-python

Issue:
`cf.aggregate` fails when creating new axes with the `dimension` keyword
At v3.11.0 (and all other versions since 3.0.0) , `cf.aggregate` fails when creating new axes for each input field from one or more given properties, using the `dimension` keyword argument:

```python
>>> import cf
>>> print (cf.__version__)
3.11.0
>>> f = cf.example_field(0)
>>> g = f.copy()
>>> f.set_property('sim', 'r1i1p1f1')
>>> g.set_property('sim', 'r2i1p1f1')
>>> a = cf.aggregate([f, g], dimension='sim')
Traceback (most recent call last):
  File "agg.py", line 20, in <module>
    a = cf.aggregate([f, g], dimension='sim')[0]
  File "/home/david/NCAS-CMS/cfdm/cfdm/decorators.py", line 171, in verbose_override_wrapper
    return method_with_verbose_kwarg(*args, **kwargs)
  File "/home/david/cf-python/cf/aggregate.py", line 1721, in aggregate
    meta = _Meta(
  File "/home/david/cf-python/cf/aggregate.py", line 355, in __init__
    aux_coords = {
  File "/home/david/cf-python/cf/aggregate.py", line 358, in <dictcomp>
    if axis in construct_axes[aux]
KeyError: 'auxiliarycoordinate0'
```

This is simply a bug in code that was not covered by the units tests. PR fix to follow.

Thank you to @bnlawrence for originally spotting this.

Discussion:

Patch:
diff --git a/Changelog.rst b/Changelog.rst
index 807c8ffd0..0ef1437a7 100644
--- a/Changelog.rst
+++ b/Changelog.rst
@@ -1,3 +1,13 @@
+version NEXT
+------------
+
+**????-??-??**
+
+* Fix `cf.aggregate` failures when using the ``dimension`` keyword
+  parameter (https://github.com/NCAS-CMS/cf-python/issues/283)
+
+----
+
 version 3.11.0
 --------------
 
diff --git a/cf/aggregate.py b/cf/aggregate.py
index 1a3bcd712..39d3a2c10 100644
--- a/cf/aggregate.py
+++ b/cf/aggregate.py
@@ -138,6 +138,7 @@ class _Meta:
         relaxed_identities=False,
         ncvar_identities=False,
         field_identity=None,
+        copy=True,
     ):
         """**initialization**
 
@@ -163,7 +164,8 @@ class _Meta:
             dimension: (sequence of) `str`, optional
                 See the `aggregate` function for details.
 
-        **Examples:**
+            copy: `bool` optional
+                See the `aggregate` function for details.
 
         """
         self._bool = False
@@ -245,6 +247,7 @@ class _Meta:
         # Promote selected properties to 1-d, size 1 auxiliary
         # coordinates
         # ------------------------------------------------------------
+        _copy = copy
         for prop in dimension:
             value = f.get_property(prop, None)
             if value is None:
@@ -258,10 +261,19 @@ class _Meta:
             aux_coord.nc_set_variable(prop)
             aux_coord.id = prop
 
+            if _copy:
+                # Copy the field, as we're about to change it.
+                f = f.copy()
+                self.field = f
+                _copy = False
+
             axis = f.set_construct(DomainAxis(1))
             f.set_construct(aux_coord, axes=[axis], copy=False)
 
-            f.del_property(prop)  # dch COPY issue?
+            f.del_property(prop)
+
+        if dimension:
+            construct_axes = f.constructs.data_axes()
 
         self.units = self.canonical_units(
             f, self.identity, relaxed_units=relaxed_units
@@ -1725,6 +1737,7 @@ def aggregate(
             ncvar_identities=ncvar_identities,
             field_identity=field_identity,
             respect_valid=respect_valid,
+            copy=copy,
         )
 
         if not meta:


Test patch:
diff --git a/cf/test/test_aggregate.py b/cf/test/test_aggregate.py
index 7ce83601d..2679eb97a 100644
--- a/cf/test/test_aggregate.py
+++ b/cf/test/test_aggregate.py
@@ -255,6 +255,22 @@ class aggregateTest(unittest.TestCase):
 
         self.assertEqual(len(x), 1, x)
 
+    def test_aggregate_dimension(self):
+        """Test the promotion of property to axis."""
+        f = cf.example_field(0)
+        g = f.copy()
+
+        f.set_property("sim", "r1i1p1f1")
+        g.set_property("sim", "r2i1p1f1")
+
+        self.assertFalse(len(f.auxiliary_coordinates()))
+
+        a = cf.aggregate([f, g], dimension="sim")
+        self.assertEqual(len(a), 1)
+
+        a = a[0]
+        self.assertEqual(len(a.auxiliary_coordinates()), 1)
+
 
 if __name__ == "__main__":
     print("Run date:", datetime.datetime.now())