From 78feb6fbae2f97c2547ceb3c59627b3e3a464d88 Mon Sep 17 00:00:00 2001 From: Mike Kryjak Date: Fri, 6 Mar 2026 14:37:11 +0000 Subject: [PATCH 1/2] Create BoutOptionsFile with grid if available If you want the input file and don't pass a grid, it'll struggle because nx/ny/nz are often missing from the input file, and the grid may not be in the directory where you run the script from. xBOUT knows where the grid is though! --- xbout/load.py | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/xbout/load.py b/xbout/load.py index 4f565f8b..ee6f6448 100644 --- a/xbout/load.py +++ b/xbout/load.py @@ -243,7 +243,7 @@ def attrs_remove_section(obj, section): if attrs_remove_section(da, "metadata"): da.attrs["metadata"] = metadata - ds = _add_options(ds, inputfilepath) + ds = _add_options(ds, inputfilepath, gridfilepath) # If geometry was set, apply geometry again if geometry is not None: @@ -314,7 +314,7 @@ def attrs_remove_section(obj, section): # grid file, as they will be removed from the full Dataset below keep_yboundaries = True - ds = _add_options(ds, inputfilepath) + ds = _add_options(ds, inputfilepath, gridfilepath) if geometry is None: if geometry in ds.attrs: @@ -428,15 +428,26 @@ def attrs_remove_section(obj, section): return ds -def _add_options(ds, inputfilepath): +def _add_options(ds, inputfilepath, gridfilepath): + """ + Add BoutOptionsFile as ds.options, if input filepath available. + BoutOptionsFile needs grid dimensions to reconstruct coordinates. + Prefer using grid if provided. Otherwise, use dataset dimensions. + """ + if inputfilepath: - # Use Ben's options class to store all input file options - options = BoutOptionsFile( - inputfilepath, - nx=ds.metadata["nx"], - ny=ds.metadata["ny"], - nz=ds.metadata["nz"], - ) + if gridfilepath: + options = BoutOptionsFile( + inputfilepath, + gridfilename=gridfilepath, + ) + else: + options = BoutOptionsFile( + inputfilepath, + nx=ds.metadata["nx"], + ny=ds.metadata["ny"], + nz=ds.metadata["nz"], + ) else: options = None ds = _set_attrs_on_all_vars(ds, "options", options) From 7c18bd68f632522115a95f0d3e5f798a6ed3bcf4 Mon Sep 17 00:00:00 2001 From: Mike Kryjak Date: Wed, 15 Apr 2026 13:18:46 +0100 Subject: [PATCH 2/2] Fix BoutOptionsFile when gridfilepath is a directory Also allows for the legacy "grid" field in the input file. --- xbout/load.py | 55 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/xbout/load.py b/xbout/load.py index 18a5e9f0..a13f6a7f 100644 --- a/xbout/load.py +++ b/xbout/load.py @@ -371,8 +371,11 @@ def is_netcdf_collection(datapath): print("Applying {} geometry conventions".format(geometry)) if _is_dir(gridfilepath): - if "grid" in ds.options: - gridfilepath += "/" + ds.options["grid"] + resolved_gridfilepath = _resolve_gridfilepath_from_options( + gridfilepath, ds.options + ) + if resolved_gridfilepath is not None: + gridfilepath = resolved_gridfilepath else: warn( "gridfilepath set to a directory, but no grid used " @@ -475,6 +478,26 @@ def is_netcdf_collection(datapath): return ds +def _resolve_gridfilepath_from_options(gridfilepath, options): + if options is None: + return None + + gridfilename = None + if "mesh" in options and "file" in options["mesh"]: + gridfilename = options["mesh"]["file"] + elif "grid" in options: + gridfilename = options["grid"] + + if gridfilename is None: + return None + + gridpath = Path(gridfilename) + if gridpath.is_absolute(): + return gridpath + + return Path(gridfilepath).joinpath(gridpath) + + def _add_options(ds, inputfilepath, gridfilepath): """ Add BoutOptionsFile as ds.options, if input filepath available. @@ -484,10 +507,30 @@ def _add_options(ds, inputfilepath, gridfilepath): if inputfilepath: if gridfilepath: - options = BoutOptionsFile( - inputfilepath, - gridfilename=gridfilepath, - ) + if _is_dir(gridfilepath): + # If only a grid directory was provided, read the input file first to + # determine the grid filename without trying to construct x, y, z yet. + options = BoutOptionsFile(inputfilepath, recalculate_xyz=False) + resolved_gridfilepath = _resolve_gridfilepath_from_options( + gridfilepath, options + ) + + if resolved_gridfilepath is not None: + options = BoutOptionsFile( + inputfilepath, + gridfilename=resolved_gridfilepath, + ) + else: + warn( + "gridfilepath set to a directory, but no grid used " + "in simulation. Continuing without grid." + ) + + else: + options = BoutOptionsFile( + inputfilepath, + gridfilename=gridfilepath, + ) else: options = BoutOptionsFile( inputfilepath,