check that a network graph doesn't contain localized loops and, optionally, that its divergence attributes are internally consistent.
Arguments
- x
data.frame network compatible with hydroloom_names.
- loop_check
logical if TRUE, the entire network is walked from top to bottom searching for loops. This loop detection algorithm visits a node in the network only once all its upstream neighbors have been visited. A complete depth first search is performed at each node, searching for paths that lead to an already visited (upstream) node. This algorithm is often referred to as "recursive depth first search".
- divergence_check
logical if TRUE,
divergenceand, where present,divergence_fractionare checked for internal consistency as described in details.- fraction_tol
numeric tolerance used when checking that
divergence_fractionsums to 1 across afromnodegroup.
Value
if no problems are found, returns TRUE. If problems are found,
problem rows with a row number added. Rows returned by the divergence
check carry a divergence_issue column naming the failed checks. Checks
run in order and the first one to find a problem returns, so a single
call reports one class of problem at a time.
Details
Required attributes: id, toid
For divergence_check: id, fromnode, divergence and,
conditionally, divergence_fraction.
The divergence check enforces the attribute contract that
accumulate_downstream() documents and depends on: 0 indicates a
catchment that is not downstream of a divergence, 1 the primary path
downstream of a divergence, and 2 a diverted path. Total upstream
routing needs a complete and consistent divergence attribute to avoid
double counting through systems of diverted channels. Use
add_divergence() to derive the attribute where it is absent.
Divergence checks, grouping features by shared fromnode:
divergenceis populated and in{0, 1, 2}.Every node with more than one outgoing feature has exactly one
divergence == 1.No
divergence == 0on a node with more than one outgoing feature.No
divergenceof 1 or 2 on a node with a single outgoing feature.
A node may have any number of diverted paths, so more than one
divergence == 2 at a node is valid and is not flagged.
When a divergence_fraction attribute is present, three further checks
apply:
divergence_fractionis populated and in[0, 1].divergence_fractionsums to 1 across eachfromnodegroup.No
divergence == 2feature takes the entire flow.
See also
accumulate_downstream() for the routing that depends on this
contract and add_divergence() for deriving the attribute.
Examples
# notice that row 4 (id = 4, toid = 9) and row 8 (id = 9, toid = 4) is a loop.
test_data <- data.frame(id = c(1, 2, 3, 4, 6, 7, 8, 9),
toid = c(2, 3, 4, 9, 7, 8, 9, 4))
check_hy_graph(test_data)
#> # A tibble: 2 × 4
#> toid id row toid_check
#> <dbl> <dbl> <int> <dbl>
#> 1 4 9 8 9
#> 2 9 4 4 4
# node 2 splits to a main path (id 2) and a diversion (id 3) -- valid
div_data <- data.frame(id = c(1, 2, 3, 4),
toid = c(2, 4, 0, 0),
fromnode = c(1, 2, 2, 3),
tonode = c(2, 3, 4, 5),
divergence = c(0, 1, 2, 0))
check_hy_graph(div_data, divergence_check = TRUE)
#> [1] TRUE
# demote the main path and node 2 has no primary outlet
div_data$divergence[2] <- 2
check_hy_graph(div_data, divergence_check = TRUE)
#> # hydroloom non-dendritic edge list (self-referencing): 2 features
#> # A tibble: 2 × 7
#> id toid fromnode tonode divergence row divergence_issue
#> <dbl> <dbl> <dbl> <dbl> <dbl> <int> <chr>
#> 1 2 4 2 3 2 2 node without exactly one diverge…
#> 2 3 0 2 4 2 3 node without exactly one diverge…
