From d8140539ee92346cfb29b1ddfcb2abf064f1549d Mon Sep 17 00:00:00 2001 From: Mike Kryjak Date: Mon, 15 Jun 2026 14:41:13 +0100 Subject: [PATCH 1/5] Fix loading closed_wall_RZ Patch for older Hypnotoad grids Hypnotoad was writing closed_wall_R/Z with a "t" dim before, which would cause an xBOUT warning. This was resolved https://github.com/boutproject/hypnotoad/pull/191. This PR does the same fix xBOUT side for older grids. --- xbout/load.py | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/xbout/load.py b/xbout/load.py index 1a758317..2c7ed192 100644 --- a/xbout/load.py +++ b/xbout/load.py @@ -37,6 +37,32 @@ ] +def _update_legacy_closed_wall_dimension(grid): + """Older Hypnotoad grids wrote closed_wall_Z/R with a "t" dim. + This function changes the dim to "closed_wall" which is + the current Hypnotoad behaviour.""" + + if "closed_wall" in grid.dims: + return grid + + if "closed_wall_R" not in grid or "closed_wall_Z" not in grid: + return grid + + if grid["closed_wall_R"].dims != ("t",): + return grid + + if grid["closed_wall_Z"].dims != ("t",): + return grid + + for name in ("closed_wall_R", "closed_wall_Z"): + attrs = grid[name].attrs.copy() + data = grid[name].data + grid[name] = (("closed_wall",), data) + grid[name].attrs = attrs + + return grid + + # This code should run whenever any function from this module is imported # Set all attrs to survive all mathematical operations # (see https://github.com/pydata/xarray/pull/2482) @@ -752,7 +778,10 @@ def _check_dataset_type(datapath): if "metadata:keep_yboundaries" in ds.attrs: # (i) return "reload" - elif "t" in ds.dims: + + _update_legacy_closed_wall_dimension(ds) + + if "t" in ds.dims: # (iii) return "dump" elif all(["restart" in Path(p).name for p in filepaths]): @@ -1266,7 +1295,7 @@ def _open_grid(datapath, chunks, keep_xboundaries, keep_yboundaries, mxg=2, **kw boundaries to deal with different conventions in a BOUT grid file. """ - acceptable_dims = ["x", "y", "z"] + acceptable_dims = ["x", "y", "z", "closed_wall"] # Passing 'chunks' with dimensions that are not present in the # dataset causes an error. A gridfile will be missing 't' and may @@ -1287,6 +1316,8 @@ def _open_grid(datapath, chunks, keep_xboundaries, keep_yboundaries, mxg=2, **kw else: grid = datapath + grid = _update_legacy_closed_wall_dimension(grid) + unrecognised_dims = list(set(grid.dims) - set(acceptable_dims)) if len(unrecognised_dims) > 0: # Weird string formatting is a workaround to deal with possible bug in From 90a83c39593309b8d43c3e616e3772aec7fec7a0 Mon Sep 17 00:00:00 2001 From: Mike Kryjak Date: Mon, 15 Jun 2026 14:57:17 +0100 Subject: [PATCH 2/5] Improve metadata identification For closed_wall_RZ The variables closed_wall_R/Z have a dimension closed_wall, which would be detected as a scalar here since it's not in the hardcoded dims. --- xbout/load.py | 4 ++-- xbout/utils.py | 7 +------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/xbout/load.py b/xbout/load.py index 2c7ed192..f95738cb 100644 --- a/xbout/load.py +++ b/xbout/load.py @@ -39,8 +39,8 @@ def _update_legacy_closed_wall_dimension(grid): """Older Hypnotoad grids wrote closed_wall_Z/R with a "t" dim. - This function changes the dim to "closed_wall" which is - the current Hypnotoad behaviour.""" + This function changes the dim to "closed_wall" which is + the current Hypnotoad behaviour.""" if "closed_wall" in grid.dims: return grid diff --git a/xbout/utils.py b/xbout/utils.py index 0c109f96..7e61a18e 100644 --- a/xbout/utils.py +++ b/xbout/utils.py @@ -68,12 +68,7 @@ def _separate_metadata(ds): # whether it is scalar or 2d/3d array. exclude = ["dz"] - scalar_vars = [ - var - for var in variables - if not any(dim in ["t", "x", "y", "z"] for dim in ds[var].dims) - and var not in exclude - ] + scalar_vars = [var for var in variables if ds[var].ndim == 0 and var not in exclude] # Save metadata as a dictionary metadata_vals = [ds[var].values.item() for var in scalar_vars] From c89f68e2ca1ddd5fb6e772b853e4a07b53568600 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 16 Jun 2026 08:54:59 +0200 Subject: [PATCH 3/5] Make code a bit shorter and clearer --- xbout/load.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/xbout/load.py b/xbout/load.py index f95738cb..fc51906b 100644 --- a/xbout/load.py +++ b/xbout/load.py @@ -45,14 +45,9 @@ def _update_legacy_closed_wall_dimension(grid): if "closed_wall" in grid.dims: return grid - if "closed_wall_R" not in grid or "closed_wall_Z" not in grid: - return grid - - if grid["closed_wall_R"].dims != ("t",): - return grid - - if grid["closed_wall_Z"].dims != ("t",): - return grid + for name in ("closed_wall_R", "closed_wall_Z"): + if name not in grid or len(grid[name].dims) != 1: + return grid for name in ("closed_wall_R", "closed_wall_Z"): attrs = grid[name].attrs.copy() From 4443212447f2c53870744d73f5414732d5fcab6a Mon Sep 17 00:00:00 2001 From: mikekryjak <62797494+mikekryjak@users.noreply.github.com> Date: Fri, 26 Jun 2026 10:31:05 +0100 Subject: [PATCH 4/5] Apply suggestion from @dschwoerer Co-authored-by: David Bold --- xbout/load.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xbout/load.py b/xbout/load.py index fc51906b..ddd3f96b 100644 --- a/xbout/load.py +++ b/xbout/load.py @@ -774,7 +774,7 @@ def _check_dataset_type(datapath): # (i) return "reload" - _update_legacy_closed_wall_dimension(ds) + ds = _update_legacy_closed_wall_dimension(ds) if "t" in ds.dims: # (iii) From b452ae39edd0e1754f9fb5e9f502be06bf6c3ff9 Mon Sep 17 00:00:00 2001 From: mikekryjak <62797494+mikekryjak@users.noreply.github.com> Date: Mon, 13 Jul 2026 10:36:36 +0100 Subject: [PATCH 5/5] Remove unnecessary attr copy Co-authored-by: David Bold --- xbout/load.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xbout/load.py b/xbout/load.py index ddd3f96b..dd2c9e47 100644 --- a/xbout/load.py +++ b/xbout/load.py @@ -50,7 +50,7 @@ def _update_legacy_closed_wall_dimension(grid): return grid for name in ("closed_wall_R", "closed_wall_Z"): - attrs = grid[name].attrs.copy() + attrs = grid[name].attrs data = grid[name].data grid[name] = (("closed_wall",), data) grid[name].attrs = attrs