def convergence_check(self): r""" Perform a convergence check. Note ---- Manipulate enthalpies/pressure at inlet and outlet if not specified by user to match physically feasible constraints. """ i, o = self.inl, self.outl if not o[0].p.val_set and o[0].p.val_SI < i[0].p.val_SI: o[0].p.val_SI = o[0].p.val_SI * 2 if not i[0].p.val_set and o[0].p.val_SI < i[0].p.val_SI: i[0].p.val_SI = o[0].p.val_SI * 0.5 if not o[0].h.val_set and o[0].h.val_SI < i[0].h.val_SI: o[0].h.val_SI = o[0].h.val_SI * 1.1 if not i[0].h.val_set and o[0].h.val_SI < i[0].h.val_SI: i[0].h.val_SI = o[0].h.val_SI * 0.9 if self.flow_char.is_set: expr = i[0].m.val_SI * v_mix_ph(i[0].get_flow(), T0=i[0].T.val_SI) if expr > self.flow_char.char_func.x[-1] and not i[0].m.val_set: i[0].m.val_SI = (self.flow_char.char_func.x[-1] / v_mix_ph(i[0].get_flow(), T0=i[0].T.val_SI)) elif expr < self.flow_char.char_func.x[1] and not i[0].m.val_set: i[0].m.val_SI = (self.flow_char.char_func.x[0] / v_mix_ph(i[0].get_flow(), T0=i[0].T.val_SI)) else: pass
def get_char_expr(self, param, type='rel', inconn=0, outconn=0): r""" Generic method to access characteristic function parameters. Parameters ---------- param : str Parameter for characteristic function evaluation. type : str Type of expression: - :code:`rel`: relative to design value - :code:`abs`: absolute value inconn : int Index of inlet connection. outconn : int Index of outlet connection. Returns ------- expr : float Value of expression """ if type == 'rel': if param == 'm': return (self.inl[inconn].m.val_SI / self.inl[inconn].m.design) elif param == 'm_out': return (self.outl[outconn].m.val_SI / self.outl[outconn].m.design) elif param == 'v': v = self.inl[inconn].m.val_SI * v_mix_ph( self.inl[inconn].get_flow(), T0=self.inl[inconn].T.val_SI) return v / self.inl[inconn].v.design elif param == 'pr': return ( (self.outl[outconn].p.val_SI * self.inl[inconn].p.design) / (self.inl[inconn].p.val_SI * self.outl[outconn].p.design)) else: msg = ('The parameter ' + str(param) + ' is not available ' 'for characteristic function evaluation.') logging.error(msg) raise ValueError(msg) else: if param == 'm': return self.inl[inconn].m.val_SI elif param == 'm_out': return self.outl[outconn].m.val_SI elif param == 'v': return self.inl[inconn].m.val_SI * v_mix_ph( self.inl[inconn].get_flow(), T0=self.inl[inconn].T.val_SI) elif param == 'pr': return (self.outl[outconn].p.val_SI / self.inl[inconn].p.val_SI) else: return False
def calc_parameters(self): r"""Postprocessing parameter calculation.""" i = self.inl[0].to_flow() o = self.outl[0].to_flow() self.pr.val = o[1] / i[1] self.zeta.val = ((i[1] - o[1]) * np.pi ** 2 / (8 * i[0] ** 2 * (v_mix_ph(i) + v_mix_ph(o)) / 2)) self.Sirr.val = i[0] * (s_mix_ph(o) - s_mix_ph(i)) self.check_parameter_bounds()
def zeta_func(self, zeta='', inconn=0, outconn=0): r""" Calculate residual value of :math:`\zeta`-function. Parameters ---------- zeta : str Component parameter to evaluate the zeta_func on, e. g. :code:`zeta1`. inconn : int Connection index of inlet. outconn : int Connection index of outlet. Returns ------- val : float Residual value of function. .. math:: val = \begin{cases} p_{in} - p_{out} & |\dot{m}| < \epsilon \\ \frac{\zeta}{D^4} - \frac{(p_{in} - p_{out}) \cdot \pi^2} {8 \cdot \dot{m}_{in} \cdot |\dot{m}_{in}| \cdot \frac{v_{in} + v_{out}}{2}} & |\dot{m}| > \epsilon \end{cases} Note ---- The zeta value is caluclated on the basis of a given pressure loss at a given flow rate in the design case. As the cross sectional area A will not change, it is possible to handle the equation in this way: .. math:: \frac{\zeta}{D^4} = \frac{\Delta p \cdot \pi^2} {8 \cdot \dot{m}^2 \cdot v} """ zeta = self.get_attr(zeta).val i = self.inl[inconn].to_flow() o = self.outl[outconn].to_flow() if abs(i[0]) < 1e-4: return i[1] - o[1] else: v_i = v_mix_ph(i, T0=self.inl[inconn].T.val_SI) v_o = v_mix_ph(o, T0=self.outl[outconn].T.val_SI) return (zeta - (i[1] - o[1]) * np.pi**2 / (8 * abs(i[0]) * i[0] * (v_i + v_o) / 2))
def cone_func(self): r""" Equation for stodolas cone law. Returns ------- residual : float Residual value of equation. .. math:: 0 = \frac{\dot{m}_{in,ref} \cdot p_{in}}{p_{in,ref}} \cdot \sqrt{\frac{p_{in,ref} \cdot v_{in}}{p_{in} \cdot v_{in,ref}}} \cdot \sqrt{\frac{1 - \left(\frac{p_{out}}{p_{in}} \right)^{2}} {1 - \left(\frac{p_{out,ref}}{p_{in,ref}} \right)^{2}}} - \dot{m}_{in} """ n = 1 i = self.inl[0] o = self.outl[0] vol = v_mix_ph(i.get_flow(), T0=self.inl[0].T.val_SI) return ( - i.m.val_SI + i.m.design * i.p.val_SI / i.p.design * np.sqrt(i.p.design * i.vol.design / (i.p.val_SI * vol)) * np.sqrt(abs((1 - (o.p.val_SI / i.p.val_SI) ** ((n + 1) / n)) / (1 - (self.pr.design) ** ((n + 1) / n)))))
def zeta_func(self): r""" Calculates residual value of :math:`\zeta`-function. Returns ------- val : float Residual value of function. .. math:: val = \begin{cases} p_{in} - p_{out} & |\dot{m}| < \epsilon \\ \frac{\zeta}{D^4} - \frac{(p_{in} - p_{out}) \cdot \pi^2}{8 \cdot \dot{m}_{in} \cdot |\dot{m}_{in}| \cdot \frac{v_{in} + v_{out}}{2}} & |\dot{m}| > \epsilon \end{cases} Note ---- The zeta value is caluclated on the basis of a given pressure loss at a given flow rate in the design case. As the cross sectional area A will not change, it is possible to handle the equation in this way: .. math:: \frac{\zeta}{D^4} = \frac{\Delta p \cdot \pi^2} {8 \cdot \dot{m}^2 \cdot v} """ i = self.inl[0].to_flow() o = self.outl[0].to_flow() if hasattr(self, 'zeta'): val = self.zeta.val else: val = self.zeta1.val if abs(i[0]) < 1e-4: return i[1] - o[1] else: v_i = v_mix_ph(i, T0=self.inl[0].T.val_SI) v_o = v_mix_ph(o, T0=self.outl[0].T.val_SI) return (val - (i[1] - o[1]) * np.pi**2 / (8 * abs(i[0]) * i[0] * (v_i + v_o) / 2))
def calc_parameters(self): r"""Postprocessing parameter calculation.""" self.Q.val = - self.inl[0].m.val_SI * (self.outl[0].h.val_SI - self.inl[0].h.val_SI) self.pr_c.val = self.outl[0].p.val_SI / self.inl[0].p.val_SI self.e.val = self.P.val / self.outl[2].m.val_SI self.eta.val = self.e0 / self.e.val i = self.inl[0].to_flow() o = self.outl[0].to_flow() self.zeta.val = ((i[1] - o[1]) * np.pi ** 2 / (8 * i[0] ** 2 * (v_mix_ph(i) + v_mix_ph(o)) / 2)) if self.eta_char.is_set: # get bound errors for efficiency characteristics expr = self.outl[2].m.val_SI / self.outl[2].m.design self.eta_char.func.get_bound_errors(expr, self.label) self.check_parameter_bounds()
def hazen_williams_func(self): r""" Equation for pressure drop calculation from Hazen-Williams equation. Returns ------- residual : float Residual value of equation. .. math:: 0 = \left(p_{in} - p_{out} \right) \cdot \left(-1\right)^i - \frac{10.67 \cdot |\dot{m}_{in}| ^ {1.852} \cdot L}{ks^{1.852} \cdot D^{4.871}} \cdot g \cdot \left(\frac{v_{in} + v_{out}}{2}\right)^{0.852} i = \begin{cases} 0 & \dot{m}_{in} \geq 0\\ 1 & \dot{m}_{in} < 0 \end{cases} Note ---- Gravity :math:`g` is set to :math:`9.81 \frac{m}{s^2}` """ i, o = self.inl[0].get_flow(), self.outl[0].get_flow() if abs(i[0]) < 1e-4: return i[1] - o[1] v_i = v_mix_ph(i, T0=self.inl[0].T.val_SI) v_o = v_mix_ph(o, T0=self.outl[0].T.val_SI) return ((i[1] - o[1]) * np.sign(i[0]) - (10.67 * abs(i[0])**1.852 * self.L.val / (self.ks.val**1.852 * self.D.val**4.871)) * (9.81 * ((v_i + v_o) / 2)**0.852))
def darcy_func(self): r""" Equation for pressure drop calculation from darcy friction factor. Returns ------- residual : float Residual value of equation. .. math:: 0 = p_{in} - p_{out} - \frac{8 \cdot |\dot{m}_{in}| \cdot \dot{m}_{in} \cdot \frac{v_{in}+v_{out}}{2} \cdot L \cdot \lambda\left(Re, ks, D\right)}{\pi^2 \cdot D^5}\\ Re = \frac{4 \cdot |\dot{m}_{in}|}{\pi \cdot D \cdot \frac{\eta_{in}+\eta_{out}}{2}}\\ \eta: \text{dynamic viscosity}\\ v: \text{specific volume}\\ \lambda: \text{darcy friction factor} """ i, o = self.inl[0].get_flow(), self.outl[0].get_flow() if abs(i[0]) < 1e-4: return i[1] - o[1] visc_i = visc_mix_ph(i, T0=self.inl[0].T.val_SI) visc_o = visc_mix_ph(o, T0=self.outl[0].T.val_SI) v_i = v_mix_ph(i, T0=self.inl[0].T.val_SI) v_o = v_mix_ph(o, T0=self.outl[0].T.val_SI) Re = 4 * abs(i[0]) / (np.pi * self.D.val * (visc_i + visc_o) / 2) return ( (i[1] - o[1]) - 8 * abs(i[0]) * i[0] * (v_i + v_o) / 2 * self.L.val * dff(Re, self.ks.val, self.D.val) / (np.pi**2 * self.D.val**5))
def calc_parameters(self): r"""Postprocessing parameter calculation.""" # connection information i1 = self.inl[0].to_flow() i2 = self.inl[1].to_flow() i3 = self.inl[2].to_flow() o1 = self.outl[0].to_flow() o2 = self.outl[1].to_flow() o3 = self.outl[2].to_flow() # specific volume v_i1 = v_mix_ph(i1, T0=self.inl[0].T.val_SI) v_i2 = v_mix_ph(i2, T0=self.inl[1].T.val_SI) v_i3 = v_mix_ph(i3, T0=self.inl[2].T.val_SI) v_o1 = v_mix_ph(o1, T0=self.outl[0].T.val_SI) v_o2 = v_mix_ph(o2, T0=self.outl[1].T.val_SI) v_o3 = v_mix_ph(o3, T0=self.outl[2].T.val_SI) # specific entropy s_i1 = s_mix_ph(i1, T0=self.inl[0].T.val_SI) s_i2 = s_mix_ph(i2, T0=self.inl[1].T.val_SI) s_i3 = s_mix_ph(i3, T0=self.inl[2].T.val_SI) s_o1 = s_mix_ph(o1, T0=self.outl[0].T.val_SI) s_o2 = s_mix_ph(o2, T0=self.outl[1].T.val_SI) s_o3 = s_mix_ph(o3, T0=self.outl[2].T.val_SI) # component parameters self.Q.val = -i3[0] * (o3[2] - i3[2]) self.pr1.val = o1[1] / i1[1] self.pr2.val = o2[1] / i2[1] self.pr3.val = o3[1] / i3[1] self.zeta1.val = ((i1[1] - o1[1]) * np.pi**2 / (8 * i1[0]**2 * (v_i1 + v_o1) / 2)) self.zeta2.val = ((i2[1] - o2[1]) * np.pi**2 / (8 * i2[0]**2 * (v_i2 + v_o2) / 2)) self.zeta3.val = ((i3[1] - o3[1]) * np.pi**2 / (8 * i3[0]**2 * (v_i3 + v_o3) / 2)) self.SQ1.val = self.inl[0].m.val_SI * (s_o1 - s_i1) self.SQ2.val = self.inl[1].m.val_SI * (s_o2 - s_i2) self.SQ3.val = self.inl[2].m.val_SI * (s_o3 - s_i3) self.Sirr.val = self.SQ1.val + self.SQ2.val + self.SQ3.val self.check_parameter_bounds()