Skip to content

tank_valves

Two-tank system with valve-based flow control.

tank_valves(area_1=1.0, area_2=1.2, inflow=0.8, outflow=0.6, outflow_1=0.2, valve_gain=1.0, high_level=1.2, low_level=0.4, gravity=9.81, initial_state=None)

Create a two-tank benchmark with valve-controlled inter-tank flow.

The system has two locations: valve open/closed. Flow between tanks depends on the valve state and level difference. Event surfaces are based on level thresholds.

Parameters:

Name Type Description Default
area_1 float

Cross-sectional area of tank 1.

1.0
area_2 float

Cross-sectional area of tank 2.

1.2
inflow float

Constant inflow to tank 1.

0.8
outflow float

Constant outflow from tank 2.

0.6
outflow_1 float

Constant outflow from tank 1.

0.2
valve_gain float

Flow gain when valve is open.

1.0
high_level float

Threshold to open the valve.

1.2
low_level float

Threshold to close the valve.

0.4
gravity float

Gravitational constant.

9.81
initial_state ndarray | None

Optional initial [level_1, level_2].

None

Returns:

Type Description
HybridSystem

HybridSystem configured for a valve-controlled two-tank system.

Source code in src/flowcean/hybrid/benchmarks/tank_valves.py
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
def tank_valves(
    area_1: float = 1.0,
    area_2: float = 1.2,
    inflow: float = 0.8,
    outflow: float = 0.6,
    outflow_1: float = 0.2,
    valve_gain: float = 1.0,
    high_level: float = 1.2,
    low_level: float = 0.4,
    gravity: float = 9.81,
    initial_state: np.ndarray | None = None,
) -> HybridSystem:
    """Create a two-tank benchmark with valve-controlled inter-tank flow.

    The system has two locations: valve open/closed. Flow between tanks depends
    on the valve state and level difference. Event surfaces are based on level
    thresholds.

    Args:
        area_1: Cross-sectional area of tank 1.
        area_2: Cross-sectional area of tank 2.
        inflow: Constant inflow to tank 1.
        outflow: Constant outflow from tank 2.
        outflow_1: Constant outflow from tank 1.
        valve_gain: Flow gain when valve is open.
        high_level: Threshold to open the valve.
        low_level: Threshold to close the valve.
        gravity: Gravitational constant.
        initial_state: Optional initial [level_1, level_2].

    Returns:
        HybridSystem configured for a valve-controlled two-tank system.
    """

    def _inter_tank_flow(
        state: np.ndarray,
        params: Parameters,
    ) -> float:
        level_1, level_2 = state
        head = max(level_1 - level_2, 0.0)
        return params["valve_gain"] * np.sqrt(2.0 * params["gravity"] * head)

    def flow_open(
        _t: float,
        state: np.ndarray,
        params: Parameters,
        _input_stream: InputStream,
    ) -> np.ndarray:
        _level_1, _level_2 = state
        inter_flow = _inter_tank_flow(state, params)
        dlevel_1 = (
            params["inflow"] - inter_flow - params["outflow_1"]
        ) / params["area_1"]
        dlevel_2 = (inter_flow - params["outflow"]) / params["area_2"]
        return np.array([dlevel_1, dlevel_2], dtype=float)

    def flow_closed(
        _t: float,
        state: np.ndarray,
        params: Parameters,
        _input_stream: InputStream,
    ) -> np.ndarray:
        _level_1, _level_2 = state
        dlevel_1 = (params["inflow"] - params["outflow_1"]) / params["area_1"]
        dlevel_2 = (-params["outflow"]) / params["area_2"]
        return np.array([dlevel_1, dlevel_2], dtype=float)

    def event_surface_open(
        _t: float,
        state: np.ndarray,
        params: Parameters,
        _input_stream: InputStream,
    ) -> float:
        return state[0] - params["high_level"]

    def event_surface_close(
        _t: float,
        state: np.ndarray,
        params: Parameters,
        _input_stream: InputStream,
    ) -> float:
        return state[0] - params["low_level"]

    open_dynamics = ContinuousDynamics(flow_open, label="open")
    closed_dynamics = ContinuousDynamics(flow_closed, label="closed")
    open_mode = Location(open_dynamics, label="open")
    closed_mode = Location(closed_dynamics, label="closed")

    transitions = [
        Transition(
            source=closed_mode,
            target=open_mode,
            event=EventSurface(
                event_surface_open,
                direction=CrossingDirection.RISING,
                label="level_high",
            ),
        ),
        Transition(
            source=open_mode,
            target=closed_mode,
            event=EventSurface(
                event_surface_close,
                direction=CrossingDirection.FALLING,
                label="level_low",
            ),
        ),
    ]

    if initial_state is None:
        initial_state = np.array([0.6, 0.2], dtype=float)

    return HybridSystem(
        locations=[open_mode, closed_mode],
        transitions=transitions,
        initial_location=closed_mode,
        initial_state=initial_state,
        parameters={
            "area_1": area_1,
            "area_2": area_2,
            "inflow": inflow,
            "outflow": outflow,
            "valve_gain": valve_gain,
            "high_level": high_level,
            "low_level": low_level,
            "gravity": gravity,
            "outflow_1": outflow_1,
        },
    )