From 2f0c67f9b58c43ce7725a225d3f9f011a8ef8245 Mon Sep 17 00:00:00 2001 From: "Sadie L. Bartholomew" Date: Thu, 25 Apr 2024 14:27:49 +0100 Subject: [PATCH 1/6] Update cyclic() docstring summary to include 'get' aspect --- cf/mixin/fielddomain.py | 2 +- cf/mixin/propertiesdata.py | 2 +- cf/mixin/propertiesdatabounds.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cf/mixin/fielddomain.py b/cf/mixin/fielddomain.py index ac57fb6784..9b0e8b7655 100644 --- a/cf/mixin/fielddomain.py +++ b/cf/mixin/fielddomain.py @@ -1909,7 +1909,7 @@ def coordinate_reference_domain_axes(self, identity=None): def cyclic( self, *identity, iscyclic=True, period=None, config={}, **filter_kwargs ): - """Set the cyclicity of an axis. + """Get or set the cyclicity of an axis. .. versionadded:: 1.0 diff --git a/cf/mixin/propertiesdata.py b/cf/mixin/propertiesdata.py index 80269e11e5..a756b4aafe 100644 --- a/cf/mixin/propertiesdata.py +++ b/cf/mixin/propertiesdata.py @@ -2934,7 +2934,7 @@ def count_masked(self): return data.count_masked() def cyclic(self, axes=None, iscyclic=True): - """Set the cyclicity of an axis. + """Get or set the cyclicity of an axis. .. seealso:: `iscyclic` diff --git a/cf/mixin/propertiesdatabounds.py b/cf/mixin/propertiesdatabounds.py index 15434785ce..685f177a94 100644 --- a/cf/mixin/propertiesdatabounds.py +++ b/cf/mixin/propertiesdatabounds.py @@ -1607,7 +1607,7 @@ def cos(self, bounds=True, inplace=False, i=False): ) def cyclic(self, axes=None, iscyclic=True): - """Set the cyclicity of axes of the data array. + """Get or set the cyclicity of axes of the data array. .. seealso:: `iscyclic` From 46cfb039c231ec5018a2555485d57f2ba7d02ebd Mon Sep 17 00:00:00 2001 From: "Sadie L. Bartholomew" Date: Thu, 25 Apr 2024 16:39:34 +0100 Subject: [PATCH 2/6] Add new test_Field method to unit test '(is)cyclic' --- cf/test/test_Field.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/cf/test/test_Field.py b/cf/test/test_Field.py index f3517fbf84..83ea979665 100644 --- a/cf/test/test_Field.py +++ b/cf/test/test_Field.py @@ -2859,6 +2859,31 @@ def test_Field_pad_missing(self): self.assertEqual(g.shape, (6, 11)) self.assertTrue(g[5, :].mask.all()) + def test_Field_cyclic_iscyclic(self): + """Test the `cyclic` and `iscyclic` Field methods.""" + f1 = cf.example_field(1) # no cyclic axes + f2 = cf.example_field(2) # one cyclic axis, 'domainaxis2' ('X') + + # Getting + self.assertEqual(f1.cyclic(), set()) + self.assertFalse(f1.iscyclic('X')) + self.assertFalse(f1.iscyclic('Y')) + self.assertFalse(f1.iscyclic('Z')) + self.assertFalse(f1.iscyclic('T')) + self.assertEqual(f2.cyclic(), set(("domainaxis2",))) + self.assertTrue(f2.iscyclic('X')) + self.assertFalse(f2.iscyclic('Y')) + self.assertFalse(f2.iscyclic('Z')) + self.assertFalse(f2.iscyclic('T')) + + # Setting + self.assertEqual( + f2.cyclic('X', iscyclic=False), set(("domainaxis2",))) + self.assertEqual(f2.cyclic(), set()) + self.assertEqual(f2.cyclic('X', period=360), set()) + self.assertEqual(f2.cyclic(), set(("domainaxis2",))) + self.assertTrue(f2.iscyclic('X')) + if __name__ == "__main__": print("Run date:", datetime.datetime.now()) From d4cf6fe2bcf14fc51632514428f755907a37f832 Mon Sep 17 00:00:00 2001 From: "Sadie L. Bartholomew" Date: Thu, 25 Apr 2024 16:42:57 +0100 Subject: [PATCH 3/6] Add new test_Domain method to unit test '(is)cyclic' --- cf/test/test_Domain.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/cf/test/test_Domain.py b/cf/test/test_Domain.py index 9e5566eab0..644a0056d4 100644 --- a/cf/test/test_Domain.py +++ b/cf/test/test_Domain.py @@ -428,6 +428,36 @@ def test_Domain_del_construct(self): self.assertIsInstance(e.del_construct("domainaxis2"), cf.DomainAxis) self.assertEqual(e.cyclic(), set()) + def test_Domain_cyclic_iscyclic(self): + """Test the `cyclic` and `iscyclic` Domain methods.""" + # A field and its domain should have the same cyclic() output. + f1 = cf.example_field(1) # no cyclic axes + d1 = f1.domain + f2 = cf.example_field(2) # one cyclic axis, 'domainaxis2' ('X') + d2 = f2.domain + + # Getting + self.assertEqual(d1.cyclic(), f1.cyclic()) + self.assertEqual(d1.cyclic(), set()) + self.assertFalse(d1.iscyclic('X')) + self.assertFalse(d1.iscyclic('Y')) + self.assertFalse(d1.iscyclic('Z')) + self.assertFalse(d1.iscyclic('T')) + self.assertEqual(d2.cyclic(), f2.cyclic()) + self.assertEqual(d2.cyclic(), set(("domainaxis2",))) + self.assertTrue(d2.iscyclic('X')) + self.assertFalse(d2.iscyclic('Y')) + self.assertFalse(d2.iscyclic('Z')) + self.assertFalse(d2.iscyclic('T')) + + # Setting + self.assertEqual( + d2.cyclic('X', iscyclic=False), set(("domainaxis2",))) + self.assertEqual(d2.cyclic(), set()) + self.assertEqual(d2.cyclic('X', period=360), set()) + self.assertEqual(d2.cyclic(), set(("domainaxis2",))) + self.assertTrue(d2.iscyclic('X')) + if __name__ == "__main__": print("Run date:", datetime.datetime.now()) From 2e6fac7065636dc2b83603a6dc64bb0f03fd2465 Mon Sep 17 00:00:00 2001 From: "Sadie L. Bartholomew" Date: Fri, 26 Apr 2024 11:13:47 +0100 Subject: [PATCH 4/6] Fix Issue 762 by accounting for cyclic axes w/ get_domain --- cf/field.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/cf/field.py b/cf/field.py index f46bc0fa0b..674b5a742f 100644 --- a/cf/field.py +++ b/cf/field.py @@ -2691,6 +2691,30 @@ def cfa_update_file_substitutions( for c in self.constructs.filter_by_data(todict=True).values(): c.cfa_update_file_substitutions(substitutions) + def get_domain(self): + """Return the domain. + + .. versionadded:: NEXTVERSION + + .. seealso:: `domain` + + :Returns: + + `Domain` + The domain. + + **Examples** + + >>> d = f.get_domain() + + """ + domain = super().get_domain() + + # Set axis cyclicity for the domain + domain._cyclic = self._cyclic + + return domain + def radius(self, default=None): """Return the radius of a latitude-longitude plane defined in spherical polar coordinates. From 4fabd9413c71a0015f542ed0bc0e566226cc030a Mon Sep 17 00:00:00 2001 From: "Sadie L. Bartholomew" Date: Fri, 26 Apr 2024 11:23:54 +0100 Subject: [PATCH 5/6] Update Changelog for PR for Issue 762 --- Changelog.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changelog.rst b/Changelog.rst index 3f16f934d8..1bc6ade20c 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -35,6 +35,9 @@ version 3.16.2 * Fix bug whereby `Field.cyclic` is not updated after a `Field.del_construct` operation (https://github.com/NCAS-CMS/cf-python/issues/758) +* Fix bug that meant `cyclic()` always returned an empty + set for domains produced by `cf.Field.domain` + (https://github.com/NCAS-CMS/cf-python/issues/762) ---- From e7088b82df27b7d0ba1a9bb1577d1d2087f341f0 Mon Sep 17 00:00:00 2001 From: "Sadie L. Bartholomew" Date: Fri, 26 Apr 2024 11:28:40 +0100 Subject: [PATCH 6/6] Apply linting to files touched for PR --- cf/field.py | 6 +++--- cf/test/test_Domain.py | 23 +++++++++++------------ cf/test/test_Field.py | 23 +++++++++++------------ 3 files changed, 25 insertions(+), 27 deletions(-) diff --git a/cf/field.py b/cf/field.py index 674b5a742f..879a549acc 100644 --- a/cf/field.py +++ b/cf/field.py @@ -10479,9 +10479,9 @@ def convolution_filter( new_bounds[0 : length - lower_offset, 1:] = old_bounds[ lower_offset:length, 1: ] - new_bounds[ - length - lower_offset : length, 1: - ] = old_bounds[length - 1, 1:] + new_bounds[length - lower_offset : length, 1:] = ( + old_bounds[length - 1, 1:] + ) coord.set_bounds(self._Bounds(data=new_bounds)) diff --git a/cf/test/test_Domain.py b/cf/test/test_Domain.py index 644a0056d4..63e639df4a 100644 --- a/cf/test/test_Domain.py +++ b/cf/test/test_Domain.py @@ -439,24 +439,23 @@ def test_Domain_cyclic_iscyclic(self): # Getting self.assertEqual(d1.cyclic(), f1.cyclic()) self.assertEqual(d1.cyclic(), set()) - self.assertFalse(d1.iscyclic('X')) - self.assertFalse(d1.iscyclic('Y')) - self.assertFalse(d1.iscyclic('Z')) - self.assertFalse(d1.iscyclic('T')) + self.assertFalse(d1.iscyclic("X")) + self.assertFalse(d1.iscyclic("Y")) + self.assertFalse(d1.iscyclic("Z")) + self.assertFalse(d1.iscyclic("T")) self.assertEqual(d2.cyclic(), f2.cyclic()) self.assertEqual(d2.cyclic(), set(("domainaxis2",))) - self.assertTrue(d2.iscyclic('X')) - self.assertFalse(d2.iscyclic('Y')) - self.assertFalse(d2.iscyclic('Z')) - self.assertFalse(d2.iscyclic('T')) + self.assertTrue(d2.iscyclic("X")) + self.assertFalse(d2.iscyclic("Y")) + self.assertFalse(d2.iscyclic("Z")) + self.assertFalse(d2.iscyclic("T")) # Setting - self.assertEqual( - d2.cyclic('X', iscyclic=False), set(("domainaxis2",))) + self.assertEqual(d2.cyclic("X", iscyclic=False), set(("domainaxis2",))) self.assertEqual(d2.cyclic(), set()) - self.assertEqual(d2.cyclic('X', period=360), set()) + self.assertEqual(d2.cyclic("X", period=360), set()) self.assertEqual(d2.cyclic(), set(("domainaxis2",))) - self.assertTrue(d2.iscyclic('X')) + self.assertTrue(d2.iscyclic("X")) if __name__ == "__main__": diff --git a/cf/test/test_Field.py b/cf/test/test_Field.py index 83ea979665..62048773e2 100644 --- a/cf/test/test_Field.py +++ b/cf/test/test_Field.py @@ -2866,23 +2866,22 @@ def test_Field_cyclic_iscyclic(self): # Getting self.assertEqual(f1.cyclic(), set()) - self.assertFalse(f1.iscyclic('X')) - self.assertFalse(f1.iscyclic('Y')) - self.assertFalse(f1.iscyclic('Z')) - self.assertFalse(f1.iscyclic('T')) + self.assertFalse(f1.iscyclic("X")) + self.assertFalse(f1.iscyclic("Y")) + self.assertFalse(f1.iscyclic("Z")) + self.assertFalse(f1.iscyclic("T")) self.assertEqual(f2.cyclic(), set(("domainaxis2",))) - self.assertTrue(f2.iscyclic('X')) - self.assertFalse(f2.iscyclic('Y')) - self.assertFalse(f2.iscyclic('Z')) - self.assertFalse(f2.iscyclic('T')) + self.assertTrue(f2.iscyclic("X")) + self.assertFalse(f2.iscyclic("Y")) + self.assertFalse(f2.iscyclic("Z")) + self.assertFalse(f2.iscyclic("T")) # Setting - self.assertEqual( - f2.cyclic('X', iscyclic=False), set(("domainaxis2",))) + self.assertEqual(f2.cyclic("X", iscyclic=False), set(("domainaxis2",))) self.assertEqual(f2.cyclic(), set()) - self.assertEqual(f2.cyclic('X', period=360), set()) + self.assertEqual(f2.cyclic("X", period=360), set()) self.assertEqual(f2.cyclic(), set(("domainaxis2",))) - self.assertTrue(f2.iscyclic('X')) + self.assertTrue(f2.iscyclic("X")) if __name__ == "__main__":