Skip to content

Lifecycle and ownership

One Simulation owns one project, worker, solver state, and in-memory file system. Open a separate owner for another model or scenario. Handles and collections belong to the owner that created them.

States and operations

State Meaning Normal operations
open Input parsed and no run is active Read or edit configuration, set persistent forcings, load checkpoint state, start(), iterate, or run()
running A run is active Read results and quality, read statistics, advance, apply controls, save hotstart/checkpoint, sleep workers, end(), or finish()
complete Routing reached the configured end time Read results, quality, and statistics, save hotstart/checkpoint, then end() or finish()
ended Native processors ended and the owner remains open Read final results and files, finalizeReport(), report(), edit configuration, set persistent forcings, start a fresh run, or resetSolver()
failed Native execution entered an error state Preserve the error and close the owner
closed Worker, solver, and project files were released Repeat close() or call getState()

close() is idempotent and can be called from every closeable state. After closing, collection access and solver operations reject. getState() still returns "closed".

Advancement ownership

steps() creates an async generator but starts lazily on its first next(). It starts an open or ended owner, continues a running owner, and returns immediately for a complete owner. While an iterator is active, it owns advancement. A second iterator, start(), step(), stride(), run(), end(), finalizeReport(), report(), or finish() rejects with LifecycleError.

Result reads, status reads, and allowed control writes can run inside the loop. Await each operation before requesting another advance. Natural exhaustion leaves the owner complete; break releases the iterator without finalizing. If you consume a generator manually and abandon it, call its return() method.

terminate() is valid only while an iterator owns advancement. It asks the iterator to end at its next observation boundary. The request does not interrupt a native call already in progress. The iterator then releases ownership and the owner remains available for finish() (and report() / finalizeReport()), another run, or close().

Finalization and native stages

finish() performs the native end, flushes binary output, writes the requested detailed report tables when the run started with saveResults: true, finalizes the report, and returns { report, output }. It can finalize a partial run and is safe to repeat in ended.

end() performs only the native end and leaves the owner ended. Use report() afterward when you need to invoke detailed report generation as a separate stage. report() is valid in ended only and requires saved results. finalizeReport() is a lighter separate stage that appends only the runtime footer and is valid after end() regardless of whether detailed results were requested. The report-selection flags on objects and the model's detailed-report option control which tables are requested.

System statistics() is available in running and complete, not ended, so capture it before finalization. Per-object statistics methods follow the same active-run window. Result and quality records remain readable in ended.

resetSolver() is valid in open or ended. It clears stale run, result, and output state without reparsing the retained declarations, then returns the owner to open. An accepted configuration edit in ended also returns the owner to open and invalidates the previous live run state. Detached records already returned to the application remain usable.

sleepWorkers() is valid only in running and asks Dynamic Wave workers to enter their sleep state. It does not end the run. close() releases resources but does not provide hard cancellation for an in-flight native operation. It also removes the worker's in-memory project files, so report text or output bytes must be copied before cleanup. finish() does that automatically; finalizeReport() exists for callers that need to flush the native close-time footer without yet releasing those files.

Reruns

After finish(), start() or iteration begins a fresh run at the configured model start time. It does not continue from the previous endpoint. Configuration and persistent forcings remain on the owner until changed. A new run replaces binary output; report text can retain prior run content. Use a new owner for independently named scenario artifacts.

Hotstarts, checkpoints, and forks

The continuation mechanisms answer different questions:

Need Method What it preserves
Exchange selected physical state with an EPA-compatible run saveHotstart() then useHotstart(bytes) EPA .hsf warm state; the receiving run owns its dates, forcing, accounting, and files
Continue the same swmmrs run in another owner saveCheckpoint() then Simulation.resume(bundle) The saved run boundary, clocks, managed resource progress, configuration, forcing, accounting, and continuation state
Branch inside one process fork() An independent child at the source's quiescent boundary
Seed an already opened compatible model loadCheckpointState(bundle) Classified warm physical state; the receiver keeps its declarations, dates, forcing, accounting, and output history

saveHotstart() is valid in running or complete; useHotstart() accepts bytes or null in open or ended and stays selected for later starts until cleared. It is a warm start, not a complete owner continuation.

A CheckpointBundle is one in-memory artifact. It contains a format/version, worker-relative manifest path, canonical manifest bytes, and a map of sidecar and validated dependency bytes. Keep all fields together. The native checkpoint JSON alone is not a complete JavaScript checkpoint. Simulation.resume(bundle) opens an independent owner from a saved running or complete boundary. fork() provides the same independence without asking the application to publish a bundle, but it is not a durable artifact.

loadCheckpointState(bundle) requires an already opened compatible receiver and leaves it open. It imports classified warm state, then normal start() applies that state after receiver initialization. The receiver keeps its own calendar, declarations, persistent forcings, input progress, accounting, and output files. State Load therefore does not promise the same next step or report history as Checkpoint Resume.

Checkpoint save, resume, and fork operate between advances at a quiescent running or complete boundary. See hotstarts, checkpoints, and forks for method signatures and compatibility details.