Skip to content

Simulation checkpoints and forks

Simulation Checkpoints preserve a fuller swmmrs continuation boundary than an EPA hotstart. Use them to resume a running owner in another process, create an independent in-process branch, or import physical state into a compatible open receiver.

These operations are distinct from save_hotstart() and use_hotstart(), which read and write EPA's .hsf format.

Choose an operation

Need Operation Result
Publish a durable continuation boundary simulation.save_checkpoint(path) JSON manifest and immutable sidecars beside it
Continue that artifact as an independent owner Simulation.resume(...) Restored RUNNING or COMPLETE owner with fresh report/output paths
Branch immediately in the same process simulation.fork(...) Independent child with cloned editable declarations
Enhanced Hotstart into a parsed compatible model receiver.load_checkpoint_state(path) Receiver stays OPEN, imports classified warm state, and retains its dates, forcing, inputs, accounting, and outputs

Save, fork, and resume operate at quiescent RUNNING or COMPLETE boundaries. They may be called between iterator advances after next() returns.

What each mechanism restores

The three durable mechanisms answer different modeling questions. In the table, source means that the value recorded at the saved boundary is restored; receiver means that the newly opened model keeps or initializes its own value. Selected means that the artifact carries only the listed state, not the complete state of that subsystem.

Model state or run property EPA Hotstart (.hsf) Enhanced Hotstart (load_checkpoint_state) Checkpoint Resume (resume)
Model declarations, object identities, and topology Receiver. The caller supplies a compatible input model; identity and complete topology are not verified. Receiver. Ordered structure, units, routing family, pollutants, subsystem dimensions, and imported-value domains must be compatible. Source. Authenticated declarations, prepared configuration, and native relationships are restored.
Simulation dates, elapsed clocks, and lifecycle boundary Receiver. A new run starts on the receiver schedule. Receiver. A new run starts on the receiver schedule; imported ages and deadlines are translated to that schedule. Source. The saved RUNNING or COMPLETE boundary and its clocks are restored.
Node and link hydraulic condition Selected. Current node depth and lateral flow, storage hydraulic residence time, and current link flow, depth, and setting are restored. Source warm state. Classified old/new node, link, conduit, storage, and inlet condition is imported. Source continuation. The complete checkpoint hydraulic projection is restored.
Subcatchment surface and runoff state Selected. Subarea ponded depth and current runoff are restored. Source warm state. Surface storage and runoff lag state are imported; source rainfall and shared runoff-step totals are excluded. Source continuation. Runoff state, flags, totals, and progress are restored.
Infiltration memory Source warm state. The active Horton, Modified Horton, Green-Ampt, Modified Green-Ampt, or Curve Number state is restored. Source warm state. Active infiltration memory is imported and checked against receiver parameters; shared runoff-step accumulators are excluded. Source continuation. Method configuration, state, and balances are restored.
Groundwater and snowpack condition Selected source state. Groundwater-zone and snow-surface state are restored where configured. Source warm state. Compatible groundwater and snow physical condition is imported; receiver-derived limits and calendar selectors remain authoritative. Source continuation. Configuration, physical state, coupling state, and accounting are restored.
LID condition Receiver. EPA hotstart files do not carry LID layer or clogging state. Source warm state. Layer state, clogging, regeneration timing, and embedded Green-Ampt memory are imported; LID rates, balances, and report history start fresh. Source continuation. LID physical state, balances, and detailed-report continuation are restored.
Water-quality condition Selected. Current node, link, runoff, and ponded quality plus surface buildup and street-sweeping date are restored. Source warm state. Compatible concentrations, reactor state, and physical buildup stocks are imported; API forcing, cumulative loads, and quality accounting are excluded. Source continuation. Quality configuration, treatment state, concentrations, loads, balances, and statistics are restored.
Control-rule and PID numerical memory Receiver. A saved link setting is applied, but rule evaluation history, PID memory, and routing-event progress are not carried. Source warm state. Compatible PID and control memory is imported; rule-clock phase is translated and receiver event position is recomputed. Source continuation. Control structure, PID memory, action history, and routing-event position are restored.
Dynamic Wave numerical memory Receiver initialization. Saved current hydraulic values seed an ordinarily initialized solver. Selected source memory. Indispensable variable-step and node/link continuation is imported; process-local workers and scratch state are rebuilt. Source continuation. Durable Dynamic Wave continuation is restored and process-local workspace is rebuilt.
Persistent API forcing Receiver. The new owner supplies its own forcing. Receiver. All nine forcing families remain receiver-owned and can be edited after State Load. Source. All nine forcing families are restored from the checkpoint.
Rain, climate, time-series, RDII, and runoff/routing interface progress Receiver initialization. Input processors start according to the receiving model. Receiver initialization. Checkpoint cursors, lookahead, residual source-input response, and sidecars are not imported. Source continuation. Resource modes, validated dependencies or sidecars, cursors, and parser lookahead recorded by the checkpoint are restored.
Continuity accounting, cumulative loads, and statistics Fresh receiver history. Fresh receiver history. Source history.
Report, binary output, interface output, and detailed-LID report history Fresh receiver files. Fresh receiver files. Checkpoint sidecars are not read. Source prefixes and cursors are copied into caller-supplied fresh destinations, then continued.
Scheduled hotstart progress and output policy Receiver. Receiver. Source. Pending/emitted schedule state is restored at fresh derived destinations.
Next-step and whole-run relationship to the saved source A warm initial condition; uninterrupted equality is not promised. A richer warm initial condition; first-step and whole-run equality with Resume are not promised. An uninterrupted-equivalent continuation at the saved quiescent boundary.

The matrix describes the current swmmrs contracts rather than every byte in a live process. Raw file handles, buffers, worker threads, and scratch workspaces are never serialized; Resume rebuilds them from durable state. EPA Hotstart has limited count and flow-unit checks, Enhanced Hotstart performs strict receiver compatibility and scalar-domain checks, and Resume validates the complete checkpoint artifact set.

Save and resume

from pathlib import Path

from swmmrs import Simulation

checkpoint = Path("continuation/state.json")

source = Simulation("model.inp", "source.rpt", "source.out")
source.start()
source.step()
source.save_checkpoint(checkpoint)

resumed = Simulation.resume(
    checkpoint,
    "resumed.rpt",
    "resumed.out",
)

# Both owners are independent and continue from the saved boundary.
source_time = source.step()
resumed_time = resumed.step()

for simulation in (source, resumed):
    simulation.end()
    simulation.close()

Published generation-addressed sidecars are immutable. Saving again to the same manifest destination atomically replaces that manifest with a new generation; use a unique manifest path for every boundary that must remain retained. resume() requires fresh report and output destinations and rejects collisions before publishing an owner.

Fork an in-process scenario

fork() avoids a durable round trip and preserves the source owner's prepared configuration coordinator and editable declarations:

from swmmrs import Simulation

source = Simulation("model.inp", "baseline.rpt", "baseline.out")
source.start()
source.step()

child = source.fork("scenario.rpt", "scenario.out")
child.links["GATE"].target_setting = 0.5

while source.step() is not None:
    pass
while child.step() is not None:
    pass

for simulation in (source, child):
    simulation.end()
    simulation.report()
    simulation.close()

Source and child do not share hydraulic state. Give every owner unique mutable artifact paths.

Load physical state into an open receiver

Use State Load when the receiver must keep its parsed configuration and later support declaration editing:

from swmmrs import Simulation

receiver = Simulation("compatible.inp", "receiver.rpt", "receiver.out")
receiver.load_checkpoint_state("continuation/state.json")

# Classified warm state is staged now. Receiver forcing and input processors
# remain authoritative when normal startup initializes the new run.
receiver.start()
while receiver.step() is not None:
    pass
receiver.end()
receiver.report()
receiver.close()

The receiver must be OPEN and structurally compatible. State Load stages dirty receiver declarations without mutation and accepts them only when they leave State Load structure and scalar interpretation unchanged. It validates the imported physical values, commits preparation and the classified physical/numerical subset together, and retains a pending copy so start() can reapply it after processor initialization.

The receiver keeps its dates, all persistent-forcing families, configured inputs and their progress, iterator cadence, fresh statistics/accounting, output history, and parsed declarations. Checkpoint sidecars are not read. Source rain/RDII progress and residual source-input response are not imported. Enhanced Hotstart makes no first-step or whole-run equality promise with Checkpoint Resume, even when receiver forcing, dates, and inputs match.

Prefer to complete preparation-dependent edits before loading. If you make one while warm state is pending, the edit is accepted but start() reports that the pending State Load is stale. Call load_checkpoint_state() again to prepare the receiver and revalidate the import. Persistent-forcing and metadata-only updates do not stale the pending warm state.

Editable declarations after durable resume

Checkpoint format 1.2 preserves the source owner's editable declarations and warning baseline as an authenticated payload. The full-resume fingerprint binds that payload to the checkpoint's canonical state; do not copy configuration between manifests, even if the models appear structurally compatible. After resume(), you can finish and end the restored run, make stable edits, and start a fully prepared rerun. State Load remains different: it keeps the open receiver's parsed declarations.

Artifacts and failures

A Simulation Checkpoint is not one opaque file. The JSON manifest identifies immutable sidecars for active report/output prefixes and generated resources, and validated external dependencies for immutable inputs. Do not move, replace, or edit members independently.

Publication is atomic from the caller's perspective: the manifest appears only after its complete generation is usable. Resume and fork preflight all requested destinations. Destination and preflight failures publish no partial owner and leave the source unchanged. An operational failure while flushing source-owned resources can establish FAILED, even though no child or partial destination is published.

ValidationError reports Python/path alias mistakes known before native resource planning; LifecycleError reports an invalid owner state; SolverError reports non-fresh destinations, integrity or compatibility failures, capture, resource validation, and publication failures. See Errors and recovery.