Skip to contents

check that a network graph doesn't contain localized loops and, optionally, that its divergence attributes are internally consistent.

Usage

check_hy_graph(
  x,
  loop_check = FALSE,
  divergence_check = FALSE,
  fraction_tol = 1e-06
)

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, divergence and, where present, divergence_fraction are checked for internal consistency as described in details.

fraction_tol

numeric tolerance used when checking that divergence_fraction sums to 1 across a fromnode group.

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:

  1. divergence is populated and in {0, 1, 2}.

  2. Every node with more than one outgoing feature has exactly one divergence == 1.

  3. No divergence == 0 on a node with more than one outgoing feature.

  4. No divergence of 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:

  1. divergence_fraction is populated and in [0, 1].

  2. divergence_fraction sums to 1 across each fromnode group.

  3. No divergence == 2 feature 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…