"""
Basic interface to libtopotoolbox implementation of graphflood.
"""
from copy import deepcopy
import numpy as np
# pylint: disable=no-name-in-module
from . import _graphflood # type:ignore
from .grid_object import GridObject
# exposing function as string dictionary
funcdict = {
"run_full": _graphflood.graphflood_run_full,
"sfgraph": _graphflood.graphflood_sfgraph,
"priority_flood_TO":
_graphflood.compute_priority_flood_plus_topological_ordering,
"priority_flood": _graphflood.compute_priority_flood,
"drainage_area_single_flow": _graphflood.compute_drainage_area_single_flow,
}
__all__ = ["run_graphflood"]
[docs]
def run_graphflood(
grid: GridObject,
initial_hw: np.ndarray | GridObject | None = None,
bcs: np.ndarray | GridObject | None = None,
dt: float = 1e-3,
p: float | np.ndarray | GridObject = 10 * 1e-3 / 3600,
manning: float | np.ndarray | GridObject = 0.033,
sfd: bool = False,
d8: bool = True,
n_iterations: int = 100,
):
"""
Runs the full graphflood's algorithm as described in Gailleton et al., 2024
Parameters
----------
grid : GridObject
A GridObject representing the digital elevation model.
initial_hw : np.ndarray or GridObject, optional
Flow depth.
Default is a matrix filled with zeros and the same shape as 'grid'.
BCs : np.ndarray or GridObject, optional
Boundary codes.
Default is a matrix filled with ones except for the outermost edges,
where values are set to 3, has same shape as 'grid'
dt : float, optional
time step(s ~ although this is not simulated time as we make the
steady low assumption). Default is 1e-3.
P : float, np.ndarray, or GridObject, optional
Precipitation rates in m.s-1
Default is a matrix with the same shape of 'grid'
filled with 10 * 1e-3 / 3600.
manning : float, np.ndarray, or GridObject, optional
Friction coefficient.
Default is a matrix with the same shape as 'grid' filled with 0.033.
SFD : bool, optional
True to compute single flow directions, False to compute multiple flow
directions. Default is `False`.
D8 : bool, optional
True to include diagonal paths. Default is `True`.
N_iterations : int, optional
Number of iterations for the simulation. Default is 100.
Returns
-------
GridObject
A grid object with the computed water depths.
Raises
------
RuntimeError
If the shape of `initial_hw`, `BCs`, `P`, or `manning` does not match
the shape of the 'grid' GridObject`.
"""
# Preparing the arguments
ny = grid.rows
nx = grid.columns
dx = grid.cellsize
dim = np.array([ny, nx], dtype=np.uint64)
# Order C for vectorised topography
z = grid.z.ravel(order="C")
# Ingesting the flow depth
if initial_hw is None:
hw = np.zeros_like(z)
else:
if initial_hw.shape != grid.z.shape:
raise RuntimeError(
"""Feeding the model with initial flow depth
requires a 2D numpy array or a GridObject of
the same dimension of the topographic grid"""
)
if isinstance(initial_hw, GridObject):
hw = initial_hw.z.ravel(order="C")
else:
hw = initial_hw.ravel(order="C")
# Ingesting boundary condition
if bcs is None:
tbcs = np.ones((grid.rows, grid.columns), dtype=np.uint8)
tbcs[[0, -1], :] = 3
tbcs[:, [0, -1]] = 3
tbcs = tbcs.ravel(order="C")
else:
if bcs.shape != grid.shape:
raise RuntimeError(
"""Feeding the model with boundary conditions requires
a 2D numpy array or a GridObject of the same dimension
of the topographic grid"""
)
if isinstance(bcs, GridObject):
tbcs = bcs.z.ravel(order="C").astype(np.uint8)
else:
tbcs = bcs.ravel(order="C").astype(np.uint8)
# Ingesting Precipitations
if isinstance(p, np.ndarray):
if p.shape != grid.shape:
raise RuntimeError(
"""Feeding the model with precipitations requires a
2D numpy array or a GridObject of the same
dimension of the topographic grid"""
)
precipitations = p.ravel(order="C")
elif isinstance(p, GridObject):
if p.shape != grid.shape:
raise RuntimeError(
"""Feeding the model with precipitations requires a
2D numpy array or a GridObject of the same dimension
of the topographic grid"""
)
precipitations = p.z.ravel(order="C")
else:
# in case precipitation is a scalar
precipitations = np.full_like(z, p)
# Ingesting manning
if isinstance(manning, np.ndarray):
if manning.shape != grid.shape:
raise RuntimeError(
"""Feeding the model with precipitations requires a
2D numpy array or a GridObject of the same dimension
of the topographic grid"""
)
manning = manning.ravel(order="C")
elif isinstance(manning, GridObject):
if manning.shape != grid.shape:
raise RuntimeError(
"""Feeding the model with precipitations requires a
2D numpy array or a GridObject of the same dimension
of the topographic grid"""
)
manning = manning.z.ravel(order="C")
else:
# in case precipitation is a scalar
manning = np.full_like(z, manning)
_graphflood.graphflood_run_full(
z, hw, tbcs, precipitations, manning,
dim, dt, dx, sfd, d8, n_iterations, 1e-3
)
res = deepcopy(grid)
res.z = hw.reshape(grid.shape)
return res