From f42d10bbca7120d561a56d3d35a44c8ce81e29f6 Mon Sep 17 00:00:00 2001 From: David Hassell Date: Wed, 5 Mar 2025 17:04:16 +0000 Subject: [PATCH 1/3] dev --- cf/field.py | 82 ++++++++++++++++++++--------------------- cf/mixin/fielddomain.py | 62 ++++++++++++++++++++++++++----- cf/test/test_Field.py | 32 +++++++++++++++- 3 files changed, 123 insertions(+), 53 deletions(-) diff --git a/cf/field.py b/cf/field.py index b576981c5b..d8acc14a8d 100644 --- a/cf/field.py +++ b/cf/field.py @@ -3006,47 +3006,47 @@ def close(self): removed_at="5.0.0", ) # pragma: no cover - def iscyclic(self, *identity, **filter_kwargs): - """Returns True if the specified axis is cyclic. - - .. versionadded:: 1.0 - - .. seealso:: `axis`, `cyclic`, `period`, `domain_axis` - - :Parameters: - - identity, filter_kwargs: optional - Select the unique domain axis construct returned by - ``f.domain_axis(*identity, **filter_kwargs)``. See - `domain_axis` for details. - - :Returns: - - `bool` - True if the selected axis is cyclic, otherwise False. - - **Examples** - - >>> f.iscyclic('X') - True - >>> f.iscyclic('latitude') - False - - >>> x = f.iscyclic('long_name=Latitude') - >>> x = f.iscyclic('dimensioncoordinate1') - >>> x = f.iscyclic('domainaxis2') - >>> x = f.iscyclic('key%domainaxis2') - >>> x = f.iscyclic('ncdim%y') - >>> x = f.iscyclic(2) - - """ - axis = self.domain_axis( - *identity, key=True, default=None, **filter_kwargs - ) - if axis is None: - raise ValueError("Can't identify unique domain axis") - - return axis in self.cyclic() + # def iscyclic(self, *identity, **filter_kwargs): + # """Returns True if the specified axis is cyclic. + # + # .. versionadded:: 1.0 + # + # .. seealso:: `axis`, `cyclic`, `period`, `domain_axis` + # + # :Parameters: + # + # identity, filter_kwargs: optional + # Select the unique domain axis construct returned by + # ``f.domain_axis(*identity, **filter_kwargs)``. See + # `domain_axis` for details. + # + # :Returns: + # + # `bool` + # True if the selected axis is cyclic, otherwise False. + # + # **Examples** + # + # >>> f.iscyclic('X') + # True + # >>> f.iscyclic('latitude') + # False + # + # >>> x = f.iscyclic('long_name=Latitude') + # >>> x = f.iscyclic('dimensioncoordinate1') + # >>> x = f.iscyclic('domainaxis2') + # >>> x = f.iscyclic('key%domainaxis2') + # >>> x = f.iscyclic('ncdim%y') + # >>> x = f.iscyclic(2) + # + # """ + # axis = self.domain_axis( + # *identity, key=True, default=None, **filter_kwargs + # ) + # if axis is None: + # raise ValueError("Can't identify unique domain axis") + # + # return axis in self.cyclic() def weights( self, diff --git a/cf/mixin/fielddomain.py b/cf/mixin/fielddomain.py index ac7cf16ca0..f089e52104 100644 --- a/cf/mixin/fielddomain.py +++ b/cf/mixin/fielddomain.py @@ -1345,10 +1345,13 @@ def autocyclic(self, key=None, coord=None, verbose=None, config={}): # Don't do anything return + dry_run = config.get("dry_run") + if "cyclic" in config: if not config["cyclic"]: - if not noop: + if not dry_run: self.cyclic(key, iscyclic=False, config=config) + return False else: period = coord.period() @@ -1357,23 +1360,45 @@ def autocyclic(self, key=None, coord=None, verbose=None, config={}): else: period = config.get("period") - self.cyclic(key, iscyclic=True, period=period, config=config) + if not dry_run: + self.cyclic( + key, iscyclic=True, period=period, config=config + ) + return True +# if axis is not None: +# if coord is not None: +# raise ValueError("TODO") +# +# key, coord = self.dimension_coordinate( +# filter_by_axis=(axis,), item=True, default=(None, None) +# ) +# if coord is None: +# if not dry_run: +# self.cyclic(key, iscyclic=False, config=config) +# +# return False + if coord is None: key, coord = self.dimension_coordinate( "X", item=True, default=(None, None) ) if coord is None: + if not dry_run: + self.cyclic(key, iscyclic=False, config=config) + return False elif "X" in config: if not config["X"]: - if not noop: + if not dry_run: self.cyclic(key, iscyclic=False, config=config) + return False elif not coord.X: - if not noop: + if not dry_run: self.cyclic(key, iscyclic=False, config=config) + return False bounds_range = config.get("bounds_range") @@ -1382,14 +1407,16 @@ def autocyclic(self, key=None, coord=None, verbose=None, config={}): else: bounds = coord.get_bounds(None) if bounds is None: - if not noop: + if not dry_run: self.cyclic(key, iscyclic=False, config=config) + return False data = bounds.get_data(None, _fill_value=False) if data is None: - if not noop: + if not dry_run: self.cyclic(key, iscyclic=False, config=config) + return False bounds_units = bounds.Units @@ -1411,7 +1438,9 @@ def autocyclic(self, key=None, coord=None, verbose=None, config={}): elif bounds_units.equivalent(_units_degrees): period = Data(360.0, units="degrees") else: - self.cyclic(key, iscyclic=False, config=config) + if not dry_run: + self.cyclic(key, iscyclic=False, config=config) + return False period.Units = bounds_units @@ -1422,14 +1451,16 @@ def autocyclic(self, key=None, coord=None, verbose=None, config={}): bounds_range = None if bounds_range is None or bounds_range != period: - if not noop: + if not dry_run: self.cyclic(key, iscyclic=False, config=config) + return False config = config.copy() config["axis"] = self.get_data_axes(key, default=(None,))[0] - self.cyclic(key, iscyclic=True, period=period, config=config) + if not dry_run: + self.cyclic(key, iscyclic=True, period=period, config=config) return True @@ -1885,6 +1916,7 @@ def cyclic( coordinates, otherwise it is assumed to have the same units as the dimension coordinates. + config: `dict`, optional Additional parameters for optimising the operation. See the code for details. @@ -1923,7 +1955,15 @@ def cyclic( cyclic = self._cyclic if not identity and not filter_kwargs: - return cyclic.copy() + cyclic = cyclic.copy() + if ( + len(cyclic) < len(self.domain_axes(todict=True)) + and self.autocyclic() + ): + cyclic = cyclic.update(self._cyclic) + self._cyclic = cyclic + + return cyclic axis = config.get("axis") if axis is None: @@ -2251,6 +2291,8 @@ def get_coordinate_reference( def iscyclic(self, *identity, **filter_kwargs): """Returns True if the given axis is cyclic. + If + {{unique construct}} .. versionadded:: 1.0 diff --git a/cf/test/test_Field.py b/cf/test/test_Field.py index c1122ac338..03f87c7a46 100644 --- a/cf/test/test_Field.py +++ b/cf/test/test_Field.py @@ -2970,9 +2970,37 @@ def test_Field_cyclic_iscyclic(self): # 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",))) + + f2.cyclic("Y", period=360), set() + self.assertEqual( + f2.cyclic(), + set( + ( + "domainaxis1", + "domainaxis2", + ) + ), + ) + self.assertTrue(f2.iscyclic("Y")) + self.assertEqual( + f2.cyclic("Y", iscyclic=False), + set( + ( + "domainaxis1", + "domainaxis2", + ) + ), + ) + self.assertEqual(f2.cyclic(), set(("domainaxis2",))) + + # Auto setting of cyclicity + self.assertTrue(f2.iscyclic("X")) + f2.cyclic("X", iscyclic=False) + self.assertFalse(f2._cyclic) + self.assertEqual(f2.cyclic(), set(("domainaxis2",))) + + f2.cyclic("X", iscyclic=False) self.assertTrue(f2.iscyclic("X")) def test_Field_is_discrete_axis(self): From 14d7ba132a9dcb1d771e560a176555113efb97fb Mon Sep 17 00:00:00 2001 From: David Hassell Date: Fri, 7 Mar 2025 17:50:06 +0000 Subject: [PATCH 2/3] dev --- cf/mixin/fielddomain.py | 33 ++++++++++----------------------- cf/test/test_Domain.py | 37 +++++++++++++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 27 deletions(-) diff --git a/cf/mixin/fielddomain.py b/cf/mixin/fielddomain.py index f089e52104..ac69a970ed 100644 --- a/cf/mixin/fielddomain.py +++ b/cf/mixin/fielddomain.py @@ -1345,6 +1345,7 @@ def autocyclic(self, key=None, coord=None, verbose=None, config={}): # Don't do anything return + # On a dry run, return as usual, but don't update _cyclic. dry_run = config.get("dry_run") if "cyclic" in config: @@ -1364,31 +1365,16 @@ def autocyclic(self, key=None, coord=None, verbose=None, config={}): self.cyclic( key, iscyclic=True, period=period, config=config ) - + return True -# if axis is not None: -# if coord is not None: -# raise ValueError("TODO") -# -# key, coord = self.dimension_coordinate( -# filter_by_axis=(axis,), item=True, default=(None, None) -# ) -# if coord is None: -# if not dry_run: -# self.cyclic(key, iscyclic=False, config=config) -# -# return False - if coord is None: key, coord = self.dimension_coordinate( "X", item=True, default=(None, None) ) if coord is None: - if not dry_run: - self.cyclic(key, iscyclic=False, config=config) - return False + elif "X" in config: if not config["X"]: if not dry_run: @@ -1955,12 +1941,15 @@ def cyclic( cyclic = self._cyclic if not identity and not filter_kwargs: - cyclic = cyclic.copy() + cyclic = cyclic.copy() + + # Check for axes that are currently marked as non-cyclic, + # but are in fact cyclic. if ( - len(cyclic) < len(self.domain_axes(todict=True)) - and self.autocyclic() + len(cyclic) < len(self.domain_axes(todict=True)) + and self.autocyclic() ): - cyclic = cyclic.update(self._cyclic) + cyclic.update(self._cyclic) self._cyclic = cyclic return cyclic @@ -2291,8 +2280,6 @@ def get_coordinate_reference( def iscyclic(self, *identity, **filter_kwargs): """Returns True if the given axis is cyclic. - If - {{unique construct}} .. versionadded:: 1.0 diff --git a/cf/test/test_Domain.py b/cf/test/test_Domain.py index 63e639df4a..c42d806574 100644 --- a/cf/test/test_Domain.py +++ b/cf/test/test_Domain.py @@ -437,14 +437,15 @@ def test_Domain_cyclic_iscyclic(self): d2 = f2.domain # Getting - self.assertEqual(d1.cyclic(), f1.cyclic()) self.assertEqual(d1.cyclic(), set()) + self.assertEqual(d1.cyclic(), f1.cyclic()) 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.assertEqual(d2.cyclic(), f2.cyclic()) self.assertTrue(d2.iscyclic("X")) self.assertFalse(d2.iscyclic("Y")) self.assertFalse(d2.iscyclic("Z")) @@ -452,9 +453,37 @@ def test_Domain_cyclic_iscyclic(self): # 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",))) + + d2.cyclic("Y", period=360), set() + self.assertEqual( + d2.cyclic(), + set( + ( + "domainaxis1", + "domainaxis2", + ) + ), + ) + self.assertTrue(d2.iscyclic("Y")) + self.assertEqual( + d2.cyclic("Y", iscyclic=False), + set( + ( + "domainaxis1", + "domainaxis2", + ) + ), + ) + self.assertEqual(d2.cyclic(), set(("domainaxis2",))) + + # Auto setting of cyclicity + self.assertTrue(d2.iscyclic("X")) + d2.cyclic("X", iscyclic=False) + self.assertFalse(d2._cyclic) + self.assertEqual(d2.cyclic(), set(("domainaxis2",))) + + d2.cyclic("X", iscyclic=False) self.assertTrue(d2.iscyclic("X")) From 9ccb894b56e85b067bd79e31f43873398ad572e3 Mon Sep 17 00:00:00 2001 From: David Hassell Date: Fri, 7 Mar 2025 17:51:41 +0000 Subject: [PATCH 3/3] dev --- cf/field.py | 42 ------------------------------------------ 1 file changed, 42 deletions(-) diff --git a/cf/field.py b/cf/field.py index d8acc14a8d..9ef41377b7 100644 --- a/cf/field.py +++ b/cf/field.py @@ -3006,48 +3006,6 @@ def close(self): removed_at="5.0.0", ) # pragma: no cover - # def iscyclic(self, *identity, **filter_kwargs): - # """Returns True if the specified axis is cyclic. - # - # .. versionadded:: 1.0 - # - # .. seealso:: `axis`, `cyclic`, `period`, `domain_axis` - # - # :Parameters: - # - # identity, filter_kwargs: optional - # Select the unique domain axis construct returned by - # ``f.domain_axis(*identity, **filter_kwargs)``. See - # `domain_axis` for details. - # - # :Returns: - # - # `bool` - # True if the selected axis is cyclic, otherwise False. - # - # **Examples** - # - # >>> f.iscyclic('X') - # True - # >>> f.iscyclic('latitude') - # False - # - # >>> x = f.iscyclic('long_name=Latitude') - # >>> x = f.iscyclic('dimensioncoordinate1') - # >>> x = f.iscyclic('domainaxis2') - # >>> x = f.iscyclic('key%domainaxis2') - # >>> x = f.iscyclic('ncdim%y') - # >>> x = f.iscyclic(2) - # - # """ - # axis = self.domain_axis( - # *identity, key=True, default=None, **filter_kwargs - # ) - # if axis is None: - # raise ValueError("Can't identify unique domain axis") - # - # return axis in self.cyclic() - def weights( self, weights=True,