question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Flow router/depression finder incorrectly directs flow in landlab version 2.1.1

See original GitHub issue

attached_files.zip

I’ve become stuck on an issue with the flow router and depression finder. I am attempting to write a simple model using linear diffusion and detachment-limited fluvial erosion on an arbitrary topography loaded from a png. However, something about this model seems to be confusing the flow router such that lakes are not handled properly and large chunks of the drainage network flash on and off (see animation_island_r320.mp4). Although I’m using topography and erosion coefficients that probably aren’t realistic, the flow router shouldn’t be impacted. It should only care about the current topography when run_one_step is called, correct?

A minimum working example with only basic plotting code is shown below. However, the attached mwe_routing_issue.py runs the same code with more useful plotting. I recommend using that file. The following description is based on those plots.

The initial topography (top left subplot)) is a ring of high topography with a small gap surrounding low topography in the interior. The router correctly identifies the lake in the interior and correctly directs the drainage network (center left and bottom left) through the lake area. The first step shifts things a bit, but overall remains similar. The only unusual thing to note is that the node at (7, 15) increases slightly despite being part of the main flow route because of diffusion.

step_2

Step 2 sees the lake shrink significantly (bottom right) and does not reflect the current topography (top center). The drainage network is cut off (center) and flow becomes trapped in one of the lake nodes (bottom center). Drainage area upstream of the lake are not accumulated and consequently the fluvial component cannot erode anywhere in the interior. Step 3 continues the trend.

Step 4 through 38 are mostly the same. Backward erosion of the original channel in the ring gap slowly reduces the elevation of what should be the outlet node.

Between steps 39 and 41, the backward erosion recaptures much of the lower left corner of the interior. The proper drainage network is restored on step 42 when the lake is eliminated.

The issue still occurs with different ordering for calling each component’s run_one_step and occurs for both D4 or D8 routing. The timing is different though and you would have to modify the script to see other scenarios.

I found this issue using landlab version 2.1.1 acquired via conda. However, when I run the same script in version 2.0.0-dev+36.g39fee962, this issue does not occur. I’m not sure about other landlab versions.

Here is the model code for minimum working example (with only one basic plot):

import numpy as np
import matplotlib.pyplot as plt

from landlab import RasterModelGrid
import landlab.components as lab_components

print("Initializing Model")
## Model parameters
n_steps = 42
dt = 100 # years
node_spacing = 100 # m

## Make initial topography
input_topo = np.array([
 [0,  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,  0, 0],
 [0, 35,  35,  35,  35,  35,  35,  35,  35,  35,  35,  35,  35,  35,  35,  35,  35,  35, 35, 0],
 [0, 35, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 35, 0],
 [0, 35, 140, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 140, 35, 0],
 [0, 35, 140, 255, 211, 212, 211, 211, 212, 211, 212, 212, 211, 211, 211, 212, 255, 140, 35, 0],
 [0, 35, 140, 255, 211, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 212, 255, 140, 35, 0],
 [0, 35, 140, 255, 211, 124,  36,  36,  36,  36,  36,  36,  36,  36, 124, 212, 255, 140, 35, 0],
 [0, 35, 140, 255, 211, 124,  36,  36,  36,  36,  36,  36,  36,  36, 124, 211, 255, 140, 35, 0],
 [0, 35, 140, 255, 211, 124,  36,  36,  36,  36,  36,  36,  36,  36, 124, 211, 255, 140, 35, 0],
 [0, 35, 140, 255, 211, 124,  36,  36,  36,  36,  36,  36,  36,  36, 124, 211, 255, 140, 35, 0],
 [0, 35, 140, 255, 211, 124,  36,  36,  36,  36,  36,  36,  36,  36, 124, 211, 255, 140, 35, 0],
 [0, 35, 140, 255, 212, 124,  36,  36,  36,  36,  36,  36,  36,  36, 124, 211, 255, 140, 35, 0],
 [0, 35, 140, 255, 211, 124,  36,  36,  36,  36,  36,  36,  36,  36, 124, 211, 255, 140, 35, 0],
 [0, 35, 140, 255, 211, 124,  36,  36,  36,  36,  36,  36,  36,  36, 124, 211, 255, 140, 35, 0],
 [0, 35, 140, 255, 211, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 212, 255, 140, 35, 0],
 [0, 35, 140, 255, 211, 211, 211, 124, 212, 211, 211, 211, 212, 211, 211, 212, 255, 140, 35, 0],
 [0, 35, 140, 255, 255, 255, 255, 124, 255, 255, 255, 255, 255, 255, 255, 255, 255, 140, 35, 0],
 [0, 35, 140, 140, 140, 140, 140, 124, 140, 140, 140, 140, 140, 140, 140, 140, 140, 140, 35, 0],
 [0, 35,  35,  35,  35,  35,  35,  35,  35,  35,  35,  35,  35,  35,  35,  35,  35,  35, 35, 0],
 [0,  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,  0, 0],
 ])
np_2D_init_topo = np.flipud(input_topo.astype(np.float))
map_shape = np_2D_init_topo.shape
np_1D_init_topo = np_2D_init_topo.ravel()

## Set up Landlab
# Set up landlab grid
print(f"Setting up Landlab grid")
mg = RasterModelGrid(map_shape, node_spacing)

# Add topography and slope
mg.add_field('node', 'topographic__elevation', 
        np_1D_init_topo, units='m')
mg.add_field('node', 'topographic__init_elevation', 
        np_1D_init_topo.copy(), units='m')
g = mg.calc_grad_at_link('topographic__elevation')
mg.add_field('link', 'topographic__slope', g)

# Set up boundaries
mg.set_closed_boundaries_at_grid_edges(False, False, False, False)

# Set up flow router component and run it once
flow_router = lab_components.FlowAccumulator(mg, 
        flow_director='FlowDirectorD8',
        depression_finder=lab_components.DepressionFinderAndRouter,
        # Or
        #flow_director='FlowDirectorSteepest',
        #depression_finder=lab_components.DepressionFinderAndRouter,
        #routing='D4',
        )
flow_router.run_one_step()
mg['node']['init_drainage_area'] = mg['node']['drainage_area'].copy()
#mg.add_field('node', 'init_flow_direct', _get_flow_dir(mg))

# Set up fluvial component
fluvial = lab_components.FastscapeEroder(mg)

# Set up diffusion component
diffusion = lab_components.LinearDiffuser(mg)

print(f"Finished setting up")
print()

## Run model
print()
print(f"Running model...")
for step in range(1, n_steps + 1):
    diffusion.run_one_step(dt)
    flow_router.run_one_step()
    fluvial.run_one_step(dt)

    if n_steps >= 10 and step%(n_steps//10) == 0 and step != n_steps:
        print(f"{step/n_steps:2.0%} complete ({step}/{n_steps})")

    elif step == n_steps:
        # Get the final drainage network
        flow_router.run_one_step()

        print(f"100% complete ({n_steps}/{n_steps})")
        print()

    # Plotting code goes here
    mg_da = mg['node']['drainage_area']
    np_da = mg.node_vector_to_raster(mg_da, flip_vertically=True)
    contrib_nodes = np_da / (mg.dx * mg.dy)
    plt.figure()
    ax = plt.gca()
    ax.imshow(np.log10(contrib_nodes + 1))
    ax.set_title("log10(contributing_nodes + 1)")
    plt.show()

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:15 (9 by maintainers)

github_iconTop GitHub Comments

2reactions
SiccarPointcommented, Jul 9, 2020

This is associated with the refactoring I did in Cython between 2.06 and 2.07: https://github.com/landlab/landlab/compare/v2.0.0b6...v2.0.0b7

Taking a look to see if there’s a quick fix.

1reaction
SiccarPointcommented, Oct 6, 2020

I think I might have it. I’m struggling to pick apart the every detail of the debug process, so you should check this, but try running this branch: https://github.com/landlab/landlab/tree/SiccarPoint/fix_1219. (Because I’m messing with the cfuncs, make sure to rerun python setup.py develop first).

I think the problem was in declaring the type a little lower down wrongly, so we actively compared a python float with a c double within an array. My understanding of Cython said this should have been OK (these types are equivalent, according to the docs??), but realistically this was the only place the problem could have been. By switching in the ctype, I seem to have got an answer that floods the necessary nodes. Do you agree, as you know your debug code better than I do?

Read more comments on GitHub >

github_iconTop Results From Across the Web

The Landlab FlowDirectors: Components for Flow Direction
Directs flow by the D infinity method (Tarboton, 1997). Each node is assigned two flow directions, toward the two neighboring nodes that are...
Read more >
tutorials/the_FlowAccumulator.ipynb at next · landlab ... - GitHub
Landlab directs flow and accumulates it using two types of components: FlowDirectors use the topography to determine how flow moves between adjacent nodes....
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found