Source code for hydromt_delft3dfm.mesh_utils

"""Utilities mesh functions for Delft3D FM model."""

import logging
from typing import Tuple

import geopandas as gpd
import xarray as xr
import xugrid as xu
from hydrolib.core.dflowfm import Network
from pyproj import CRS
from shapely.geometry import LineString

# TODO: maybe move this function here instead of under workflows?
from hydromt_delft3dfm.workflows.mesh import _set_link1d2d

logger = logging.getLogger(__name__)


__all__ = [
    "hydrolib_network_from_mesh",
    "mesh1d_network1d_from_hydrolib_network",
    "links1d2d_from_hydrolib_network",
    "mesh_from_hydrolib_network",
    "mesh1d_nodes_geodataframe",
    "network1d_nodes_geodataframe",
    "network1d_geoms_geodataframe",
]


[docs] def hydrolib_network_from_mesh( mesh: xu.UgridDataset, ) -> Network: """ Convert from xugrid mesh to hydrolib-core network object. Parameters ---------- mesh : xu.UgridDataset Mesh UgridDataset. Returns ------- network : Network Network object. """ # split grids grids = dict() for grid in mesh.ugrid.grids: grids[grid.name] = grid # create network dfm_network = Network(is_geographic=mesh.ugrid.grids[0].crs.is_geographic) _mesh1d_attrs = dfm_network._mesh1d.__dict__.keys() # add mesh2d if "mesh2d" in grids: dfm_network._mesh2d.meshkernel.mesh2d_set(grids["mesh2d"].mesh) # FIXME: test what if the mesh had bedlevel variable # add mesh1d (including mesh1d and networkd1d) if "mesh1d" in grids: mesh1d = mesh.ugrid.to_dataset( optional_attributes=True ) # optional_attributes includes edge_x and edge_y for var, val in mesh1d.variables.items(): if var in _mesh1d_attrs: # use hydrolib-core conventions as it does harmonization when reading. # check conventions at hydrolib.core.dflowfm.net.ugrid_conventions.json setattr(dfm_network._mesh1d, var, val.values) # process dfm_network._mesh1d._process_network1d() dfm_network._mesh1d._set_mesh1d() # TODO: avoid this private function # dfm_network._mesh1d.meshkernel.mesh1d_set(grids["mesh1d"].mesh) if "link1d2d" in mesh: # add 1d2dlinks to network link1d2d_arr = mesh["link1d2d"].to_numpy() _set_link1d2d(dfm_network, link1d2d_arr) return dfm_network
[docs] def mesh1d_network1d_from_hydrolib_network( network: Network, crs: CRS, ) -> Tuple[xu.UgridDataset, xu.UgridDataset]: """ Create xugrid mesh1d and network1d UgridDataset from hydrolib-core network object. Parameters ---------- network : Network Network hydrolib-core object. crs : pyproj.CRS Coordinate reference system of the network. Returns ------- uds_mesh1d : xu.UgridDataset Mesh1d UgridDataset. uds_network1d : xu.UgridDataset Network1d UgridDataset. """ # Mesh1d to mesh1d and network1d xugrid mesh1d = network._mesh1d if not mesh1d.is_empty(): # get grid grid_mesh1d = xu.Ugrid1d( node_x=mesh1d.mesh1d_node_x, node_y=mesh1d.mesh1d_node_y, fill_value=-1, edge_node_connectivity=mesh1d.mesh1d_edge_nodes, name="mesh1d", projected=crs.is_projected, crs=crs, ) grid_mesh1d.set_crs(crs) edge_dim = grid_mesh1d.edge_dimension node_dim = grid_mesh1d.node_dimension # get data ds = xr.Dataset() ds["mesh1d_node_id"] = (node_dim, mesh1d.mesh1d_node_id) ds["mesh1d_node_long_name"] = (node_dim, mesh1d.mesh1d_node_long_name) ds["mesh1d_node_branch_id"] = (node_dim, mesh1d.mesh1d_node_branch_id) ds["mesh1d_node_branch_offset"] = (node_dim, mesh1d.mesh1d_node_branch_offset) ds["mesh1d_edge_branch_id"] = (edge_dim, mesh1d.mesh1d_edge_branch_id) ds["mesh1d_edge_branch_offset"] = (edge_dim, mesh1d.mesh1d_edge_branch_offset) # get ugrid dataset uds_mesh1d = xu.UgridDataset(ds, grids=grid_mesh1d) # derive network1d # The 1D network topology serves as the coordinate space in which a 1D mesh # discretization will later be defined. The network is largely based on the # UGRID conventions for its topology (i.e., nodes and edges) and additionally # uses an optional edge_geometry to define the precise network branch geometries # (more about this in the next Section). grid_network1d = xu.Ugrid1d( node_x=mesh1d.network1d_node_x, node_y=mesh1d.network1d_node_y, fill_value=-1, edge_node_connectivity=mesh1d.network1d_edge_nodes, name="network1d", projected=crs.is_projected, crs=crs, ) grid_network1d.set_crs(crs) network_edge_dim = grid_network1d.edge_dimension network_node_dim = grid_network1d.node_dimension # get data ds_network1d = xr.Dataset() ds_network1d["network1d_node_id"] = (network_node_dim, mesh1d.network1d_node_id) ds_network1d["network1d_node_long_name"] = ( network_node_dim, mesh1d.network1d_node_long_name, ) ds_network1d["network1d_branch_id"] = ( network_edge_dim, mesh1d.network1d_branch_id, ) ds_network1d["network1d_branch_long_name"] = ( network_edge_dim, mesh1d.network1d_branch_long_name, ) ds_network1d["network1d_branch_length"] = ( network_edge_dim, mesh1d.network1d_branch_length, # real length of the branches ) # network1d_geometry related ds_network1d["network1d_part_node_count"] = ( network_edge_dim, mesh1d.network1d_part_node_count, ) ds_network1d["network1d_geom_x"] = ( "network1d_nGeometryNodes", mesh1d.network1d_geom_x, ) ds_network1d["network1d_geom_y"] = ( "network1d_nGeometryNodes", mesh1d.network1d_geom_y, ) ds_network1d["network1d_branch_order"] = ( network_edge_dim, mesh1d.network1d_branch_order, ) # might be supported in the future # https://github.com/Deltares/HYDROLIB-core/issues/561 # ds["network1d_branch_type"] = ( # edge_dim, # mesh1d.network1d_branch_type, # ) # get ugrid dataset uds_network1d = xu.UgridDataset(ds_network1d, grids=grid_network1d) else: uds_mesh1d = None uds_network1d = None return uds_mesh1d, uds_network1d
[docs] def links1d2d_from_hydrolib_network( network: Network, ) -> xr.Dataset(): """ Extract link1d2d from hydrolib-core network object. Parameters ---------- network : Network Network hydrolib-core object. Returns ------- link1d2d : xr.Dataset Link1d2d Dataset. """ # extract links from network object link1d2d = xr.Dataset() link1d2d["link1d2d"] = (["nLink1D2D_edge", "Two"], network._link1d2d.link1d2d) # extra variables link1d2d["link1d2d_id"] = ("nLink1D2D_edge", network._link1d2d.link1d2d_id) link1d2d["link1d2d_long_name"] = ( "nLink1D2D_edge", network._link1d2d.link1d2d_long_name, ) link1d2d["link1d2d_contact_type"] = ( "nLink1D2D_edge", network._link1d2d.link1d2d_contact_type, ) return link1d2d
def mesh2d_from_hydrolib_network( network: Network, crs: CRS, ) -> xu.UgridDataset: """ Create xugrid mesh2d UgridDataset from hydrolib-core network object. Parameters ---------- network : Network Network hydrolib-core object. crs : pyproj.CRS Coordinate reference system of the network. Returns ------- uds_mesh2d : xu.UgridDataset Mesh2d UgridDataset. """ mk_mesh2d = network._mesh2d.meshkernel.mesh2d_get() # meshkernel to xugrid Ugrid2D uds_mesh2d = xu.Ugrid2d.from_meshkernel( mk_mesh2d, name="mesh2d", projected=crs.is_projected, crs=crs, ) # Convert to UgridDataset uds_mesh2d = xu.UgridDataset(uds_mesh2d.to_dataset()) uds_mesh2d = uds_mesh2d.ugrid.assign_face_coords() # set crs uds_mesh2d.ugrid.set_crs(crs) return uds_mesh2d
[docs] def mesh_from_hydrolib_network( network: Network, crs: CRS, ) -> xu.UgridDataset: """ Create xugrid mesh from hydrolib-core network object. Parameters ---------- network : Network Network hydrolib-core object. crs : pyproj.CRS Coordinate reference system of the network. Returns ------- mesh : xu.UgridDataset Mesh UgridDataset. """ # initialise mesh mesh = None # Mesh1d to mesh1d and network1d xugrid if not network._mesh1d.is_empty(): uds_mesh1d, uds_network1d = mesh1d_network1d_from_hydrolib_network(network, crs) # add to mesh mesh = xu.UgridDataset( xr.merge([uds_mesh1d.ugrid.to_dataset(), uds_network1d.ugrid.to_dataset()]) ) # Mesh2d if not network._mesh2d.is_empty(): uds_mesh2d = mesh2d_from_hydrolib_network(network, crs) if mesh is None: mesh = uds_mesh2d else: mesh = xu.UgridDataset( xr.merge( [ mesh.ugrid.to_dataset(optional_attributes=True), uds_mesh2d.ugrid.to_dataset(optional_attributes=True), ] ) ) # 1d2dlinks if not network._link1d2d.is_empty(): link1d2d = links1d2d_from_hydrolib_network(network) # Add to mesh (links should only exist if mesh1d and mesh2d exist) if mesh is not None: for v in link1d2d.data_vars: mesh[v] = link1d2d[v] # Set crs if mesh is not None: for grid in mesh.ugrid.grids: grid.set_crs(crs) return mesh
[docs] def mesh1d_nodes_geodataframe( uds_mesh1d: xu.UgridDataset, branches: gpd.GeoDataFrame, ) -> gpd.GeoDataFrame: """ Return the nodes of mesh 1D as geodataframe. Parameters ---------- uds_mesh1d : xu.UgridDataset Mesh1d UgridDataset including mesh1d data_vars. branches : gpd.GeoDataFrame Branches GeoDataFrame. Returns ------- mesh1d_nodes : gpd.GeoDataFrame Mesh1d nodes GeoDataFrame. """ # create node points mesh1d_nodes = gpd.points_from_xy( x=uds_mesh1d.ugrid.grid.node_x, y=uds_mesh1d.ugrid.grid.node_y, crs=uds_mesh1d.ugrid.grid.crs, ) # Creates gdf with some extra data (using hydrolib-core convention) mesh1d_nodes = gpd.GeoDataFrame( data={ "branch_id": uds_mesh1d["mesh1d_node_branch_id"], "branch_name": [ branches.branchid[i] for i in uds_mesh1d["mesh1d_node_branch_id"].values ], "branch_chainage": uds_mesh1d["mesh1d_node_branch_offset"], "geometry": mesh1d_nodes, } ) return mesh1d_nodes
[docs] def network1d_nodes_geodataframe( uds_network1d: xu.UgridDataset, ) -> gpd.GeoDataFrame: """ Get network1d nodes as gdp. Parameters ---------- uds_network1d : xu.UgridDataset Network1d UgridDataset including network1d data_vars. Returns ------- network1d_nodes : gpd.GeoDataFrame Network1d nodes GeoDataFrame. """ # get networkids to complete the boundaries network1d_nodes = gpd.points_from_xy( x=uds_network1d.ugrid.grid.node_x, y=uds_network1d.ugrid.grid.node_y, crs=uds_network1d.ugrid.grid.crs, ) network1d_nodes = gpd.GeoDataFrame( data={ "nodeid": uds_network1d["network1d_node_id"], "geometry": network1d_nodes, } ) return network1d_nodes
def network1d_geoms_geodataframe( uds_network1d: xu.UgridDataset, ) -> gpd.GeoDataFrame: """ Get network1d as gdp from network1d geoms. Parameters ---------- uds_network1d : xu.UgridDataset Network1d UgridDataset including network1d data_vars. Returns ------- network1d : gpd.GeoDataFrame Network1d GeoDataFrame. """ network1d_geoms = [] start_index = 0 for n in uds_network1d["network1d_part_node_count"].values: x = uds_network1d["network1d_geom_x"][start_index : start_index + n] y = uds_network1d["network1d_geom_y"][start_index : start_index + n] start_index += n points = list(zip(x, y)) line = LineString(points) network1d_geoms.append(line) network1d_geoms = gpd.GeoDataFrame( geometry=network1d_geoms, crs=uds_network1d.ugrid.grid.crs, ) network1d_geoms["branchid"] = uds_network1d["network1d_branch_id"] network1d_geoms["branchorder"] = uds_network1d["network1d_branch_order"] return network1d_geoms