aquakin.Unit#

class aquakin.Unit(*args, **kwargs)[source]#

Bases: Protocol

A plant unit operation.

Units expose:

  • name: a string identifier used for connections.

  • state_size: the number of ODE state variables this unit owns (zero for stateless units like mixers / splitters). Exposed as a read-only @property on every shipped unit – a constant 0 on stateless ones, derived from the unit’s config on stateful ones.

  • input_ports / output_ports: named stream ports.

And implement:

  • initial_state() — the (state_size,) initial state vector.

  • compute_outputs() — given the current t, internal state, input streams, and the control-signal bus, return the output streams. Called by the plant in topological order on every RHS evaluation. It receives the same signals bus threaded into rhs(), so a unit whose output stream depends on a control signal (e.g. a feedback-dosing unit) can read it; an uncontrolled unit ignores it.

  • rhs() — given the current t, internal state, input streams, and the control-signal bus, return dstate/dt of shape (state_size,). Called by the plant on every RHS evaluation after all output streams are known.

  • flow_outputs() — the unit’s linear flow rule (output-port flows as a function of input-port flows), used by the plant’s exact recycle-flow solve. Receives a FlowContext so a state- or time-dependent split has one fixed signature.

Every method receives the same fixed arguments for every unit – the plant never branches its call on a per-unit capability flag. A unit ignores the arguments it does not use (signals for an uncontrolled unit, the FlowContext for a fixed-split unit).

compute_outputs, rhs and flow_outputs must be AD-clean (no Python branching on traced values, no concretisation of t / state).

Optional capability hooks (a unit opts in by implementing the method; the plant detects it with isinstance). Each has a named runtime_checkable Protocol below so the contract is explicit:

  • SignalProducer (signal_outputs) – produces control signals for the shared signal bus (e.g. a PI controller).

  • PHOperating (operating_pH) – exposes its state-derived pH to a pH-coupled interface translator.

  • LiquidVolumeUnit (liquid_volume) – a state-dependent liquid volume, read by the results-level mass balance.

  • ComponentInventoryUnit (component_inventory) – owns its COD/N/P inventory for a non-concentration-vector state layout.

  • CycleEventSource (cycle_events) – schedules located phase events (an SBR’s fill/react/settle/decant boundaries).

  • TemperatureSettable (set_temperature) – its operating temperature can be set by Plant.set_temperature().

A few further optional hooks are read as values with a default rather than checked for presence, so they stay plain attribute look-ups (not Protocols): signal_names / required_signals (the signals a unit publishes / consumes), flow_param_defaults (a flow-parameterized unit’s setpoint defaults), and, on a StateTranslator, needs_src_pH / needs_dest_pH (a pH-feedback interface).

See aquakin.plant.control and Plant._rhs.

__init__(*args, **kwargs)#

Methods

__init__(*args, **kwargs)

compute_outputs(t, state, inputs, params[, ...])

flow_outputs(input_flows, params, ctx)

initial_state()

rhs(t, state, inputs, params[, signals])

Attributes

name

state_size

input_ports

output_ports