Subglacially

WhereTheWaterFlows.Subglacially extends core routing for subglacial settings.

Compared with plain waterflows, it provides:

  • routing based on Shreve (1972) hydraulic potential,
  • pressure-melting/supercooling-aware flow deflection,
  • lake-depth diagnostics,
  • optional per-sink flux aggregation.

This module is aimed at routing water at the ice-bed interface below glaciers and ice sheets using the Shreve hydraulic potential (Shreve, 1972). Optionally, pressure-melting-point effects are accounted for (Röthlisberger, 1972).

Recent applications using this workflow include Malczyk et al. (2023), Delaney et al. (2023), Ogier et al. (2025), Horgan et al. (2025), Washam et al. (2026) and Ogier et al. (2026).

Hydraulic potential

The Shreve hydraulic potential φ used for routing is

φ = f · H · (ρ_w/ρ_w) + (z_s − H)

where H is ice thickness, z_s is surface elevation, f is the flotation fraction, and ρ_i, ρ_w are ice and water density (units in m water level). At full flotation (f = 1) this is the standard Shreve potential. The bed elevation is zb = zs − H.

Water flows down the gradient of φ, not down the gradient of the bed.

Minimal run

using WhereTheWaterFlows, CairoMakie
using Random; Random.seed!(42)

const WWFS = WhereTheWaterFlows.Subglacially

n = 90
dx = 100.0
x = y = range(0, step=dx, length=n)

surfdem = 1200 .+ 0.015.*x .+ 0.015.*y' .+ 4 .* randn(n, n)
beddem = 950 .+ 0.01.*x .+ 0.01.*y' .- 100 .* exp.(-((x .- x[end]/2).^2 .+ (y' .- y[end]/2).^2) ./ (2 * 4000.0^2))
surfdem = max.(surfdem, beddem .+ 10.0)

out = WWFS.waterflows_subglacial(surfdem, beddem, dx; gamma=WWFS.GAMMA)

plt_area(x, y, out.routing.area.total)
Example block output

Set gamma=0 to disable deflection/supercooling effects and recover Shreve-potential-only behaviour.

Output structure

waterflows_subglacial returns a nested named tuple with four top-level keys:

routing

All fields from the core waterflows call, plus phi (the hydraulic potential used for routing):

  • area: named tuple with four routed fields:
    • total: total accumulated water flux (source + melt; in m³/s when source is in m/s)
    • extra: accumulated flux from melt only (dissipation + pressure melting)
    • dissipation_melt_rate: local melt rate from energy dissipation [m/s]
    • pressure_melt_rate: local melt rate from pressure-melting-point effects [m/s]
  • slen, dir, nout, nin, sinks, pits, c, bnds: same as waterflows (see Tutorial)
  • phi: the Shreve hydraulic potential field

pressmelt

Diagnostics from the flow-deflection step:

  • sc_locs: Boolean mask of cells where supercooling occurs (outflow edge is supercooled)
  • kappas: deflection angle in units of π/4 at each cell
  • dir_og: flow directions before deflection

lakes

Lake-depth diagnostics derived from fill_dem applied to the hydraulic potential:

  • depth_fixed_surface: lake depth assuming the ice surface is fixed [m water equivalent]
  • depth_free_surface: lake depth assuming the ice surface adjusts freely [m]

sink_catchments

Only populated when ctch_sinks is non-empty:

  • masks: vector of BitMatrix, one per sink set
  • fluxes: named tuple (total, dissipation, pressmelt), each a vector of total flux values (m³/s) for the corresponding sink set

Key controls

Argument / KeywordDefaultMeaning
surfdem, beddem—Surface and bed elevation arrays (same size)
dx—Grid spacing in metres (must be equal in x and y)
floatfrac1Flotation fraction (scalar or array); 1 = full flotation
sourceones(size(surfdem))Meltwater input per unit area [m/s]
maskall trueActive routing mask; false cells are set to NaN in φ
gammaGAMMA (≈−0.31)Röthlisberger constant controlling deflection strength; set to 0 to disable
avoid_scfalseIf true, supercooled cells become barriers (mass is lost there)
ctch_sinks[]Sink-area sets for per-catchment flux diagnostics
rhow, rhoi1000, 910Water and ice densities [kg m⁻³]
drain_pits, bnd_as_sink, nan_as_sinktrueInherited from core routing

Defining outlet groups (ctch_sinks)

ctch_sinks groups routing sinks for per-outlet masks and flux aggregation. Each group must contain only actual routing cells. Typically these would be cells from out.routing.sinks but also interior cells can be used (e.g. for the catchment of a subglacial lake). Using arbitrary boundary indices can silently include inactive cells (for example off-glacier boundary cells that are barriers), which can produce zero-flux groups and misleading accounting.

The robust pattern for partitioning sinks at the routing boundary is a two-step workflow:

  1. Run one deterministic routing pass without ctch_sinks to discover active sinks.
  2. Partition out.routing.sinks into outlet groups.
  3. Re-run routing (or Monte Carlo) with this partition as ctch_sinks.

This examples, continuing from above, shows the workflow:

# Step 1: discover active sinks
out_first = WWFS.waterflows_subglacial(surfdem, beddem, dx; gamma=WWFS.GAMMA)
all_sinks = out_first.routing.sinks

# Step 2: partition sinks by position (simple example)
nx, ny = size(surfdem)
south_sinks = filter(ci -> ci[1] <= nx ÷ 2 && ci[2] == 1, all_sinks)
east_sinks = filter(ci -> ci[1] == 1 && ci[2] >  ny ÷ 2, all_sinks)
ctch_sinks = [south_sinks, east_sinks]

# Optional check for an exhaustive non-overlapping partition (actually not the case here)
#@assert length(all_sinks) == sum(length.(ctch_sinks))

# Step 3: run again with outlet groups
out = WWFS.waterflows_subglacial(surfdem, beddem, dx;
                                 gamma=WWFS.GAMMA,
                                 ctch_sinks=ctch_sinks)

# plot the two catchments with main drainage pathways overlain
c = out.sink_catchments.masks[2].*2.0 .+ out.sink_catchments.masks[1]; c[c.==0].=NaN
f = heatmap(x, y, c)
heatmap!(x, y, out.routing.area.total.>1e6, alpha=0.3, transparency=true)
f
Example block output

For exhaustive outlet partitions, groups are typically non-overlapping. The API also allows overlapping groups if that is useful for a specific application.

For real datasets, WWFS.catchment_sinks is usually the most practical approach: it derives sink-group partitions from outlet polygons.

Suggested examples

  • examples/wwfs-simple.jl: quick intro
  • examples/subglacially/valley-glacier.jl: valley setup
  • examples/subglacially/ice-sheet-margin-shmip.jl: SHMIP-like geometry
  • examples/subglacially/ice-cap-full-workflow.jl: full deterministic + stochastic workflow with two-step ctch_sinks partitioning and per-outlet flux statistics

You can run those from the examples environment with, e.g.:

include("subglacially/valley-glacier.jl")
include("subglacially/ice-cap-full-workflow.jl")

Physical notes

  • Inputs are expected on a regular Cartesian grid with scalar dx.
  • Routing assumes local D8 connectivity.
  • Supercooling diagnostics (sc_locs) are a model approximation and should be interpreted with care.
  • Negative dissipation_melt_rate can occur where the breach algorithm routes water uphill out of a filled depression; the corresponding melt going into the depression balances this.

See also: Tutorial, Examples, and API Reference.