Skip to content

Commit f2e0d41

Browse files
authored
Merge pull request #852 from davidhassell/cyclic
Improve detection of cyclic axes
2 parents d7c7293 + 9ccb894 commit f2e0d41

File tree

4 files changed

+102
-58
lines changed

4 files changed

+102
-58
lines changed

cf/field.py

Lines changed: 0 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3006,48 +3006,6 @@ def close(self):
30063006
removed_at="5.0.0",
30073007
) # pragma: no cover
30083008

3009-
def iscyclic(self, *identity, **filter_kwargs):
3010-
"""Returns True if the specified axis is cyclic.
3011-
3012-
.. versionadded:: 1.0
3013-
3014-
.. seealso:: `axis`, `cyclic`, `period`, `domain_axis`
3015-
3016-
:Parameters:
3017-
3018-
identity, filter_kwargs: optional
3019-
Select the unique domain axis construct returned by
3020-
``f.domain_axis(*identity, **filter_kwargs)``. See
3021-
`domain_axis` for details.
3022-
3023-
:Returns:
3024-
3025-
`bool`
3026-
True if the selected axis is cyclic, otherwise False.
3027-
3028-
**Examples**
3029-
3030-
>>> f.iscyclic('X')
3031-
True
3032-
>>> f.iscyclic('latitude')
3033-
False
3034-
3035-
>>> x = f.iscyclic('long_name=Latitude')
3036-
>>> x = f.iscyclic('dimensioncoordinate1')
3037-
>>> x = f.iscyclic('domainaxis2')
3038-
>>> x = f.iscyclic('key%domainaxis2')
3039-
>>> x = f.iscyclic('ncdim%y')
3040-
>>> x = f.iscyclic(2)
3041-
3042-
"""
3043-
axis = self.domain_axis(
3044-
*identity, key=True, default=None, **filter_kwargs
3045-
)
3046-
if axis is None:
3047-
raise ValueError("Can't identify unique domain axis")
3048-
3049-
return axis in self.cyclic()
3050-
30513009
def weights(
30523010
self,
30533011
weights=True,

cf/mixin/fielddomain.py

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,10 +1345,14 @@ def autocyclic(self, key=None, coord=None, verbose=None, config={}):
13451345
# Don't do anything
13461346
return
13471347

1348+
# On a dry run, return as usual, but don't update _cyclic.
1349+
dry_run = config.get("dry_run")
1350+
13481351
if "cyclic" in config:
13491352
if not config["cyclic"]:
1350-
if not noop:
1353+
if not dry_run:
13511354
self.cyclic(key, iscyclic=False, config=config)
1355+
13521356
return False
13531357
else:
13541358
period = coord.period()
@@ -1357,7 +1361,11 @@ def autocyclic(self, key=None, coord=None, verbose=None, config={}):
13571361
else:
13581362
period = config.get("period")
13591363

1360-
self.cyclic(key, iscyclic=True, period=period, config=config)
1364+
if not dry_run:
1365+
self.cyclic(
1366+
key, iscyclic=True, period=period, config=config
1367+
)
1368+
13611369
return True
13621370

13631371
if coord is None:
@@ -1366,14 +1374,17 @@ def autocyclic(self, key=None, coord=None, verbose=None, config={}):
13661374
)
13671375
if coord is None:
13681376
return False
1377+
13691378
elif "X" in config:
13701379
if not config["X"]:
1371-
if not noop:
1380+
if not dry_run:
13721381
self.cyclic(key, iscyclic=False, config=config)
1382+
13731383
return False
13741384
elif not coord.X:
1375-
if not noop:
1385+
if not dry_run:
13761386
self.cyclic(key, iscyclic=False, config=config)
1387+
13771388
return False
13781389

13791390
bounds_range = config.get("bounds_range")
@@ -1382,14 +1393,16 @@ def autocyclic(self, key=None, coord=None, verbose=None, config={}):
13821393
else:
13831394
bounds = coord.get_bounds(None)
13841395
if bounds is None:
1385-
if not noop:
1396+
if not dry_run:
13861397
self.cyclic(key, iscyclic=False, config=config)
1398+
13871399
return False
13881400

13891401
data = bounds.get_data(None, _fill_value=False)
13901402
if data is None:
1391-
if not noop:
1403+
if not dry_run:
13921404
self.cyclic(key, iscyclic=False, config=config)
1405+
13931406
return False
13941407

13951408
bounds_units = bounds.Units
@@ -1411,7 +1424,9 @@ def autocyclic(self, key=None, coord=None, verbose=None, config={}):
14111424
elif bounds_units.equivalent(_units_degrees):
14121425
period = Data(360.0, units="degrees")
14131426
else:
1414-
self.cyclic(key, iscyclic=False, config=config)
1427+
if not dry_run:
1428+
self.cyclic(key, iscyclic=False, config=config)
1429+
14151430
return False
14161431

14171432
period.Units = bounds_units
@@ -1422,14 +1437,16 @@ def autocyclic(self, key=None, coord=None, verbose=None, config={}):
14221437
bounds_range = None
14231438

14241439
if bounds_range is None or bounds_range != period:
1425-
if not noop:
1440+
if not dry_run:
14261441
self.cyclic(key, iscyclic=False, config=config)
1442+
14271443
return False
14281444

14291445
config = config.copy()
14301446
config["axis"] = self.get_data_axes(key, default=(None,))[0]
14311447

1432-
self.cyclic(key, iscyclic=True, period=period, config=config)
1448+
if not dry_run:
1449+
self.cyclic(key, iscyclic=True, period=period, config=config)
14331450

14341451
return True
14351452

@@ -1885,6 +1902,7 @@ def cyclic(
18851902
coordinates, otherwise it is assumed to have the same
18861903
units as the dimension coordinates.
18871904
1905+
18881906
config: `dict`, optional
18891907
Additional parameters for optimising the
18901908
operation. See the code for details.
@@ -1923,7 +1941,18 @@ def cyclic(
19231941
cyclic = self._cyclic
19241942

19251943
if not identity and not filter_kwargs:
1926-
return cyclic.copy()
1944+
cyclic = cyclic.copy()
1945+
1946+
# Check for axes that are currently marked as non-cyclic,
1947+
# but are in fact cyclic.
1948+
if (
1949+
len(cyclic) < len(self.domain_axes(todict=True))
1950+
and self.autocyclic()
1951+
):
1952+
cyclic.update(self._cyclic)
1953+
self._cyclic = cyclic
1954+
1955+
return cyclic
19271956

19281957
axis = config.get("axis")
19291958
if axis is None:

cf/test/test_Domain.py

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -437,24 +437,53 @@ def test_Domain_cyclic_iscyclic(self):
437437
d2 = f2.domain
438438

439439
# Getting
440-
self.assertEqual(d1.cyclic(), f1.cyclic())
441440
self.assertEqual(d1.cyclic(), set())
441+
self.assertEqual(d1.cyclic(), f1.cyclic())
442442
self.assertFalse(d1.iscyclic("X"))
443443
self.assertFalse(d1.iscyclic("Y"))
444444
self.assertFalse(d1.iscyclic("Z"))
445445
self.assertFalse(d1.iscyclic("T"))
446-
self.assertEqual(d2.cyclic(), f2.cyclic())
446+
447447
self.assertEqual(d2.cyclic(), set(("domainaxis2",)))
448+
self.assertEqual(d2.cyclic(), f2.cyclic())
448449
self.assertTrue(d2.iscyclic("X"))
449450
self.assertFalse(d2.iscyclic("Y"))
450451
self.assertFalse(d2.iscyclic("Z"))
451452
self.assertFalse(d2.iscyclic("T"))
452453

453454
# Setting
454455
self.assertEqual(d2.cyclic("X", iscyclic=False), set(("domainaxis2",)))
455-
self.assertEqual(d2.cyclic(), set())
456-
self.assertEqual(d2.cyclic("X", period=360), set())
457456
self.assertEqual(d2.cyclic(), set(("domainaxis2",)))
457+
458+
d2.cyclic("Y", period=360), set()
459+
self.assertEqual(
460+
d2.cyclic(),
461+
set(
462+
(
463+
"domainaxis1",
464+
"domainaxis2",
465+
)
466+
),
467+
)
468+
self.assertTrue(d2.iscyclic("Y"))
469+
self.assertEqual(
470+
d2.cyclic("Y", iscyclic=False),
471+
set(
472+
(
473+
"domainaxis1",
474+
"domainaxis2",
475+
)
476+
),
477+
)
478+
self.assertEqual(d2.cyclic(), set(("domainaxis2",)))
479+
480+
# Auto setting of cyclicity
481+
self.assertTrue(d2.iscyclic("X"))
482+
d2.cyclic("X", iscyclic=False)
483+
self.assertFalse(d2._cyclic)
484+
self.assertEqual(d2.cyclic(), set(("domainaxis2",)))
485+
486+
d2.cyclic("X", iscyclic=False)
458487
self.assertTrue(d2.iscyclic("X"))
459488

460489

cf/test/test_Field.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2970,9 +2970,37 @@ def test_Field_cyclic_iscyclic(self):
29702970

29712971
# Setting
29722972
self.assertEqual(f2.cyclic("X", iscyclic=False), set(("domainaxis2",)))
2973-
self.assertEqual(f2.cyclic(), set())
2974-
self.assertEqual(f2.cyclic("X", period=360), set())
29752973
self.assertEqual(f2.cyclic(), set(("domainaxis2",)))
2974+
2975+
f2.cyclic("Y", period=360), set()
2976+
self.assertEqual(
2977+
f2.cyclic(),
2978+
set(
2979+
(
2980+
"domainaxis1",
2981+
"domainaxis2",
2982+
)
2983+
),
2984+
)
2985+
self.assertTrue(f2.iscyclic("Y"))
2986+
self.assertEqual(
2987+
f2.cyclic("Y", iscyclic=False),
2988+
set(
2989+
(
2990+
"domainaxis1",
2991+
"domainaxis2",
2992+
)
2993+
),
2994+
)
2995+
self.assertEqual(f2.cyclic(), set(("domainaxis2",)))
2996+
2997+
# Auto setting of cyclicity
2998+
self.assertTrue(f2.iscyclic("X"))
2999+
f2.cyclic("X", iscyclic=False)
3000+
self.assertFalse(f2._cyclic)
3001+
self.assertEqual(f2.cyclic(), set(("domainaxis2",)))
3002+
3003+
f2.cyclic("X", iscyclic=False)
29763004
self.assertTrue(f2.iscyclic("X"))
29773005

29783006
def test_Field_is_discrete_axis(self):

0 commit comments

Comments
 (0)