Outputs, plotting, and saved runs¶
Plotting¶
Every model carries a plotter at m.plot:
1D —
m.plot.profile()draws the long profile and returns(fig, axes).2D raw fields —
m.plot.map(field='bedrock')displays one stored raster without terrain smoothing and returns theAxes.2D channel profiles —
m.plot.profile()extracts a channel and returns(fig, axes).2D cartography —
m.plot.landscape()returns(fig, ax)and has the display presets described below.
All take an output index i (default -1, the last frame). map and
profile use field_min / field_max; landscape uses z_min / z_max
for terrain and H_min / H_max for ice colour. See 1D profile model and
2D landscape model for the method signatures.
Basin and steady-state summaries¶
largest_basins, largest_basins_history, sediment_history, hacks_law
and steady_state support plot=False to calculate their results without
creating a figure. Calculations, drawing and text formatting have separate
internal helpers. Calls are quiet by default; pass verbose=True to
hacks_law, largest_basins_history or steady_state for a printed summary.
history = m.plot.largest_basins_history(ref=-1, n_samples=20, plot=False)
sediment = m.plot.sediment_history(ref=-1, quantity='flux', plot=False)
ss_step = m.plot.steady_state(plot=False)
ref has the same meaning throughout the plotting API: it selects the output
frame used to extract a reference channel or choose basin outlets. Histories
keep those outlet identities across the run. The old i_ref keyword remains
accepted by both history methods, with a DeprecationWarning; use ref in
new calls. Positional reference arguments retain their meaning. Pass one
spelling; the legacy alias can override the default ref=-1.
hacks_law(i=..., ref=..., basin_rank=...) displays area at i on the channel
selected and fitted at ref. Its default reference remains the final output.
For largest_basins(i=...), the ranking and snapshot both come from i.
The historical t_start and t_end names in largest_basins_history denote
inclusive output-step indices, not years; negative indices count backward
from the final output. Empty/reversed windows and nonpositive sample counts
are rejected.
Existing results are preserved: basin methods return metric namespaces, and
steady_state returns the first matching output index (or -1). hacks_law
returns an Axes when plotting; with plot=False it returns fit parameters
(k_h, d, xo, L, in SI units), scatter arrays (distance_km, area_km2)
and fitted-curve arrays (fit_distance_km, fit_area_km2). Basin history results
also expose xt_over_L and zo_over_zELA. Data times stay in years even when
displayed axes use kyr or Myr.
Public summary plots share fonts, semantic colors and readable time units.
They use constrained layouts and fig_width in inches. Supply ax to compose
them into your own figure: a pair for largest_basins or steady_state, nine
axes in row order for largest_basins_history, and one Axes for hacks_law.
sediment_history accepts a single series Axes or a map/series pair. Supplied
axes must belong to one figure; other axes and figure titles are preserved.
The 1D _limit_cycle and _limit_cycle_phase methods are private research
helpers. Their former public names have been removed; their drawing behavior
is retained, and their appearance is outside the public plotting style contract.
Movies and viewers¶
animate_profile and animate_map retain fps=20; explicit fps determines
the encoded rate. Pass fps=None to derive it from interval in milliseconds
per frame. animate_landscape derives its rate from interval (default 42 ms)
unless an explicit fps is given, on both its serial and parallel render
paths. All three accept frames — a slice, a range or a sequence of
saved-frame indices, negatives counting from the end — and encode just those,
in the order given. A movie path may include .mp4; it is added only once. Existing run_id
filename conventions are preserved and take precedence over path.
Plotter-created movie figures close even if encoding fails. Supplied landscape figures/axes stay open; unrelated axes are preserved. All supplied axes must belong to the same figure. Map viewers update one image and colorbar in place. Sliders redraw on release to avoid queuing expensive renders while dragging.
Smooth and raw landscape views¶
landscape defaults to style='smooth', an atlas-style presentation. With no
other arguments it uses an 8-inch figure and shows bedrock and ice (field='bedrock+ice'); terrain is
supersampled 4×, Gaussian-de-staircased, hillshaded, and contoured, and the
same preset:
draws ice across the claimed sub-grid valley width (
ice_extent='footprint');shades it as a depth-graded translucent veil (
ice_shading='veil');fills the resolved trunk glaciers as true-width ribbons over that veil (
trunk_display='ribbons');hides nothing by thickness (
H_threshold=0) — the veil already fades thin ice out, so a gate would only delete real glacierets;smooths the ice outline by two sub-grid pixels, and outlines the current ice margin (
show_margin, formerlyshow_trimline) — the ribbons only, when they are drawn; andleaves connected-component removal and time averaging off.
This is intentionally a cartographic view, not an unmodified-cell view:
m.plot.landscape()
Pass field='bedrock' for the bare bed. The veil normalises column depth on
H_max, which defaults to the run-global hc_over_H * max H_out, so a still and
every frame of an animation put the same colour on the same depth;
ice_shading='flat' restores a single opaque ice_color, and an explicit
ice_cmap overrides both.
A trunk cell is any icy cell downstream of one whose claimed width
W = alpha_g*H already spans trunk_width_cells grid cells (default 1.0,
the width the grid can just resolve), the class carried along the receivers to
the terminus so a thinning tongue stays a trunk down to its toe; those cells
are traced and drawn at their true width, at a minimum opacity of
trunk_alpha (deep ice keeps the depth ramp’s own higher value), over a
veil built from the sub-resolution ice that is left. Pass
trunk_display='none' for the veil alone, or raise trunk_width_cells to
reserve the ribbons for the widest glaciers.
For inspection use style='raw'. It selects field='bedrock+ice' with flat
ice (ice_shading='flat'), renders one pixel per cell, disables terrain/ice
smoothing, hillshade, contours, the margin outline and the trunk ribbons
(trunk_display='none'), and shows channel-cell ice (ice_extent='cells').
Both presets leave H_threshold=0; the raw preset
additionally applies an upstream-area gate of
1e6 m² to suppress small-catchment specks. To display every ice-bearing cell,
disable that gate explicitly:
m.plot.landscape(style='raw', area_threshold=0)
Every explicit keyword overrides its preset value. animate_landscape
inherits the smooth preset; for densely sampled animation output, an optional
anti-flicker recipe is:
m.plot.animate_landscape(field='bedrock+ice',
H_threshold=50, ice_sigma_cells=3, ice_time_avg=2)
fps and frames are movie knobs rather than landscape ones — encode the
last twenty saved frames at ten frames per second with:
m.plot.animate_landscape(fps=10, frames=slice(-20, None))
H_threshold gates width-mean H in meters; area_threshold gates upstream
area in m². Both apply to footprint and ribbon sources. min_ice_cells cleans
the combined veil/ribbon mask once, so connected parts count as one glacier.
With ice_smoothing='field', the veil retains thickness near the threshold
until the smoothed field is thresholded; ribbon-source gating is separate.
sigma_cells and ice_sigma_cells
are subgrid pixels, so their native-grid strength changes with oversample.
ice_time_avg changes only the displayed ice layer, not terrain or stored
state. min_ice_cells=6 can remove small components, but it can also hide real
small glaciers and is therefore never enabled by a preset.
cross_section adds the section and hypsometry panels. A y in km reads that
one row and marks it on the map; 'mean' averages over the rendered rows of
the section grid instead — the interpolated oversample grid, whose two end
rows carry less ground than the interior ones — so the painted band is the
average ice column per unit x and vanishes where nothing is iced. The mean
section also shows the spread behind that mean — the ice thickness’s 25–75%
band shaded on the mean bed, the bed’s 25th and 75th percentiles as dashed
lines, both named in a legend — so a flat mean over one landscape reads
differently from a flat mean over several. The band is the spread of the ice
column, not an envelope of surfaces: a surface band would mostly repeat the
bed’s spread. An averaged profile has no single y to mark, so the map gets
no locator line, and it skips the lake layer — a reduced water table is not a
water table:
m.plot.landscape(cross_section='mean')
Two ice thicknesses¶
H_out is the width-mean ice thickness — the quantity the flux closures,
sliding laws, erosion laws and tau = rho*g*H*S consume, and the one
map(field='ice') and the profile panels plot (“Mean ice thickness”). What
landscape renders is instead the local column depth to the flat ice
surface: hc_over_H*H at the channel floor (default ratio 1.5), and deeper on carved flanks, which is
why its colorbar reads “Ice column depth (m)”.
Basin histories record unavailable channels/fits as NaN; summary statistics
exclude missing samples and report valid counts in result.stats[metric]['n'].
A single output cannot establish steady state, so steady_state() returns -1.
1D arrays¶
The 1D model stores NumPy arrays directly. Spatial output has shape
(nx, n_output); output_times and zELA_out have length n_output.
attribute |
meaning |
|---|---|
|
ice-surface elevation and channel-floor bed elevation |
|
width-mean ice thickness |
|
ice and water flux |
|
erosion rate |
|
basal shear stress and sliding velocity |
|
local ice mass-balance rate |
|
divide elevation and position, shaped |
|
terminus position and elevation, shaped |
The first dimension of spatial arrays follows m.x, which runs from L down
to zero. The 1D model has no built-in run save/load method.
2D dataset and arrays¶
The standalone driver builds m.ds_out as an in-memory xarray Dataset; a
normal run does not create a Zarr store. The convenience attributes below are
NumPy views of that dataset and have shape (time, y, x) unless noted.
attribute |
|
meaning |
|---|---|---|
|
reconstructed or |
ice-surface elevation |
|
|
channel-floor bed elevation |
|
|
width-mean ice thickness |
|
|
upstream drainage area |
|
|
ice flux |
|
|
water flux |
|
|
erosion rate |
|
|
per-step rock removed: Δ |
|
|
receiver graph |
|
|
topological routing stack |
|
|
basin identifiers |
|
reconstructed from receivers/grid |
node-to-receiver distance |
|
|
flexural displacement; present when |
|
|
sediment throughput; present when |
|
|
cumulative eroded volume; present when |
|
|
volume leaving the domain across each edge, shaped |
|
|
its running total, shaped |
track_sediment chooses what the routed sediment is reported as: False
(off, the default), True or 'basin' (the per-node rasters above),
'edge' (the per-domain-edge totals only) or 'both'. Any other value is
rejected.
The two (time, side) arrays carry the side coordinate
['left', 'right', 'bottom', 'top'] — boundary_status order, the same keys a
per-side bl dict takes. Each entry is this step’s routed sediment__flux
summed over that edge’s outlet ring, in m³ per step, with each corner node
counted exactly once (an outlet left/right edge owns its corners, otherwise the
bottom/top edge does). Only a 'fixed_value' edge is an outlet, so a 'core'
or 'looped' edge reads NaN rather than zero: nothing can leave there, which
is not the same as nothing leaving. With every side 'fixed_value', the four
entries sum to the whole domain’s denuded volume for that step.
Mode B/C stores the bed as topography__elevation and reconstructs the ice
surface as z = zb + 1.5*H. Mode A stores the ice surface as topography and a
separate bed field. 1.5 is the channel-floor ratio HC_OVER_H; H remains
the width-mean thickness used by the physics.
The true-state output convention¶
Outputs report the true (zb, H) everywhere; base level is not a presentation
floor in stored state. With nonzero bl, a drowned or relict bed may lie below
the waterline. That is bed memory, not a failed clamp. Ocean and lake surfaces
are display layers used by landscape, not substitutions in the output arrays.
Files and output locations¶
Plot methods return Matplotlib objects and do not save unless their save/path argument requests it. Relative names are placed beneath the current working directory:
images:
model_outputs/images/movies:
model_outputs/movies/2D saved runs:
model_outputs/saved_models/
An absolute output name is used as given.
MP4 animation methods use Matplotlib’s ffmpeg writer and require a system
ffmpeg executable on PATH.
Saving and loading a 2D run¶
path = m.save('run_name') # adds .pkl; returns the path written
from siim.siim2d import load
restored = load('run_name')
save writes atomically and records a save-format schema version, the
producing SIIM version, the concrete model class, the original user-parameter
dictionary, and ds_out. load checks the envelope, schema version, model
identity, required keys, and value types; the producing package version is
recorded for diagnostics. Unsupported, malformed, and old unversioned payloads
fail with a directed error; cross-version migration is not promised.
These files use Python pickle. Load only files you trust: unpickling can execute code before SIIM can validate the decoded object. The validation detects incompatible SIIM state; it is not a security sandbox.
The saved parameter dictionary preserves the user’s original inputs. External resources referenced there, such as an initial-topography CSV path, are not embedded automatically and must still be available when the model is rebuilt. Reloaded compatible runs carry their dataset, convenience arrays, and plotter, so they can be plotted and post-processed without rerunning.