"""Stage `erosion`: implicit stream-power incision (Braun & Willett 2013) + km-scale hillslope smoothing."""
from __future__ import annotations
import numpy as np
from .config import params
from .crust import CRATON, LIP, OCEANIC, POST_OROGEN, PRE_OROGEN, RIFT, SCAR, VOLCANO
from .elevation import solve_sea_level_connected
from .graph import (accumulate, drop_smooth_cache, ocean_mask, priority_flood, receiver_levels, smooth_km,
steepest_receivers)
DEFAULTS = {"min_sea_km2": 5.0e6, "steps": 12, "dt_myr": 5.0, "k": 0.02, "m": 0.5, "hillslope_km": 25.0, "sediment_fill": 0.15}
K_MULT = {OCEANIC: 0.0, CRATON: 1.5, PRE_OROGEN: 1.2, POST_OROGEN: 1.0, RIFT: 1.0, LIP: 0.6, SCAR: 0.9, VOLCANO: 0.8}
def erode(g, z, kmult, P):
z = np.asarray(z, dtype=np.float64).copy()
ar = np.arange(g.n)
for _ in range(int(P["steps"])):
ocean = ocean_mask(g, z, P["min_sea_km2"])
zb = np.where(ocean, 0.0, z) # base level = sea level, not the seafloor
zf = priority_flood(g, zb, ocean)
zb = np.where(ocean, zb, zb + P["sediment_fill"] * (zf - zb)) # sediment infills closed basins
recv, _, dist = steepest_receivers(g, zf)
recv = np.where(ocean, ar, recv)
levels = receiver_levels(recv)
area = accumulate(recv, levels, g.area_km2)
F = P["k"] * kmult * P["dt_myr"] * area ** P["m"] / np.where(np.isfinite(dist), dist, 1.0)
F[ocean] = 0.0
znew = zb.copy()
for lv in levels[1:]:
r = recv[lv]
znew[lv] = np.minimum(zb[lv], (zb[lv] + F[lv] * znew[r]) / (1.0 + F[lv]))
znew = smooth_km(g, znew, P["hillslope_km"], keep=True) # hillslope/sub-grid smoothing, fixed length in km
z = np.where(ocean, z, znew)
drop_smooth_cache(g)
return z
def run(ctx) -> dict:
g = ctx.grid
P = params(ctx.cfg, "erosion", DEFAULTS)
z0, age = ctx.need("elevation_m", "age_class")
kmult = np.vectorize(K_MULT.get)(age).astype(np.float64)
z0 = np.asarray(z0, dtype=np.float64)
added = np.asarray(ctx.data.get("land_added", np.zeros(g.n, bool)), bool) # land the sea-level solve didn't
target = ctx.cfg["build"]["land_fraction"] + g.area_km2[added & (z0 > 0)].sum() / g.area_km2.sum() # see
z = erode(g, z0, kmult, P)
z = solve_sea_level_connected(g, z, target, P["min_sea_km2"])
z = np.maximum(z, -11000.0)
# the sea is the connected world ocean; a separate basin ≥ min_sea_km2 shaped the coasts above as sea and stays
# open water for the climate (open_water), but its water is a lake (hydrology), not sea
return {"elevation_eroded_m": z.astype(np.float32), "ocean": ocean_mask(g, z, np.inf),
"open_water": ocean_mask(g, z, P["min_sea_km2"])}