Skip to content

adjustments to support gridded forcing#202

Open
mdeshotel wants to merge 1 commit into
developmentfrom
gridded_support
Open

adjustments to support gridded forcing#202
mdeshotel wants to merge 1 commit into
developmentfrom
gridded_support

Conversation

@mdeshotel

@mdeshotel mdeshotel commented Jul 16, 2026

Copy link
Copy Markdown

Modify the code base to better support gridded mode instead of only hydrofabric mode. This will be needed down the road for coastal modeling.

  • Move hydrofabric specific functionality to the hydrofabric BMI model class
  • Fix bugs for gridded mode in geomod
  • Make the selection of BMI_model classes dynamic to support all run modes.
  • Remove hardcoding of hydrofabric as the run mode.

I think this resolves the issues discovered in:

Additions

Removals

Changes

Testing

Screenshots

Notes

Todos

Checklist

  • PR has an informative and human-readable title
  • Changes are limited to a single goal (no scope creep)
  • Code can be automatically merged (no conflicts)
  • Code follows project standards (link if applicable)
  • Passes all existing automated tests
  • Any change in functionality is tested
  • New functions are documented (with a description, list of inputs, and expected output)
  • Placeholder code is flagged / future todos are captured in comments
  • Visually tested in supported browsers and devices (see checklist below 👇)
  • Project documentation has been updated (including the "Unreleased" section of the CHANGELOG)
  • Reviewers requested with the Reviewers tool ➡️

Testing checklist

Target Environment support

  • Linux

@mdeshotel
mdeshotel requested review from idtodd and mxkpp July 16, 2026 16:58
Comment on lines +1703 to +1715
@property
def cat_ids(self) -> NDArray[np.int_]:
"""Get the catchment IDs for the hydrofabric domain."""
if self._mpi_meta.rank == 0:
cat_ids = esmf_creation.create_mesh(self._job_meta)
cat_count = np.array(
[len(cat_ids) if self._mpi_meta.rank == 0 else 0], dtype=np.intc
)
self._mpi_meta.comm.Bcast(cat_count, root=0)
if self._mpi_meta.rank != 0:
cat_ids = np.empty(cat_count[0], dtype=np.int64)
self._mpi_meta.comm.Bcast(cat_ids, root=0)
return cat_ids

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels awkward as a property that non-hydrofabric models have to conform to. Maybe change this to a private method that gets overwritten.

Suggested change
@property
def cat_ids(self) -> NDArray[np.int_]:
"""Get the catchment IDs for the hydrofabric domain."""
if self._mpi_meta.rank == 0:
cat_ids = esmf_creation.create_mesh(self._job_meta)
cat_count = np.array(
[len(cat_ids) if self._mpi_meta.rank == 0 else 0], dtype=np.intc
)
self._mpi_meta.comm.Bcast(cat_count, root=0)
if self._mpi_meta.rank != 0:
cat_ids = np.empty(cat_count[0], dtype=np.int64)
self._mpi_meta.comm.Bcast(cat_ids, root=0)
return cat_ids
def _get_cat_ids(self) -> NDArray[np.int64]:
"""Get the catchment IDs for the hydrofabric domain."""
if self._mpi_meta.rank == 0:
cat_ids = esmf_creation.create_mesh(self._job_meta)
cat_count = np.array(
[len(cat_ids) if self._mpi_meta.rank == 0 else 0], dtype=np.intc
)
self._mpi_meta.comm.Bcast(cat_count, root=0)
if self._mpi_meta.rank != 0:
cat_ids = np.empty(cat_count[0], dtype=np.int64)
self._mpi_meta.comm.Bcast(cat_ids, root=0)
return cat_ids

if self._mpi_meta.rank != 0:
cat_ids = np.empty(cat_count[0], dtype=np.int64)
self._mpi_meta.comm.Bcast(cat_ids, root=0)
self._values["CAT-ID"] = self.cat_ids

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self._values["CAT-ID"] = self.cat_ids
self._values["CAT-ID"] = self._get_cat_ids()

z[:] = grid.grid_z
return z
raise ValueError(f"get_grid_z: grid_id {grid_id} unknown")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def _get_cat_ids(self) -> None:
"""Get the catchment IDs for the hydrofabric domain. Returns `None` for all other models."""

"""
super().__init__()
self.GeoMeta = UnstructuredGeoMeta
self.cat_ids = None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.cat_ids = None

"""
super().__init__()
self.GeoMeta = GriddedGeoMeta
self.cat_ids = None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
self.cat_ids = None

@mxkpp mxkpp left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

conftest.py still references BMIMODEL which no longer exists.

self._job_meta,
self.geo_meta,
self._mpi_meta,
self._values.get("CAT-ID"),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason for .get (returns None when key doesn't exist) instead of bracket notation for accessing this value (raises KeyError).

hyfab = gpd.read_file(hyfab_name, layer='divides')
return np.sort(hyfab.div_id.values, copy=True, dtype=np.int64)
hyfab = gpd.read_file(hyfab_name, layer="divides")
return np.array(np.sort(hyfab.div_id.values), copy=True,dtype=np.int64)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The old code converted to int and then sorted. The pending code sorts the raw values before converting to int. If the raw values are floats, the ordering within each integer range might be different before and after this change. I don't know if that matters or not, or the type of the raw div_id.values, but wanted to mention in case relevant.

Generally, this np.sort() and the usage of copy=True is memory-heavy. If there are memory concerns, places like this could be considered for later changes, modifying arrays in-place rather than creating full copies of them (as long as it is safe to do so, which I did not evalute).

Comment on lines 137 to 157
@cached_property
def geogrid_ds(self) -> xr.Dataset:
"""Open the geogrid file and return the xarray dataset object."""
try:
with xr.open_dataset(self.config_options.geogrid) as ds:
return ds.load()
return ds
except Exception as e:
self.config_options.errMsg = "Unable to open geogrid file with xarray"
log_critical(self.config_options, self.mpi_config)
raise e

@cached_property
@set_none
def esmf_ds(self) -> xr.Dataset:
"""Open the geospatial metadata file and return the xarray dataset object."""
try:
with xr.open_dataset(self.config_options.spatial_meta) as ds:
esmf_ds = ds.load()
esmf_ds = ds
except Exception as e:
self.config_options.errMsg = (
f"Unable to open esmf file: {self.config_options.spatial_meta}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't try running this, but I think with the removal of .load(), the file handle will be closed by the context manager before the data can be accessed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants