5. Preprocessing a surface

Pit filling and swamp filling …

Take the previous mesh with random noise

Quagmire allows the user to specify the number of downhill pathways to model landscape evolution. This is set using:

mesh.downhill_neighbours = 1
mesh.update_height(height)

where an integer specifies the number of downhill neighbour nodes (receipients) that will receive a packet of information from a donor node. The QuagMesh object can also be initialised with:

mesh = QuagMesh(DM, downhill_neighbours=1)

to specify the number of downhill neighbours (default is 2).

In this notebook we use a landscape function with many outflow points to examine the effect of varying the number of recipient nodes on catchment area, stream lengths, and outflow fluxes.

import matplotlib.pyplot as plt
import numpy as np
from quagmire import tools as meshtools
from quagmire import function as fn
%matplotlib inline
from quagmire import QuagMesh 
from quagmire import QuagMesh # all routines we need are within this class
from quagmire import QuagMesh


minX, maxX = -5.0, 5.0
minY, maxY = -5.0, 5.0,

spacingX = 0.05
spacingY = 0.05

x, y, simplices = meshtools.elliptical_mesh(minX, maxX, minY, maxY, spacingX, spacingY, 1.)

DM = meshtools.create_DMPlex(x, y, simplices, refinement_levels=2)

mesh = QuagMesh(DM)

x = mesh.coords[:,0]
y = mesh.coords[:,1]

print( "\nNumber of points in the triangulation: {}".format(mesh.npoints))
Underlying Mesh type: TriMesh
0 - Delaunay triangulation 0.46267416599999933s
0 - Calculate node weights and area 0.043066750000001264s
0 - Find boundaries 0.02290166599999921s
0 - cKDTree 0.02247120799999891s
0 - Construct neighbour cloud arrays 0.6619863330000015s, (0.44069049999999876s + 0.2212610419999983s)
0 - Construct rbf weights 0.05931295799999958s

Number of points in the triangulation: 156925
# topography

radius  = np.sqrt((x**2 + y**2))
theta   = np.arctan2(y,x)+0.1

height  = np.exp(-0.025*(x**2 + y**2)**2) + 0.25 * (0.2*radius)**4  * np.cos(5.0*theta)**2 ## Less so
height  += 0.5 * (1.0-0.2*radius)
height  -= height.min()

## Add some pits 

hrand0 = np.where(np.random.random(height.shape)>0.995, -0.1, 0.0)


## Add smoothed random noise to make some "lakes" 

rbf_smoother = mesh.build_rbf_smoother(0.05, iterations=3)
h0 = mesh.add_variable(name="h0")
h0.data = np.where(np.random.random(height.shape)>0.995, -0.33, 0.0)

hrand1 = 25.0 * rbf_smoother.smooth_fn(h0, iterations=25).evaluate(mesh)


# randpts1 = np.where(np.random.random(height.shape)>0.995, -1.0, 0.0)
# hrand1   = 10.0 * rbf_smoother.smooth(randpts1, iterations=10)

heightn = np.maximum(0.0, height + hrand0 + hrand1)
with mesh.deform_topography():
    
    mesh.downhill_neighbours = 2
    mesh.topography.data = heightn

# let's use a rainfall proportional to height (any choice is ok)

rainfall_fn = mesh.topography**2

low_points = mesh.identify_low_points()
low_point_coords = mesh.coords[low_points] 
print(low_points.shape)
0 - Build downhill matrices 3.843434333000001s
0 - Build upstream areas 1.5636943749999972s
(886,)
h0.data = mesh.topography.data

mesh

import k3d

lowsxyz = np.column_stack([mesh.tri.points[low_points], heightn[low_points]])
xyz  = np.column_stack([mesh.tri.points, height])
xyz2 = np.column_stack([mesh.tri.points, heightn])

plot = k3d.plot(camera_auto_fit=False, grid_visible=False, lighting=0.75)

indices = mesh.tri.simplices.astype(np.uint32)

surface = k3d.mesh(xyz2, indices, wireframe=False, flat_shading=True, side="double",
                    color_map = k3d.colormaps.matplotlib_color_maps.Cividis,
                    attribute=mesh.topography.data,
                    color_range = [0.0,1.5]
                   )

lows    = k3d.points(lowsxyz, point_size=0.05, color=0xFF0000)

plot += surface + lows

plot.display()
/Users/lmoresi/mambaforge/envs/underworld/lib/python3.9/site-packages/traittypes/traittypes.py:97: UserWarning: Given trait value dtype "float64" does not match required type "float32". A coerced copy has been created.
  warnings.warn(
rainfall_fn = mesh.topography**2
flowrate_fn = mesh.upstream_integral_fn(rainfall_fn)
stream_power_fn = flowrate_fn ** 2.0 * mesh.slope ** 2.0 * mesh.mask

cumulative_rain_n1 = mesh.upstream_integral_fn(rainfall_fn).evaluate(mesh)
import k3d

lowsxyz = np.column_stack([mesh.tri.points[low_points], heightn[low_points]])
xyz  = np.column_stack([mesh.tri.points, height])
xyz2 = np.column_stack([mesh.tri.points, heightn])
indices = mesh.tri.simplices.astype(np.uint32)



plot = k3d.plot(camera_auto_fit=False, grid_visible=False, lighting=0.75)


surface = k3d.mesh(xyz2, indices, wireframe=False, flat_shading=True, side="double",
                    color_map = k3d.colormaps.matplotlib_color_maps.Blues,
                    attribute=stream_power_fn.evaluate(mesh),
                    color_range = [0.0,0.01]
                   )

lows    = k3d.points(lowsxyz, point_size=0.05, color=0xFF0000)

plot += surface + lows

plot.display()
## Pit filling algorithm in quagmire

mesh1p = QuagMesh(DM)

rainfall_fn_1p = mesh1p.topography**2
cumulative_rain_n1p = mesh1p.upstream_integral_fn(rainfall_fn_1p)
stream_power_n1p    = cumulative_rain_n1p ** 2.0 * mesh1p.slope ** 2.0 * mesh1p.mask



with mesh1p.deform_topography():
    mesh1p.topography.data = mesh.topography.data
Underlying Mesh type: TriMesh
0 - Delaunay triangulation 0.3040237499999421s
0 - Calculate node weights and area 0.0241118330000063s
0 - Find boundaries 0.01836920799996733s
0 - cKDTree 0.019008834000032948s
0 - Construct neighbour cloud arrays 0.6636431250001351s, (0.43505416700008936s + 0.2285542079998777s)
0 - Construct rbf weights 0.06518099999993865s
0 - Build downhill matrices 2.430833333999999s
0 - Build upstream areas 1.5887439580001228s
mesh1p.low_points_local_patch_fill(its=2, smoothing_steps=1, fraction=0.25)
Low point local patch fill
0 - Build downhill matrices 2.4737774160000754s
0 - Build downhill matrices 2.4994586660000095s
Low point local patch fill  7.116479208999863  seconds
0 - Build upstream areas 1.5854269160001877s
mesh1p.identify_low_points().shape
(193,)
import k3d

low_points2 = mesh1p.low_points
height2 = mesh1p.topography.data

lowsxyz = np.column_stack([mesh1p.tri.points[low_points], heightn[low_points]])
lows2xyz = np.column_stack([mesh1p.tri.points[low_points2], height2[low_points2]])

xyz  = np.column_stack([mesh.tri.points, mesh1p.topography.data])
xyz2 = np.column_stack([mesh.tri.points, heightn])
indices = mesh.tri.simplices.astype(np.uint32)

plot = k3d.plot(camera_auto_fit=False, grid_visible=False, lighting=0.75)


surface = k3d.mesh(xyz, indices, wireframe=False, flat_shading=True, side="double",
                    color_map = k3d.colormaps.matplotlib_color_maps.Blues,
                    attribute=cumulative_rain_n1p.evaluate(mesh),
                    color_range = [0.0,1.0]
                   )

lows    = k3d.points(lowsxyz, point_size=0.05, color=0xFF0000)
lows2    = k3d.points(lows2xyz, point_size=0.05, color=0x0000FF)


plot += surface + lows + lows2 

plot.display()
## Quagmire also has a swamp filling algorithm
## NOTE this is much more efficient if it follows the pit filling

mesh1s = QuagMesh(DM)
rainfall_fn_1s = mesh1s.topography**2

with mesh1s.deform_topography():
    mesh1s.topography.data = mesh1p.topography.data
    
print("There are still {} low points".format(mesh1s.identify_low_points().shape))
Underlying Mesh type: TriMesh
0 - Delaunay triangulation 0.3330004590000044s
0 - Calculate node weights and area 0.0643369580000126s
0 - Find boundaries 0.018808749999834617s
0 - cKDTree 0.0196499579999454s
0 - Construct neighbour cloud arrays 0.6992000420000295s, (0.44694904200014207s + 0.25221525000006295s)
0 - Construct rbf weights 0.058850083999914204s
0 - Build downhill matrices 2.4509212079999543s
0 - Build upstream areas 1.571919041000001s
(193,)
for i in range(0,50):
    mesh1s.low_points_swamp_fill(ref_height=-0.01)
    
    # In parallel, we can't break if ANY processor has work to do (barrier / sync issue)
    low_points2 = mesh1s.identify_global_low_points()
    
    print("{} : {}".format(i,low_points2[0]))
    if low_points2[0] == 0:
        break
120  iterations, time =  1.2702137920000496
Build low point catchments -  1.2706606249998913  seconds
0  Sort spills -  0.010811040999897159
0  Gather spill data -  0.0019107919999896694
0  Sort all spills -  0.00026254099998368474
0 - Build downhill matrices 2.4735215000000608s
Low point swamp fill  3.997965583999985  seconds
0 - Build upstream areas 1.8586511250000513s
0 : 96
178  iterations, time =  1.3629859999998644
Build low point catchments -  1.3634352080000554  seconds
0  Sort spills -  0.005808292000210713
0  Gather spill data -  7.35829999030102e-05
0  Sort all spills -  9.187499995277904e-05
0 - Build downhill matrices 2.4238212090001525s
Low point swamp fill  3.935164916999838  seconds
0 - Build upstream areas 1.8560250830000768s
1 : 70
263  iterations, time =  1.4846040420000008
Build low point catchments -  1.485055124999917  seconds
0  Sort spills -  0.004419750000124623
0  Gather spill data -  6.962499992368976e-05
0  Sort all spills -  8.179099995686556e-05
0 - Build downhill matrices 2.443070875000103s
Low point swamp fill  4.039379707999842  seconds
0 - Build upstream areas 2.0338651250001476s
2 : 62
261  iterations, time =  1.5380792920000204
Build low point catchments -  1.5385104590000083  seconds
0  Sort spills -  0.003909999999905267
0  Gather spill data -  0.00012487499998314888
0  Sort all spills -  7.504199993491056e-05
0 - Build downhill matrices 2.5028409589999683s
Low point swamp fill  4.142744708000009  seconds
0 - Build upstream areas 2.005432916000018s
3 : 70
262  iterations, time =  1.7582492910000838
Build low point catchments -  1.758841208999911  seconds
0  Sort spills -  0.006341291000126148
0  Gather spill data -  0.00023295899995900982
0  Sort all spills -  0.00010766600007627858
0 - Build downhill matrices 3.0342551250000724s
Low point swamp fill  4.935335333000012  seconds
0 - Build upstream areas 2.779530582999996s
4 : 64
265  iterations, time =  2.100522917000035
Build low point catchments -  2.10303458400017  seconds
0  Sort spills -  0.003463125000052969
0  Gather spill data -  0.0029077920000872837
0  Sort all spills -  9.208300002683245e-05
0 - Build downhill matrices 3.149582374999909s
Low point swamp fill  5.405693749999955  seconds
0 - Build upstream areas 2.578506583000035s
5 : 46
266  iterations, time =  1.7416526659999363
Build low point catchments -  1.742177791000131  seconds
0  Sort spills -  0.002930999999989581
0  Gather spill data -  7.379199996648822e-05
0  Sort all spills -  5.487500015988189e-05
0 - Build downhill matrices 2.9200388749998183s
Low point swamp fill  4.733386958999972  seconds
0 - Build upstream areas 2.319822375000058s
6 : 38
267  iterations, time =  1.8350086250000004
Build low point catchments -  1.8355307499998617  seconds
0  Sort spills -  0.0033648750002157612
0  Gather spill data -  7.483300009880622e-05
0  Sort all spills -  5.600000008598727e-05
0 - Build downhill matrices 2.569668374999992s
Low point swamp fill  4.467106000000058  seconds
0 - Build upstream areas 2.469780124999943s
7 : 30
267  iterations, time =  1.621527874999856
Build low point catchments -  1.6220797500000117  seconds
0  Sort spills -  0.0024131250002028537
0  Gather spill data -  8.974999991551158e-05
0  Sort all spills -  4.429099999470054e-05
0 - Build downhill matrices 2.603554832999862s
Low point swamp fill  4.277322541999865  seconds
0 - Build upstream areas 2.067419541999925s
8 : 28
267  iterations, time =  1.6253727079999862
Build low point catchments -  1.6258912910000163  seconds
0  Sort spills -  0.002284459000065908
0  Gather spill data -  9.158300008493825e-05
0  Sort all spills -  3.85000000733271e-05
0 - Build downhill matrices 2.548146124999903s
Low point swamp fill  4.217911333000075  seconds
0 - Build upstream areas 2.6503998749999482s
9 : 15
267  iterations, time =  1.6346717500000523
Build low point catchments -  1.6351971250001043  seconds
0  Sort spills -  0.0018603329999677953
0  Gather spill data -  6.962499992368976e-05
0  Sort all spills -  0.000473916999908397
0 - Build downhill matrices 2.594370875000095s
Low point swamp fill  4.272328667000011  seconds
0 - Build upstream areas 2.288963542000147s
10 : 12
267  iterations, time =  1.8006609159999698
Build low point catchments -  1.8014930829999685  seconds
0  Sort spills -  0.0017905830000017886
0  Gather spill data -  7.483399986085715e-05
0  Sort all spills -  0.00014474999989033677
0 - Build downhill matrices 3.254716791999954s
Low point swamp fill  5.0976602920000005  seconds
0 - Build upstream areas 2.1872506670001712s
11 : 10
267  iterations, time =  1.6066588749999937
Build low point catchments -  1.6071492080000098  seconds
0  Sort spills -  0.0014944170000035228
0  Gather spill data -  9.075000002667366e-05
0  Sort all spills -  3.300000003036985e-05
0 - Build downhill matrices 2.5504909999999654s
Low point swamp fill  4.190495874999897  seconds
0 - Build upstream areas 2.1012557080000533s
12 : 5
276  iterations, time =  1.5801827910001975
Build low point catchments -  1.580588834000082  seconds
0  Sort spills -  0.0013571249999131396
0  Gather spill data -  6.670800007668731e-05
0  Sort all spills -  2.212499998677231e-05
0 - Build downhill matrices 2.490343208000013s
Low point swamp fill  4.101697958000159  seconds
0 - Build upstream areas 2.1326142080001773s
13 : 7
277  iterations, time =  1.7845102500000394
Build low point catchments -  1.7850002919999497  seconds
0  Sort spills -  0.0023967919998995058
0  Gather spill data -  7.13749998340063e-05
0  Sort all spills -  2.375000008214556e-05
0 - Build downhill matrices 2.4884266659998957s
Low point swamp fill  4.30308137499992  seconds
0 - Build upstream areas 2.1146000000001095s
14 : 5
278  iterations, time =  1.5713676249999935
Build low point catchments -  1.5717985830001453  seconds
0  Sort spills -  0.0011000420001892053
0  Gather spill data -  0.00017608300004212651
0  Sort all spills -  3.087499999310239e-05
0 - Build downhill matrices 2.5157320420000815s
Low point swamp fill  4.114825457999814  seconds
0 - Build upstream areas 2.0815340420001576s
15 : 3
276  iterations, time =  1.5753376669999852
Build low point catchments -  1.5758753329998854  seconds
0  Sort spills -  0.0010460419998707948
0  Gather spill data -  6.816700010858767e-05
0  Sort all spills -  1.9374999965293682e-05
0 - Build downhill matrices 2.4807477500000914s
Low point swamp fill  4.085463207999965  seconds
0 - Build upstream areas 2.1508202499999243s
16 : 1
279  iterations, time =  1.5369610840000405
Build low point catchments -  1.5374124999998457  seconds
0  Sort spills -  0.0006748330001755676
0  Gather spill data -  7.76249999034917e-05
0  Sort all spills -  1.9416999975874205e-05
0 - Build downhill matrices 2.540103290999923s
Low point swamp fill  4.099138374999939  seconds
0 - Build upstream areas 2.0256681250000383s
17 : 0
cumulative_rain_n1s = mesh1s.upstream_integral_fn(rainfall_fn_1s)
stream_power_n1s    = cumulative_rain_n1s ** 2.0 * mesh1s.slope ** 2.0 * mesh1s.mask
import k3d

low_points2 = mesh1p.low_points
height2 = mesh1p.topography.data

lowsxyz = np.column_stack([mesh1p.tri.points[low_points], heightn[low_points]])
lows2xyz = np.column_stack([mesh1p.tri.points[low_points2], height2[low_points2]])

xyz  = np.column_stack([mesh1s.tri.points, mesh1s.topography.data])
xyz2 = np.column_stack([mesh1p.tri.points, mesh1p.topography.data])
indices = mesh.tri.simplices.astype(np.uint32)

plot = k3d.plot(camera_auto_fit=False, grid_visible=False, lighting=0.75)


surface = k3d.mesh(xyz2, indices, wireframe=False, flat_shading=True, side="double",
                    color_map = k3d.colormaps.matplotlib_color_maps.Blues,
                    attribute=cumulative_rain_n1s.evaluate(mesh),
                    color_range = [0.0,1.0]
                   )

lakes_surface = k3d.mesh(xyz-(0.0,0.0,0.005), indices, wireframe=False, flat_shading=True, side="double",
                    color=0x0010C0, opacity=0.5
                   )



lows    = k3d.points(lowsxyz, point_size=0.05, color=0xFF0000)
lows2    = k3d.points(lows2xyz, point_size=0.05, color=0x0000FF)


plot += surface + lows + lows2 + lakes_surface

plot.display()