173 lines
5.5 KiB
Python
173 lines
5.5 KiB
Python
from __future__ import annotations
|
|
|
|
from PythonModels.core.base import AlgebraicComponent
|
|
from PythonModels.core.ports import PortState
|
|
|
|
|
|
class Tee(AlgebraicComponent):
|
|
"""Python port of ModelicaModels.Mytee."""
|
|
|
|
def __init__(self, name: str) -> None:
|
|
super().__init__(name=name)
|
|
self.port_in = PortState()
|
|
self.port_out1 = PortState()
|
|
self.port_out2 = PortState()
|
|
|
|
def mixed_inlet_enthalpy(
|
|
self,
|
|
branch1_m_flow: float,
|
|
branch1_h: float,
|
|
branch2_m_flow: float,
|
|
branch2_h: float,
|
|
fallback_h: float = 0.0,
|
|
) -> float:
|
|
positive_1 = max(branch1_m_flow, 0.0)
|
|
positive_2 = max(branch2_m_flow, 0.0)
|
|
total = positive_1 + positive_2
|
|
if total <= 1e-9:
|
|
return fallback_h
|
|
return (positive_1 * branch1_h + positive_2 * branch2_h) / total
|
|
|
|
def inlet_stream_enthalpy(
|
|
self,
|
|
branch1_m_flow: float,
|
|
branch1_h: float,
|
|
branch2_m_flow: float,
|
|
branch2_h: float,
|
|
fallback_h: float,
|
|
) -> float:
|
|
"""Approximate `inStream(port_in.h_outflow)` for the current tee topology."""
|
|
|
|
return self.mixed_inlet_enthalpy(
|
|
branch1_m_flow,
|
|
branch1_h,
|
|
branch2_m_flow,
|
|
branch2_h,
|
|
fallback_h=fallback_h,
|
|
)
|
|
|
|
def branch_actual_stream_enthalpy(
|
|
self,
|
|
branch_m_flow: float,
|
|
branch_h: float,
|
|
inlet_h: float,
|
|
) -> float:
|
|
"""Approximate `actualStream(branch.h_outflow)` for a tee branch port."""
|
|
|
|
return inlet_h if branch_m_flow > 0.0 else branch_h
|
|
|
|
@staticmethod
|
|
def _solve_linear_2x2(
|
|
a11: float,
|
|
a12: float,
|
|
a21: float,
|
|
a22: float,
|
|
b1: float,
|
|
b2: float,
|
|
) -> tuple[float, float] | None:
|
|
determinant = a11 * a22 - a12 * a21
|
|
if abs(determinant) <= 1e-12:
|
|
return None
|
|
x1 = (b1 * a22 - b2 * a12) / determinant
|
|
x2 = (a11 * b2 - a21 * b1) / determinant
|
|
return x1, x2
|
|
|
|
def solve_branch_outlet_flows_from_energy_balance(
|
|
self,
|
|
*,
|
|
ratio_branch1: float,
|
|
ratio_branch2: float,
|
|
inlet_h_branch1: float,
|
|
inlet_h_branch2: float,
|
|
branch1_h: float,
|
|
branch2_h: float,
|
|
inlet_h: float,
|
|
q_in_branch1: float,
|
|
q_in_branch2: float,
|
|
tolerance: float = 1e-12,
|
|
) -> tuple[float, float]:
|
|
"""Solve branch outlet flows for the current three-port downstream tee use-case."""
|
|
|
|
rhs_branch1 = q_in_branch1 * inlet_h_branch1
|
|
rhs_branch2 = q_in_branch2 * inlet_h_branch2
|
|
|
|
def solve_both_forward() -> tuple[float, float] | None:
|
|
return self._solve_linear_2x2(
|
|
(1.0 + ratio_branch1) * branch1_h,
|
|
ratio_branch1 * branch2_h,
|
|
ratio_branch2 * branch1_h,
|
|
(1.0 + ratio_branch2) * branch2_h,
|
|
rhs_branch1,
|
|
rhs_branch2,
|
|
)
|
|
|
|
def solve_one_reverse(
|
|
*,
|
|
branch1_reverse: bool,
|
|
) -> tuple[float, float] | None:
|
|
if branch1_reverse:
|
|
return self._solve_linear_2x2(
|
|
inlet_h * (1.0 + ratio_branch1),
|
|
ratio_branch1 * inlet_h,
|
|
ratio_branch2 * inlet_h,
|
|
branch2_h + ratio_branch2 * inlet_h,
|
|
rhs_branch1,
|
|
rhs_branch2,
|
|
)
|
|
|
|
return self._solve_linear_2x2(
|
|
branch1_h + ratio_branch1 * inlet_h,
|
|
ratio_branch1 * inlet_h,
|
|
ratio_branch2 * inlet_h,
|
|
inlet_h * (1.0 + ratio_branch2),
|
|
rhs_branch1,
|
|
rhs_branch2,
|
|
)
|
|
|
|
def solve_both_reverse() -> tuple[float, float] | None:
|
|
return self._solve_linear_2x2(
|
|
inlet_h * (1.0 + ratio_branch1),
|
|
ratio_branch1 * inlet_h,
|
|
ratio_branch2 * inlet_h,
|
|
inlet_h * (1.0 + ratio_branch2),
|
|
rhs_branch1,
|
|
rhs_branch2,
|
|
)
|
|
|
|
candidate_solvers = (
|
|
(
|
|
solve_both_forward,
|
|
lambda q1, q2: q1 >= -tolerance and q2 >= -tolerance,
|
|
),
|
|
(
|
|
lambda: solve_one_reverse(branch1_reverse=True),
|
|
lambda q1, q2: q1 < -tolerance and q2 >= -tolerance and q1 + q2 > tolerance,
|
|
),
|
|
(
|
|
lambda: solve_one_reverse(branch1_reverse=True),
|
|
lambda q1, q2: q1 < -tolerance and q2 >= -tolerance and q1 + q2 <= tolerance,
|
|
),
|
|
(
|
|
lambda: solve_one_reverse(branch1_reverse=False),
|
|
lambda q1, q2: q2 < -tolerance and q1 >= -tolerance and q1 + q2 > tolerance,
|
|
),
|
|
(
|
|
lambda: solve_one_reverse(branch1_reverse=False),
|
|
lambda q1, q2: q2 < -tolerance and q1 >= -tolerance and q1 + q2 <= tolerance,
|
|
),
|
|
(
|
|
solve_both_reverse,
|
|
lambda q1, q2: q1 < -tolerance and q2 < -tolerance,
|
|
),
|
|
)
|
|
|
|
for solver, predicate in candidate_solvers:
|
|
candidate = solver()
|
|
if candidate is None:
|
|
continue
|
|
q_out_branch1, q_out_branch2 = candidate
|
|
if predicate(q_out_branch1, q_out_branch2):
|
|
return q_out_branch1, q_out_branch2
|
|
|
|
return solve_both_forward() or (0.0, 0.0)
|