Source code for openswmm_mcp.backends.base
"""Backend protocol definition.
Defines the interface every engine backend exposes to tools. Both
backends present the v1 Pythonic surface of :mod:`openswmm.engine`:
- Container protocol on every collection: ``len(backend.nodes)``,
``backend.nodes[id_or_idx]``, ``for n in backend.nodes``,
``id_or_idx in backend.nodes``.
- Item access returns wrapper objects exposing property-style attribute
reads/writes: ``backend.nodes["J1"].depth``,
``backend.nodes["J1"].lateral_inflow = 0.5``.
- Sub-views per type: ``node.stats``, ``node.storage``, ``node.outfall``,
``link.pump``, ``link.weir``, ``subcatchment.infiltration``, etc.
Available only on the openswmm backend; the legacy backend raises
:class:`AttributeError` on these.
The legacy adapter has scalar accessor methods (``get_depth(idx)``,
``get_type(idx)``, …) on each collection — these are **not** a public
transition shim, they are the SWMM-5-toolkit dispatch path that the
``_LegacyNode`` / ``_LegacyLink`` / etc. wrappers call into. Treat them
as private implementation; the public surface is the v1 shape described
above.
"""
from __future__ import annotations
from typing import Any, Protocol, runtime_checkable
[docs]
@runtime_checkable
class Backend(Protocol):
"""Engine-agnostic surface that tools call against.
New-engine-only attributes (``editor``, ``statistics``, ``spatial``,
``tables``, ``patterns``, ``controls``, ``inflows``,
``infrastructure``, ``quality``, ``save_schedule``) raise
:class:`AttributeError` on the legacy backend; tools that need them
must guard with :func:`openswmm_mcp.dependencies.require_new_engine`.
Attributes
----------
engine_kind:
``"openswmm"`` or ``"legacy"``. Tools that require the new engine
check this via :func:`openswmm_mcp.dependencies.require_new_engine`.
solver:
Lifecycle-managing solver handle. Has ``open / initialize / start
/ step / end / report / close / destroy``, plus ``elapsed``,
``state``, ``start_datetime``, ``end_datetime``,
``current_datetime``, and (openswmm only) ``steps()`` /
``stride(n)`` / ``until(target)``.
nodes / links / subcatchments / gages / pollutants:
v1-shape collections: ``len(...)``, ``[key]`` returning wrappers
with property-style attribute access, iteration, and ``in``.
mass_balance:
Continuity-error queries returning fractions (0.001 = 0.1 %),
accessible via property-style ``mass_balance.runoff_continuity_error``.
forcing:
Runtime forcing dispatcher. On legacy a subset of methods is
available; unsupported methods raise :class:`NotImplementedError`
which the forcing tool translates to a clear ``NOT_SUPPORTED``
``ToolError``.
hotstart:
``save(solver, path)`` and ``open(path)`` returning an object with
``apply(solver)``. Legacy backend wraps ``solver.save_hotstart`` /
``solver.use_hotstart`` to match this shape.
"""
engine_kind: str
@property
def solver(self) -> Any: ...
@property
def nodes(self) -> Any: ...
@property
def links(self) -> Any: ...
@property
def subcatchments(self) -> Any: ...
@property
def gages(self) -> Any: ...
@property
def pollutants(self) -> Any: ...
@property
def mass_balance(self) -> Any: ...
@property
def forcing(self) -> Any: ...
@property
def hotstart(self) -> Any: ...