fastscape integration — siim.fastscape¶
The public, composable surface for using siim’s glacial-erosion physics inside
your own fastscape/xsimlab model: the assembly helpers (glacial_processes,
glacial_model) and the @xs.process classes they wire in. This is the
optional adapter (pip install siim-lem[fastscape]; conda for the Fortran
backend) — the standalone siim.siim2d model runs its own in-house driver and
does not require it, though siim2d(...).run(driver='xsimlab') will drive the
model through this adapter when the stack is present.
Public surface — siim.fastscape¶
siim.fastscape — siim’s glacial processes as composable fastscape citizens.
This is the public surface for using siim’s glacial-erosion physics inside your
own fastscape/xsimlab model. The names exported here are @xs.process classes
that drop into fastscape’s basic_model once its stock
'spl' and 'drainage' slots are removed. Build the override dict with
glacial_processes(), or get a ready-to-run model from glacial_model():
from fastscape.models import basic_model
from siim.fastscape import glacial_processes
model = basic_model.drop_processes(['spl', 'drainage']).update_processes(
glacial_processes(mode='B'))
For the full coupled model with forcing, I/O and plotting, use
siim.siim2d.siim instead. Its default in-house driver calls the shared
framework-free numerical core directly; its optional driver='xsimlab' path
assembles these process classes. The two paths share the same law parameters
and step kernels and are kept aligned by driver-conformance tests.
Implementation lives in submodules: the glacial @xs.process classes in
siim.fastscape.processes, and two reusable generic forcing processes
(WaveUplift, PlateauSurface) in siim.fastscape.forcing.
API stability¶
siim is pre-1.0 (0.x). This surface is intended to stay stable, but may still
evolve before 1.0 — it is not a frozen contract yet. Modes A, B and C are
settled: the sub-grid width-carving path (carve=True → GlacialSPLModeC)
is a mode-B citizen too (bed + ice thickness + carve), and is siim’s flagship
carved mode.
- siim.fastscape.glacial_processes(*, mode='B', carve=None, routing='single', router_backend=None, flexure=False, sediment=False, trunk_surface=None, numerics_backend='inhouse')[source]¶
Build the slot→process-class override dict for fastscape’s
basic_model.Apply the returned dict after dropping the stock fluvial slots:
basic_model.drop_processes(['spl', 'drainage']).update_processes( glacial_processes(...))
or use
glacial_model(), which does exactly that. The optionaldriver='xsimlab'path ofsiim.siim2d.siimbuilds its model from this function; the default in-house driver consumes the same shared law records and numerical kernels without xsimlab.Every flag defaults off, except that mode C (
mode='C', ormode='B'withcarve=True) resolvestrunk_surfaceto the mode-C standard exactly assiim.siim2d.siimdoes (siim.constants.mode_c_standard()), soglacial_model(mode='C')with no extra inputs is native mode C.- Parameters:
mode ({'B', 'A', 'C'}) – One-breath taxonomy:
'A'= ice-surface state (carved troughs heal instantly);'B'= bedrockzb+ ice-thicknessHstate (bed memory; siim’s native regime);'C'= B + sub-grid width carving (equivalent tomode='B', carve=True— impliescarve, and an explicitcarve=Falsecontradicts it). Case-insensitive.carve (bool, optional) – Mode-B sub-grid glacier-width carving — wires
GlacialSPLModeC(the citizen bed-and-thickness class plus the carve). Ignored formode='A'. Defaults off here so the advertised default is the plain Mode-B citizen.siim.siim2d.siimdefaults to Mode C, which enables carving; explicit Mode B leaves it off unless requested.mode='C'is the alias for turning it on.trunk_surface (bool or None, optional) – Fabricated trunk-surface routing (mode B/C) — swaps in
TrunkSurfaceToErodeso routing + accumulation converge trunk flow onto the centerline (the centerline’s raw flux is then the full cross-section).None(default) resolves to the mode-C standard: on for mode C, off for plain mode B. An explicitTrue/Falsewins.routing_relaxfollows the provider class here (0.5 onTrunkSurfaceToErode, 0 onGlacialSurfaceToErode) wheresiim.siim2d.siimfollows the mode, so two explicit combinations differ unlesssurf2erode__routing_relaxis set: mode B withtrunk_surface=True(0.5 here, 0 in siim2d) and mode C withtrunk_surface=False(0 here, 0.5 in siim2d). Ignored formode='A'.routing ({'single', 'dinf'}, optional) – D8 single-flow (
'single', default) or D-infinity ('dinf').router_backend ({'inhouse_d8'}, optional) – Single-flow routing backend (
constants.ROUTER_DEFAULT).'inhouse_d8'— siim’s numbaD8FlowRouter— is the only accepted value since the 0.9.1 standalone flip (the fortranSingleFlowRouterwiring was retired); the parameter is the router-contract plug point for future backends. Only affectsrouting='single'— the D-inf directions + mask + basin ride the same in-house contract.flexure (bool, optional) – Opt in to glacial-isostatic flexure / sediment tracking.
sediment (bool, optional) – Opt in to glacial-isostatic flexure / sediment tracking.
numerics_backend ({'inhouse'}, optional) – Flexure + hillslope-diffusion backend (
constants.NUMERICS_BACKEND).'inhouse'—HillslopeDiffusion(siim’s numba ADI) in the stockLinearDiffusionslot andGlacialFlexureon siim’s scipy.fft plate solve — is the only accepted value since the 0.9.1 standalone flip. Does not affect routing.
- Returns:
Maps xsimlab slot name → process class, ready for
update_processes.- Return type:
- siim.fastscape.glacial_model(*, mode='B', carve=None, routing='single', router_backend=None, flexure=False, sediment=False, trunk_surface=None, numerics_backend='inhouse')[source]¶
Return a ready-to-run xsimlab
Model: fastscape’sbasic_modelwith its'spl'/'drainage'slots replaced by siim’s glacial processes.Convenience wrapper over
glacial_processes()(same parameters). For the full coupled model with forcing, I/O and plotting, usesiim.siim2d.siiminstead.
Glacial processes — siim.fastscape.processes¶
xsimlab process classes for siim2d.
Three processes plug into fastscape’s basic_model:
InitialTopographyreplacesFlatSurfacefor the initial elevation field (precomputed elevation + zeroed-on-fixed-edges noise).
GlacialFlowAccumulatorextendsFlowAccumulatorto track water flux, ice flux, drainage area, basin IDs, and the per-step routing topology (receivers_2d, stack_2d) needed by downstream extract_channel / extract_basin / strahler_order code in siim2d.py.
GlacialSPLModeA/GlacialSPLModeB/GlacialSPLModeC(allGlacialSPLBasesubclasses) plug into the model’s erosion slot, one per surface-evolution mode (build-time selection): A tracks the ice surface, B the bed + ice thickness (citizen), C = B plus the sub-grid width carve. They dispatch on (sliding_law, routing) and call the correspondinglaw_codestep skeleton insiim._core.skeleton.
DinfFlowRouteroverrides fastscape’sFlowRouterwith Tarboton (1997) D-infinity flow directions (defined at the bottom of this module).
The numba flow-accumulation + D-infinity routing primitives live in
siim._core.routing; the ice-thickness + erosion solvers in
siim._core (solvers / eroders / skeleton).
- class siim.fastscape.processes.InitialTopography¶
Bases:
objectInitialize surface topography from a pre-computed elevation array plus uniform tie-breaking noise. Noise is zeroed on ‘fixed_value’ edges so the boundary z stays exactly at the elevation_init value across the run (BlockUplift and the glacial erosion process both skip fixed cells; without this any IC noise there gets locked in and drifts the base level).
- class siim.fastscape.processes.GlacialBlockUplift(*args: Any, **kwargs: Any)¶
Bases:
BlockUpliftBlockUplift that tolerates a time-varying
(nt, ny, nx)uplift rate.xsimlab slices the clock axis of a
(('tstep', 'y', 'x'), array)input per step, but xarray no longer squeezes groupby, so the per-stepratekeeps a leading size-1 dim and stockBlockUplift’snp.broadcast_to((1, ny, nx), (ny, nx))raises. Drop that leading dim before broadcasting; scalar /(y, x)rates are unaffected.
- class siim.fastscape.processes.GlacialLaw¶
Bases:
objectRaw sliding-law inputs + the derived-constant algebra, hoisted out of the glacial erosion process (
GlacialSPLBase).Owns the per-run physical/law scalars (sliding law, erosion coefficients, ice-rheology parameters, the channel-floor ratio
hc_over_H, the channel aspect ratioalpha_g) and turns them into the frozen(law_code, GlacialParams)record thelaw_codestep skeletons consume (siim._core.skeleton). Exposed asparamsso the erosion process (and any sibling) reads one record by foreign instead of re-deriving the constants.hc_over_His the centerline/mean channel-depth ratio (defaultconstants.HC_OVER_H); the surfaces built from(zb, H)state arezs = zb + hc_over_H * H(the tracked bed is the sub-grid channel floor,Hthe width-mean depth).
- class siim.fastscape.processes.GlacialFlowAccumulator(*args: Any, **kwargs: Any)¶
Bases:
FlowAccumulatorAccumulates water flux, ice flux, and drainage area through the flow graph.
Sub-grid mass balance: ablation below the ELA scales with the glacier’s plan-view area (width × flow length) wherever that exceeds cell_area. Width is set by Hack-style drainage-area scaling (Hack [Hac57]):
glacier_width = width_hack_k * upstream_area ** width_hack_p
This decouples width from instantaneous ice thickness, so the kinematic- wave mode at the toe doesn’t feed back through ablation. Matches the strictly-elevation-driven mass balance of the 1D analytical SS.
- class siim.fastscape.processes.GlacialSPLBase¶
Bases:
objectGlacial + fluvial erosion — shared base for the per-mode erosion processes (
GlacialSPLModeA,GlacialSPLModeB,GlacialSPLModeC). Abstract: it owns the shared declarations + setup but defines NOrun_step(the concrete subclass that fills theglacial_splslot supplies it; siim2d picks the subclass at build time by mode). It is@xs.process-decorated only so its variables are discoverable as foreign targets (siblings + subclasses); it is never assigned to a model slot itself.The tracked bed is the sub-grid channel floor zb = z - hc_over_H*H (H the width-mean depth). The raw law params + constant algebra live in
GlacialLaw; this base reads the derived(law_code, GlacialParams)record via thelawforeign. Subclasses dispatch on the record’slaw_codeand on routing dimensionality (SFR vs D-inf), calling thelaw_codestep skeletons insiim._core.skeleton.Sibling processes foreign onto THIS base (xsimlab resolves
foreign(Base)via the MRO to whichever subclass fills the slot):SedimentTrackerandGlacialFlexurereaddenudation/ice_thicknesshere.
- class siim.fastscape.processes.GlacialSPLModeA(*args: Any, **kwargs: Any)¶
Bases:
processMode A erosion (ice-surface state). Fills the
glacial_splslot whenmode == 'A'. The ice surface is the single tracked state (pinned at base level by the BCs); H is solved from the local pre-erosion surface slope each step, the bed is the derivedz - hc_over_H*H. Historical siim2d behaviour; never carves (the bed is reconstructed, not stored).
- class siim.fastscape.processes.GlacialSPLModeB(*args: Any, **kwargs: Any)¶
Bases:
processMode B erosion as a fastscape citizen (Fork B), no carve.
The tracked state
topography__elevationIS the bedzb(the sub-grid channel floor);ice_thicknesscarries H. Each step hands the kernel the POST-uplift bed (persisted state + this step’sTectonicForcingbed motion; fastscape composes the same uplift at finalize) and H fromice_thickness, runs the UNCHANGED mode-B kernel (which reconstructszs = zb + hc_over_H*Hinternally), and reports a genuine erosion height:denudation = zb_in - zb_out(the bed lowering this step)erosion = denudation(the erosion-group height)fastscape then finalizes
zb_new = zb + uplift - erosion = zb + uplift - delta-zb= the kernel’s eroded post-uplift bed, so the bed evolves correctly. Nobedrock_surfaceoutput —topographyIS the bed (the derived display surfacezb + hc_over_H*His reconstructed by the consumer / plotter). The sub-grid width carve rides on theGlacialSPLModeCsubclass; routing/erosion slopes come from the reconstructed ice surface (GlacialSurfaceToErode).
- class siim.fastscape.processes.GlacialSPLModeC(*args: Any, **kwargs: Any)¶
Bases:
processMode C erosion: the citizen mode-B bed-and-thickness class (
GlacialSPLModeB) plus the sub-grid glacier-width carve. Fills theglacial_splslot formode == 'B'withcarve_widthon (the default siim2d path).Everything the citizen does is inherited:
topographyIS the tracked bedzb(bed memory; no flicker, nobedrock_surfaceoutput), routing + erosion slopes come off the reconstructed ice surface (GlacialSurfaceToErode), anderosion == denudation == delta-zbso fastscape finalizeszb_new = zb + uplift - delta-zb. This subclass runs the UNCHANGED mode-B kernel and then carves the sub-grid footprint into the bed (the topography state) — so the carve deepening flows straight intodenudation(the sediment / flexural-unloading source), the same rock the retired surface-replace carve class removed but now on citizen semantics. The carve edits the BED after the kernel;surface_out(the kernel’s reconstructed ice surface) is handed to the carve for its per-cell updates but discarded by the citizen.With
carve_width=Falsethis isGlacialSPLModeBbit-for-bit (the carve is gated onself._carve); the class exists so the carve rides the citizen path rather than the surface-replace one.
- class siim.fastscape.processes.GlacialSurfaceToErode(*args: Any, **kwargs: Any)¶
Bases:
SurfaceAfterTectonicsSurface used for routing + erosion slopes in citizen Mode B (
GlacialSPLModeB).Subclasses fastscape’s
SurfaceAfterTectonics, whoseelevation = topo_elevation + forced_motionis the post-uplift surface (heretopoIS the bedzb, so this is the post-uplift bed). Adds the reconstructed ice column on top:elevation = (post-uplift bed zb) + hc_over_H * H_lag=zsi.e. the ice surface the flow router stacks on and the erosion kernel takes its slopes from. The uplift add is inherited (no explicit
+uplift); H is the step-start (lagged)ice_thicknessglobal, consistent with the ordersurf2erode -> glacial_flow -> glacial_spl.hc_over_His read from the law record viaGlacialLaw. (A pre-uplift climate varianttopo + hc_over_H*His deferred to Phase 3.)Anti-flicker relaxation (
routing_relax= r): the once-per-step H -> zs -> D8-receivers -> flux -> closure-H loop lags by one step and D8 receiver choice is discrete, so near-tied surfaces flip whole subtrees of flux each step and H ~ Q^(1/4.. 1/5) turns those O(1) swaps into a period-2 ice-thickness slosh (a cosmetic planview flicker; integrated area/volume + attractor stats are unaffected). With r > 0 the routing thickness is the EMAH_eff(t) = r*H_eff(t-1) + (1-r)*H_lag(t)(seededH_eff(0) = H_lag) instead of the raw lagged H. r = 0 (default) uses the raw H, bit-for-bit.State-separation firewall: the relaxed H reaches ONLY this provider’s elevation, which feeds ONLY the flow router graph and
GlacialFlowAccumulator’s mass-balance surface. The mode-B kernel reconstructs its ownzs = zb + hc*Hfrom the tracked bed + raw H for every closure and erosion slope (it never reads this elevation), so no relaxed/geometric value ever enters a physics closure, the carve, the flexure load or the outputs.
- class siim.fastscape.processes.TrunkSurfaceToErode(*args: Any, **kwargs: Any)¶
Bases:
processFabricated trunk-surface routing (mode B/C,
trunk_surface; on by default for mode C, as part of the mode-C standard).routing_relaxdefaults toconstants.MODE_C_ROUTING_RELAXhere (the parent’s isconstants.ROUTING_RELAX), so this class in thesurf2erodeslot is the whole mode-C routing standard with no extra inputs.A trunk glacier of mean thickness H occupies width
W = alpha_g*H— routinely several cells — but the flow graph carries its ice down 2-3 parallel chains, each with its own dilutedice_fluxand its own closure H, understating the trunk H. This provider replaces the plain reconstructed ice surface (zb + hc*H,GlacialSurfaceToErode) with a fabricated surface that has a LINEAR cross-valley dip toward the trunk centerline. The flow router stacks on it and the accumulator reads it, so (a) flow converges onto the centerline chain — its raw accumulated flux then IS the full cross-section discharge (no accounting correction needed), and (b) mass balance is evaluated at the trunk-surface elevation.Fabrication (per-step, from LAGGED closure-H — this provider runs before the router). The power transform the carve uses gives footprint membership
D < 0and per-cell sourceSRC(the thickest disc wins — the medial-axis-transform inversion). For a footprint celliwith sources(whose disc radiusR_s = alpha_g*H_s/2 > cell):zs_geo(i) = zs_dyn(s) + S_c(s) * (d_i - R_s)
with
d_ithe distance to the source (d_i^2 = D_i + R_s^2, from the transform),zs_dyn(s) = zb_s + hc*H_sthe source’s own dynamic surface, andS_c(s) = TRUNK_DIP_K * max(|grad zs_dyn|(s), TRUNK_DIP_FLOOR)the cross-slope (0 at the rimd = R_s,-S_c*R_sat the axis). The routing value inside a footprint ismax(zs_geo, zb)— bare cells / nunataks present their rock (routing goes around them); a footprint cell’s own dynamic ice column is deliberately IGNORED (its ice is part of the trunk; taking a max with it would re-erect a spine). Outside footprints the surface is the dynamiczb + hc*Hunchanged.State-separation rule: the fabricated surface reaches ONLY the flow router (graph) and the accumulator’s mass-balance surface. The mode-B kernel is UNTOUCHED — it uses the router’s receiver graph but reconstructs its own
zs = zb + hc*Hfor every closure and erosion slope, so no fabricated (geometric) elevation ever enters a closure. A flank cell routed to a taller centerline hits the well-posed negative-a(from-a) branch, bounded by flux; the carve reads post-kernel closure state. The dip is a routing numerics device (like the priority-flood eps), not physics.
- class siim.fastscape.processes.SedimentTracker¶
Bases:
objectOptional sediment-throughput tracker (added only when siim’s
track_sedimentis on — zero cost otherwise).Routes each step’s denuded rock volume —
max(denudation, 0) * cell_area— down the flow graph in one accumulation pass, giving per node the total upstream-eroded volume passing through it that step (flux, m^3) and its running time-integral (cumulative, m^3). Detachment-limited bookkeeping only: no deposition, every node passes all upstream sediment through. Differencecumulativealong the time axis to recover per-interval volumes; the outlet node’scumulativeis the whole-basin yield.edge_flux/edge_cumulativereport the same routed flux summed over each domain-edge outlet ring instead (dims('side',), theborder_statusorder left/right/bottom/top, NaN off'fixed_value'sides) — one shared sum (siim._core.step.edge_sediment()) with the in-house driver. Both reports ride the one accumulation pass; siim’strack_sedimentvalue picks which are stored, and clearsbasinwhen only the edge totals are wanted so the per-node running integral is skipped.Reads the erosion process’s
denudation(viaGlacialSPLBase: the true rock removed — delta-zb incl. sub-grid carve in mode B, delta-zs in mode A; per-step, already includes dt) so it runs after the erosion step and reuses the same receiver/stack flow graph.
- class siim.fastscape.processes.GlacialFlexure(*args: Any, **kwargs: Any)¶
Bases:
FlexureFlexural isostasy with true glacial isostatic adjustment: ice loading + erosional/tectonic unloading + elastic rebound.
fastscape’s
Flexureloads the elastic plate fromTotalErosion.height(the ice-SURFACE change in siim2d mode B — ice-thickness change leaks in). This override does two things:Re-sources the rock load from the erosion process’s per-step
denudation(viaGlacialSPLBase; mode B: delta-zb incl. sub-grid carve; mode A: delta-zs), so the plate flexes in response to rock erosional + tectonic unloading, correct under transient ice.Adds the per-step ICE load. The glacial ice load is the channel cross-section carried over the cell,
col = alpha_g * H**2 / L(L = sqrt(cell_area)) — the mass-conserving, hc-FREE ice volume per cell. alpha_g*H**2 = Qg/V (the ice flux over the channel velocity), and the centerline/mean hc factor cancels the parabola’s 2/3, so it never appears. Its per-step change folds in as a rock-equivalent column, (rho_ice/lithos_density)*d(col), into thediffthat drives fastscape’s incremental plate solve (incremental: the full column would re-apply the whole load every step). Width-aware (∝ H**2): a glacier wider than the cell (alpha_g*H > L) piles proportionally more ice than the mean depth H. Gated byice_load(default True; False = erosion-only, the pre-GIA behaviour). The mass-conserving Qg/V load is used in preference to a per-cell thickness because siim does not resolve 3D ice flow: sheet accumulation Qg = area*balance is trustworthy, the unresolved per-cell H is not (see DECISIONS 2026-06-16).
The plate solve, densities, boundary handling and rebound feedback are inherited unchanged. The ice LOAD is applied everywhere ice is present, including afloat cells (zs = zb + hc*H < bl): the waterline-flotation gate gates glacial EROSION, not the flexural load. This is an accepted secondary seam — the ice mass is physically present regardless of grounding, and a true floating-shelf load would be reduced by the water it displaces; siim’s overdeepenings sit right at the flotation draft, so the residual is small. Hillslope diffusion of the ice surface is deliberately not in the load (an accepted small approximation: it is a near- conservative redistribution of the ice surface, not rock unloading).
- class siim.fastscape.processes.HillslopeDiffusion¶
Bases:
objectIn-house linear hillslope diffusion (ADI), the standalone replacement for fastscape’s stock
LinearDiffusion(fortranfs.diffusion).Same input/output contract as
LinearDiffusion— readsSurfaceToErode.elevation+ thediffusivityinput, writes the per-steperosioninto the"erosion"group — so it drops into the'diffusion'model slot whennumerics_backend='inhouse'. The unconditionally-stable ADI solve (siim._core.hillslope.diffuse()) is bit-for-bit with the fortran for the uniformkdsiim uses. Boundary handling keys off the sameibccode (BorderBoundary), read directly rather than via the fortran context.
- class siim.fastscape.processes.D8FlowRouter(*args: Any, **kwargs: Any)¶
Bases:
processIn-house D8 single-flow router (S4) — the framework-free replacement for fastscape’s fortran
SingleFlowRouter(fs.flowroutingsingleflowdirection). Fills theflowslot whenrouting='single'androuter_backend='inhouse_d8'. Produces the SFR bundle (1D receivers / lengths, all-ones weights / nb_receivers, outlet-first stack) viasiim._core.step.route_d8()on the eps-filled surface; basin labeled by outlet index. The routing delta vs the fortran SFR is confined to depression/tie cells (behavioral/attractor gate).
- class siim.fastscape.processes.DinfFlowRouter(*args: Any, **kwargs: Any)¶
Bases:
processD-infinity flow router [Tar97].
Subclasses the in-house router shell — fully fortran-free (S4): the interior/boundary mask comes from
border_status(Map 3 §4, provably identical to the old fortransfr_rec != imask) andbasinfrom the in-house outlet labeling, so the lastfs.flowrouting()call is gone. Outputs use the (n_nodes, 2) receiver/weight shape consumed by GlacialFlowAccumulator and the mode-B / mode-A D-inf kernels.Continuous facet-direction weights replace the discrete neighbor weighting jumps of Quinn-style multi-flow routing, removing per-step receiver-flip kicks (~10x reduction at 100x100 vs Quinn MFR).
Depressions: directions are computed on the eps-filled surface (priority-flood from the outlet cells, _priority_flood_eps), so flux crosses closed basins toward their spills — the same depression semantics as the fortran SFR. The PHYSICS stays on the true surface: the mass balance samples real z (flux crossing a deep trough melts at the drowned elevation and dies) and the erosion/H kernels clip negative true-surface slopes to zero (lake interiors do not erode). Looped boundaries wrap (fill and facet scan both). See
docs/guides/concepts.mdfor the public routing overview.
Forcing processes — siim.fastscape.forcing¶
Generic fastscape forcing processes.
Two reusable @xs.process forcing classes — not glacial, not siim-specific —
that drop into any fastscape model. They came out of the escarpment work
(siim.escarpment) but depend only on fastscape’s grid/boundary/surface
processes:
WaveUplift— a moving Gaussian uplift wave (a drop-in replacement for fastscape’sBlockUplift).
PlateauSurface— an arctan-smoothed plateau initial topography (a drop-in replacement for an initial-elevation process).
- class siim.fastscape.forcing.WaveUplift¶
Bases:
objectMoving Gaussian uplift wave (replaces fastscape’s BlockUplift).
rate(x, t) = U_inf + U0 * exp(-(x - wave_center(t))^2 / wave_width^2), wave_center(t) = x[0] + x_escarpment + t * wave_velocity, U0 = wave_calibration * delta_h * wave_velocity / (wave_width * sqrt(pi)).
- class siim.fastscape.forcing.PlateauSurface¶
Bases:
objectArctan-smoothed plateau initial topography (replaces InitialTopography).
Arctan ramp from exactly 0 on the low-x side to plateau_zo on the high-x side, centered at x_escarpment = (1 - plateau_frac) * Lx with transition width plateau_w (rescaled so the domain ends land on 0 / plateau_zo - plateau_dz). A small slope plateau_dz across the plateau seeds the divide. Uniform noise is added for D8 tie-breaking and zeroed on ‘fixed_value’ edges (matching InitialTopography, so the boundary z stays put across the run).