diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..33bead1 --- /dev/null +++ b/.flake8 @@ -0,0 +1,47 @@ +[flake8] +max-line-length = 90 +accept-encodings = utf-8 +mypy_config = mypy.ini + +ignore = + # 1669 instances + A501, # A501 prefer built-in rounding of assertAlmostEqual() for 'round' expressions + + E226, # E226 missing whitespace around arithmetic operator (5939 instances) + + E265, # E265 block comment should start with '# ' (47 instances) + + E501, # E501 line too long (X > L characters) (127 instances) + + E741, # E741 ambiguous variable name 'l', 'I' (45 instances, mostly 'I/l') + + # Since this commit Python recommends breaking before operators. + # https://github.com/python/peps/commit/c59c4376ad233a62ca4b3a6060c81368bd21e85b + W503, # W503 line break before binary operator + + # Disable warnings about missing docstrings. + D107, # D107 Missing docstring in __init__ (11 instances) + + # (26 instances), Many summary lines are fairly complicated. + D205, # D205 1 blank line required between summary line and description + + D400, # D400 First line should end with a period (230 instances) + + D401, # D401 First line should be in imperative mood (20 instances) + + # One spurious instance, see: https://github.com/PyCQA/pydocstyle/issues/412 + D412, # D412 No blank lines allowed between a section header and its content + + R504, # R504 value assigned to variable is only used as a return value (42 instances) + + N081, # N801 class name 'fase' should use CapWords convention (12 instances) + + N802, # N802 function name '...' should be lowercase (143 instances) + + N803, # N803 argument name 'X' should be lowercase (138 instances) + + N806, # N806 variable 'X' in function should be lowercase (1014 instances) + + N815, # N815 variable 'xX' in class scope should not be mixedCase (24 instances) + + N816 # N816 variable 'isoP_kw' in global scope should not be mixedCase (11 instances) diff --git a/.gitignore b/.gitignore index f9f520f..2d509c7 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,9 @@ __pycache__/ *.py[cod] +# Emacs autosaves +**/*~ + # C extensions *.so diff --git a/docs/conf.py b/docs/conf.py index b5c28d4..3c27383 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,4 +1,6 @@ +#!/usr/bin/python # -*- coding: utf-8 -*- +"""Sphinx documentation config file.""" # # pychemqt documentation build configuration file, created by # sphinx-quickstart on Wed Jan 13 22:26:06 2016. @@ -14,6 +16,7 @@ import sys import os +from typing import Dict # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -46,7 +49,7 @@ 'sphinx.ext.viewcode', 'sphinx.ext.autosummary', 'sphinx.ext.napoleon', - 'numpydoc' + 'numpydoc', ] # Add any paths that contain templates here, relative to this directory. @@ -222,26 +225,26 @@ # -- Options for LaTeX output --------------------------------------------- -latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', +latex_elements: Dict[str, str] = { + # The paper size ('letterpaper' or 'a4paper'). + #'papersize': 'letterpaper', -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', + # The font size ('10pt', '11pt' or '12pt'). + #'pointsize': '10pt', -# Additional stuff for the LaTeX preamble. -#'preamble': '', + # Additional stuff for the LaTeX preamble. + #'preamble': '', -# Latex figure (float) alignment -#'figure_align': 'htbp', + # Latex figure (float) alignment + #'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'iapws.tex', u'iapws Documentation', - u'Juan José Gómez Romera', 'manual'), + (master_doc, 'iapws.tex', u'iapws Documentation', + u'Juan José Gómez Romera', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -271,7 +274,7 @@ # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'iapws', u'iapws Documentation', - [author], 1) + [author], 1), ] # If true, show URL addresses after external links. @@ -284,9 +287,9 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'iapws', u'iapws Documentation', - author, 'iapws', 'One line description of project.', - 'Miscellaneous'), + (master_doc, 'iapws', u'iapws Documentation', + author, 'iapws', 'One line description of project.', + 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. @@ -312,4 +315,3 @@ # Autosummary configuration autosummary_generate = True - diff --git a/iapws/__init__.py b/iapws/__init__.py index de21f2e..5140f32 100644 --- a/iapws/__init__.py +++ b/iapws/__init__.py @@ -1,5 +1,6 @@ #!/usr/bin/python # -*- coding: utf-8 -*- +"""International Association for the Properties of Water and Steam (IAPWS).""" import os @@ -241,4 +242,4 @@ "Seawater", "ref": "2016", "doi": ""}, - } +} diff --git a/iapws/_iapws.py b/iapws/_iapws.py index 7c6acd4..94297e5 100644 --- a/iapws/_iapws.py +++ b/iapws/_iapws.py @@ -31,13 +31,15 @@ from cmath import log as log_c from math import log, exp, tan, atan, acos, sin, pi, log10, copysign import warnings +from typing import Dict, Optional from scipy.optimize import minimize +from ._utils import _fase # Constants -M = 18.015268 # g/mol -R = 0.461526 # kJ/kg·K +_global_M = 18.015268 # g/mol +_global_R = 0.461526 # kJ/kg·K # Table 1 from Release on the Values of Temperature, Pressure and Density of # Ordinary and Heavy Water Substances at their Respective Critical Points @@ -59,7 +61,7 @@ # IAPWS-06 for Ice -def _Ice(T, P): +def _Ice(T: float, P: float) -> Dict[str, float]: """Basic state equation for Ice Ih Parameters @@ -150,14 +152,14 @@ def _Ice(T, P): t2 = complex(0.337315741065416, 0.335449415919309) r1 = complex(0.447050716285388e2, 0.656876847463481e2)*1e-3 - go = gop = gopp = 0 + go = gop = gopp = 0.0 for k in range(5): go += gok[k]*1e-3*(Pr-P0)**k for k in range(1, 5): gop += gok[k]*1e-3*k/Pt*(Pr-P0)**(k-1) for k in range(2, 5): gopp += gok[k]*1e-3*k*(k-1)/Pt**2*(Pr-P0)**(k-2) - r2 = r2p = 0 + r2 = r2p = complex(0.0, 0.0) for k in range(3): r2 += r2k[k]*(Pr-P0)**k for k in range(1, 3): @@ -207,7 +209,7 @@ def _Ice(T, P): # IAPWS-08 for Liquid water at 0.1 MPa -def _Liquid(T, P=0.1): +def _Liquid(T: float, P: float = 0.1) -> Dict[str, float]: """Supplementary release on properties of liquid water at 0.1 MPa Parameters @@ -277,51 +279,52 @@ def _Liquid(T, P=0.1): alfa = Tr/(593-T) beta = Tr/(T-232) - a = [None, -1.661470539e5, 2.708781640e6, -1.557191544e8, None, + a = [-1.661470539e5, 2.708781640e6, -1.557191544e8, 0.0, 1.93763157e-2, 6.74458446e3, -2.22521604e5, 1.00231247e8, -1.63552118e9, 8.32299658e9, -7.5245878e-6, -1.3767418e-2, 1.0627293e1, -2.0457795e2, 1.2037414e3] - b = [None, -8.237426256e-1, 1.908956353, -2.017597384, 8.546361348e-1, + b = [-8.237426256e-1, 1.908956353, -2.017597384, 8.546361348e-1, 5.78545292e-3, -1.53195665E-2, 3.11337859e-2, -4.23546241e-2, 3.38713507e-2, -1.19946761e-2, -3.1091470e-6, 2.8964919e-5, -1.3112763e-4, 3.0410453e-4, -3.9034594e-4, 2.3403117e-4, -4.8510101e-5] - c = [None, -2.452093414e2, 3.869269598e1, -8.983025854] - n = [None, 4, 5, 7, None, None, 4, 5, 7, 8, 9, 1, 3, 5, 6, 7] - m = [None, 2, 3, 4, 5, 1, 2, 3, 4, 5, 6, 1, 3, 4, 5, 6, 7, 9] + c = [-2.452093414e2, 3.869269598e1, -8.983025854] + # Zero entries are not used or present in the table. + n = [4, 5, 7, 0, 0, 4, 5, 7, 8, 9, 1, 3, 5, 6, 7] + m = [2, 3, 4, 5, 1, 2, 3, 4, 5, 6, 1, 3, 4, 5, 6, 7, 9] - suma1 = sum([a[i]*alfa**n[i] for i in range(1, 4)]) - suma2 = sum([b[i]*beta**m[i] for i in range(1, 5)]) - go = R*Tr*(c[1]+c[2]*tau+c[3]*tau*log(tau)+suma1+suma2) + suma1 = sum(a[i]*alfa**n[i] for i in range(0, 3)) + suma2 = sum(b[i]*beta**m[i] for i in range(0, 4)) + go = R*Tr*(c[0]+c[1]*tau+c[2]*tau*log(tau)+suma1+suma2) - suma1 = sum([a[i]*alfa**n[i] for i in range(6, 11)]) - suma2 = sum([b[i]*beta**m[i] for i in range(5, 11)]) - vo = R*Tr/Po/1000*(a[5]+suma1+suma2) + suma1 = sum(a[i]*alfa**n[i] for i in range(5, 10)) + suma2 = sum(b[i]*beta**m[i] for i in range(4, 10)) + vo = R*Tr/Po/1000*(a[4]+suma1+suma2) - suma1 = sum([a[i]*alfa**n[i] for i in range(11, 16)]) - suma2 = sum([b[i]*beta**m[i] for i in range(11, 18)]) + suma1 = sum(a[i]*alfa**n[i] for i in range(10, 15)) + suma2 = sum(b[i]*beta**m[i] for i in range(10, 17)) vpo = R*Tr/Po**2/1000*(suma1+suma2) - suma1 = sum([n[i]*a[i]*alfa**(n[i]+1) for i in range(1, 4)]) - suma2 = sum([m[i]*b[i]*beta**(m[i]+1) for i in range(1, 5)]) - so = -R*(c[2]+c[3]*(1+log(tau))+suma1-suma2) + suma1 = sum(n[i]*a[i]*alfa**(n[i]+1) for i in range(0, 3)) + suma2 = sum(m[i]*b[i]*beta**(m[i]+1) for i in range(0, 4)) + so = -R*(c[1]+c[2]*(1+log(tau))+suma1-suma2) - suma1 = sum([n[i]*(n[i]+1)*a[i]*alfa**(n[i]+2) for i in range(1, 4)]) - suma2 = sum([m[i]*(m[i]+1)*b[i]*beta**(m[i]+2) for i in range(1, 5)]) - cpo = -R*(c[3]+tau*suma1+tau*suma2) + suma1 = sum(n[i]*(n[i]+1)*a[i]*alfa**(n[i]+2) for i in range(0, 3)) + suma2 = sum(m[i]*(m[i]+1)*b[i]*beta**(m[i]+2) for i in range(0, 4)) + cpo = -R*(c[2]+tau*suma1+tau*suma2) - suma1 = sum([n[i]*a[i]*alfa**(n[i]+1) for i in range(6, 11)]) - suma2 = sum([m[i]*b[i]*beta**(m[i]+1) for i in range(5, 11)]) + suma1 = sum(n[i]*a[i]*alfa**(n[i]+1) for i in range(5, 10)) + suma2 = sum(m[i]*b[i]*beta**(m[i]+1) for i in range(4, 10)) vto = R/Po/1000*(suma1-suma2) # This properties are only neccessary for computing thermodynamic # properties at pressures different from 0.1 MPa - suma1 = sum([n[i]*(n[i]+1)*a[i]*alfa**(n[i]+2) for i in range(6, 11)]) - suma2 = sum([m[i]*(m[i]+1)*b[i]*beta**(m[i]+2) for i in range(5, 11)]) + suma1 = sum(n[i]*(n[i]+1)*a[i]*alfa**(n[i]+2) for i in range(5, 10)) + suma2 = sum(m[i]*(m[i]+1)*b[i]*beta**(m[i]+2) for i in range(4, 10)) vtto = R/Tr/Po/1000*(suma1+suma2) - suma1 = sum([n[i]*a[i]*alfa**(n[i]+1) for i in range(11, 16)]) - suma2 = sum([m[i]*b[i]*beta**(m[i]+1) for i in range(11, 18)]) + suma1 = sum(n[i]*a[i]*alfa**(n[i]+1) for i in range(10, 15)) + suma2 = sum(m[i]*b[i]*beta**(m[i]+1) for i in range(10, 17)) vpto = R/Po**2/1000*(suma1-suma2) if P != 0.1: @@ -335,8 +338,8 @@ def _Liquid(T, P=0.1): h = go+T*so u = h-P*vo - a = go-P*vo - cv = cpo+T*vto**2/vpo + a_result = go-P*vo + local_cv = cpo+T*vto**2/vpo xkappa = -vpo/vo alfa = vto/vo ks = -(T*vto**2/cpo+vpo)/vo @@ -355,31 +358,31 @@ def _Liquid(T, P=0.1): propiedades["h"] = h propiedades["s"] = so propiedades["cp"] = cpo - propiedades["cv"] = cv + propiedades["cv"] = local_cv propiedades["u"] = u - propiedades["a"] = a + propiedades["a"] = a_result propiedades["xkappa"] = xkappa propiedades["alfav"] = vto/vo propiedades["ks"] = ks propiedades["w"] = w # Viscosity correlation, Eq 7 - a = [None, 280.68, 511.45, 61.131, 0.45903] - b = [None, -1.9, -7.7, -19.6, -40] + a = [280.68, 511.45, 61.131, 0.45903] + b = [-1.9, -7.7, -19.6, -40] T_ = T/300 - mu = sum([a[i]*T_**b[i] for i in range(1, 5)])/1e6 + mu = sum(a[i]*T_**b[i] for i in range(0, 4))/1e6 propiedades["mu"] = mu # Thermal conductivity correlation, Eq 8 - c = [None, 1.6630, -1.7781, 1.1567, -0.432115] - d = [None, -1.15, -3.4, -6.0, -7.6] - k = sum([c[i]*T_**d[i] for i in range(1, 5)]) + c = [1.6630, -1.7781, 1.1567, -0.432115] + d = [-1.15, -3.4, -6.0, -7.6] + k = sum(c[i]*T_**d[i] for i in range(0, 4)) propiedades["k"] = k # Dielectric constant correlation, Eq 9 - e = [None, -43.7527, 299.504, -399.364, 221.327] - f = [None, -0.05, -1.47, -2.11, -2.31] - epsilon = sum([e[i]*T_**f[i] for i in range(1, 5)]) + e = [-43.7527, 299.504, -399.364, 221.327] + f = [-0.05, -1.47, -2.11, -2.31] + epsilon = sum(e[i]*T_**f[i] for i in range(0, 4)) propiedades["epsilon"] = epsilon return propiedades @@ -387,29 +390,30 @@ def _Liquid(T, P=0.1): class _Supercooled_minimize(object): - def __init__(self, L, omega, xmin, xmax): + def __init__(self, L: float, omega: float, xmin: float, xmax: float): self.L = L self.omega = omega self.xmin = xmin self.xmax = xmax self.f_inner_value_sign = 0.0 - def f(self, x): + def f(self, x: float) -> float: f_inner_value = self.L+log(x/(1-x))+self.omega*(1-2*x) self.f_inner_value_sign = copysign(1.0, f_inner_value) return abs(f_inner_value) - def jac(self, x): + def jac(self, x: float) -> float: return self.f_inner_value_sign*(1/(x*(1 - x)) - 2*self.omega) @property - def x(self): - return minimize(self.f, x0=((self.xmin+self.xmax)/2,), - bounds=((self.xmin, self.xmax),), jac=self.jac)["x"][0] + def x(self) -> float: + m = minimize(self.f, x0=((self.xmin+self.xmax)/2,), + bounds=((self.xmin, self.xmax),), jac=self.jac)["x"][0] + return float(m) # IAPWS-15 for supercooled liquid water -def _Supercooled(T, P): +def _Supercooled(T: float, P: float) -> Dict[str, float]: """Guideline on thermodynamic properties of supercooled water Parameters @@ -459,7 +463,6 @@ def _Supercooled(T, P): IAPWS, Guideline on Thermodynamic Properties of Supercooled Water, http://iapws.org/relguide/Supercooled.html """ - # Check input in range of validity if P < 198.9: Tita = T/235.15 @@ -494,16 +497,16 @@ def _Supercooled(T, P): 4.3773754, -2.9967770e-3, -9.6558018e-1, 3.7595286, 1.2632441, 2.8542697e-1, -8.5994947e-1, -3.2916153e-1, 9.0019616e-2, 8.1149726e-2, -3.2788213] - ai = [0, 0, 1, -0.2555, 1.5762, 1.6400, 3.6385, -0.3828, 1.6219, 4.3287, + ai = [0.0, 0.0, 1.0, -0.2555, 1.5762, 1.6400, 3.6385, -0.3828, 1.6219, 4.3287, 3.4763, 5.1556, -0.3593, 5.0361, 2.9786, 6.2373, 4.0460, 5.3558, 9.0157, 1.2194] - bi = [0, 1, 0, 2.1051, 1.1422, 0.9510, 0, 3.6402, 2.0760, -0.0016, 2.2769, + bi = [0.0, 1.0, 0.0, 2.1051, 1.1422, 0.9510, 0.0, 3.6402, 2.0760, -0.0016, 2.2769, 0.0008, 0.3706, -0.3975, 2.9730, -0.3180, 2.9805, 2.9265, 0.4456, 0.1298] - di = [0, 0, 0, -0.0016, 0.6894, 0.0130, 0.0002, 0.0435, 0.0500, 0.0004, + di = [0.0, 0.0, 0.0, -0.0016, 0.6894, 0.0130, 0.0002, 0.0435, 0.0500, 0.0004, 0.0528, 0.0147, 0.8584, 0.9924, 1.0041, 1.0961, 1.0228, 1.0303, 1.6180, 0.5213] - phir = phirt = phirp = phirtt = phirtp = phirpp = 0 + phir = phirt = phirp = phirtt = phirtp = phirpp = 0.0 for c, a, b, d in zip(ci, ai, bi, di): phir += c*tau_**a*p_**b*exp(-d*p_) phirt += c*a*tau_**(a-1)*p_**b*exp(-d*p_) @@ -557,8 +560,8 @@ def _Supercooled(T, P): prop["g"] = phir+(tau+1)*(x*L+x*log(x)+(1-x)*log(1-x)+omega*x*(1-x)) # Eq 14 - prop["s"] = -R*((tau+1)/2*Lt*(fi+1) + - (x*L+x*log(x)+(1-x)*log(1-x)+omega*x*(1-x))+phirt) + prop["s"] = -R*((tau+1)/2*Lt*(fi+1) + + (x*L+x*log(x)+(1-x)*log(1-x)+omega*x*(1-x))+phirt) # Basic derived state properties prop["h"] = prop["g"]+T*prop["s"] @@ -569,8 +572,8 @@ def _Supercooled(T, P): prop["xkappa"] = prop["rho"]/rho0**2/R*1000/Tll*( (tau+1)/2*(Xi*(Lp-omega0*fi)**2-(fi+1)*Lpp)-phirpp) prop["alfap"] = prop["rho"]/rho0/Tll*( - Ltp/2*(tau+1)*(fi+1) + (omega0*(1-fi**2)/2+Lp*(fi+1))/2 - - (tau+1)*Lt/2*Xi*(Lp-omega0*fi) + phirtp) + Ltp/2*(tau+1)*(fi+1) + (omega0*(1-fi**2)/2+Lp*(fi+1))/2 + - (tau+1)*Lt/2*Xi*(Lp-omega0*fi) + phirtp) prop["cp"] = -R*(tau+1)*(Lt*(fi+1)+(tau+1)/2*(Ltt*(fi+1)-Lt**2*Xi)+phirtt) # Eq 16 @@ -581,7 +584,7 @@ def _Supercooled(T, P): return prop -def _Sublimation_Pressure(T): +def _Sublimation_Pressure(T: float) -> float: """Sublimation Pressure correlation Parameters @@ -612,7 +615,7 @@ def _Sublimation_Pressure(T): """ if 50 <= T <= 273.16: Tita = T/Tt - suma = 0 + suma = 0.0 a = [-0.212144006e2, 0.273203819e2, -0.61059813e1] expo = [0.333333333e-2, 1.20666667, 1.70333333] for ai, expi in zip(a, expo): @@ -622,7 +625,7 @@ def _Sublimation_Pressure(T): raise NotImplementedError("Incoming out of bound") -def _Melting_Pressure(T, ice="Ih"): +def _Melting_Pressure(T: float, ice: str = "Ih") -> float: """Melting Pressure correlation Parameters @@ -664,7 +667,7 @@ def _Melting_Pressure(T, ice="Ih"): Tita = T/Tref a = [0.119539337e7, 0.808183159e5, 0.33382686e4] expo = [3., 0.2575e2, 0.10375e3] - suma = 1 + suma = 1.0 for ai, expi in zip(a, expo): suma += ai*(1-Tita**expi) P = suma*Pref @@ -691,15 +694,16 @@ def _Melting_Pressure(T, ice="Ih"): Tref = 355 Pref = 2216.000 Tita = T/Tref - P = Pref*exp(1.73683*(1-1./Tita)-0.544606e-1*(1-Tita**5) + - 0.806106e-7*(1-Tita**22)) + P = Pref*exp(1.73683*(1-1./Tita)-0.544606e-1*(1-Tita**5) + + 0.806106e-7*(1-Tita**22)) else: raise NotImplementedError("Incoming out of bound") return P # Transport properties -def _Viscosity(rho, T, fase=None, drho=None): +def _Viscosity(rho: float, T: float, fase: Optional[_fase] = None, + drho: float = None) -> float: """Equation for the Viscosity Parameters @@ -735,7 +739,7 @@ def _Viscosity(rho, T, fase=None, drho=None): # Eq 11 H = [1.67752, 2.20462, 0.6366564, -0.241605] - mu0 = 100*Tr**0.5/sum([Hi/Tr**i for i, Hi in enumerate(H)]) + mu0 = 100*Tr**0.5/sum(Hi/Tr**i for i, Hi in enumerate(H)) # Eq 12 I = [0, 1, 2, 3, 0, 1, 2, 3, 5, 0, 1, 2, 3, 4, 0, 1, 0, 3, 4, 3, 5] @@ -744,10 +748,12 @@ def _Viscosity(rho, T, fase=None, drho=None): 0.188797e1, 0.126613e1, 0.120573, -0.281378, -0.906851, -0.772479, -0.489837, -0.257040, 0.161913, 0.257399, -0.325372e-1, 0.698452e-1, 0.872102e-2, -0.435673e-2, -0.593264e-3] - mu1 = exp(Dr*sum([(1/Tr-1)**i*h*(Dr-1)**j for i, j, h in zip(I, J, Hij)])) + mu1 = exp(Dr*sum((1/Tr-1)**i*h*(Dr-1)**j for i, j, h in zip(I, J, Hij))) # Critical enhancement if fase and drho: + assert(isinstance(fase.drhodP_T, float)) + qc = 1/1.9 qd = 1/1.1 @@ -788,7 +794,8 @@ def _Viscosity(rho, T, fase=None, drho=None): return mu*1e-6 -def _ThCond(rho, T, fase=None, drho=None): +def _ThCond(rho: float, T: float, fase: Optional[_fase] = None, + drho: Optional[float] = None) -> float: """Equation for the thermal conductivity Parameters @@ -824,7 +831,7 @@ def _ThCond(rho, T, fase=None, drho=None): # Eq 16 no = [2.443221e-3, 1.323095e-2, 6.770357e-3, -3.454586e-3, 4.096266e-4] - k0 = Tr**0.5/sum([n/Tr**i for i, n in enumerate(no)]) + k0 = Tr**0.5/sum(n/Tr**i for i, n in enumerate(no)) # Eq 17 I = [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, @@ -837,10 +844,15 @@ def _ThCond(rho, T, fase=None, drho=None): -1.40944978, 0.275418278, -0.0205938816, -1.21051378, 1.60812989, -0.621178141, 0.0716373224, -2.7203370, 4.57586331, -3.18369245, 1.1168348, -0.19268305, 0.012913842] - k1 = exp(d*sum([(1/Tr-1)**i*n*(d-1)**j for i, j, n in zip(I, J, nij)])) + k1 = exp(d*sum((1/Tr-1)**i*n*(d-1)**j for i, j, n in zip(I, J, nij))) # Critical enhancement if fase: + assert(isinstance(fase.drhodP_T, float)) + assert(isinstance(fase.cp, float)) + assert(isinstance(fase.cp_cv, float)) + assert(isinstance(fase.mu, float)) + R = 0.46151805 if not drho: @@ -861,7 +873,7 @@ def _ThCond(rho, T, fase=None, drho=None): else: ai = [1.11999926419994, 0.595748562571649, 9.88952565078920, -10.3255051147040, 4.66861294457414, -0.503243546373828] - drho = 1/sum([a*d**i for i, a in enumerate(ai)])*rhoc/Pc + drho = 1.0/sum(a*d**i for i, a in enumerate(ai))*rhoc/Pc DeltaX = d*(Pc/rhoc*fase.drhodP_T-Pc/rhoc*drho*1.5/Tr) if DeltaX < 0: @@ -872,7 +884,7 @@ def _ThCond(rho, T, fase=None, drho=None): # Eq 19 if y < 1.2e-7: - Z = 0 + Z = 0.0 else: Z = 2/pi/y*(((1-1/fase.cp_cv)*atan(y)+y/fase.cp_cv)-( 1-exp(-1/(1/y+y**2/3/d**2)))) @@ -889,7 +901,7 @@ def _ThCond(rho, T, fase=None, drho=None): return 1e-3*k -def _Tension(T): +def _Tension(T: float) -> float: """Equation for the surface tension Parameters @@ -931,7 +943,7 @@ def _Tension(T): raise NotImplementedError("Incoming out of bound") -def _Dielectric(rho, T): +def _Dielectric(rho: float, T: float) -> float: """Equation for the Dielectric constant Parameters @@ -977,8 +989,8 @@ def _Dielectric(rho, T): d = rho/rhoc Tr = Tc/T - I = [1, 1, 1, 2, 3, 3, 4, 5, 6, 7, 10, None] - J = [0.25, 1, 2.5, 1.5, 1.5, 2.5, 2, 2, 5, 0.5, 10, None] + I = [1, 1, 1, 2, 3, 3, 4, 5, 6, 7, 10] + J = [0.25, 1.0, 2.5, 1.5, 1.5, 2.5, 2.0, 2.0, 5.0, 0.5, 10.0] n = [0.978224486826, -0.957771379375, 0.237511794148, 0.714692244396, -0.298217036956, -0.108863472196, .949327488264e-1, -.980469816509e-2, .165167634970e-4, .937359795772e-4, -.12317921872e-9, @@ -987,13 +999,13 @@ def _Dielectric(rho, T): g = 1+n[11]*d/(Tc/228/Tr-1)**1.2 for i in range(11): g += n[i]*d**I[i]*Tr**J[i] - A = Na*mu**2*rho*g/M*1000/epsilon0/k/T - B = Na*alfa*rho/3/M*1000/epsilon0 + A = Na*mu**2*rho*g/_global_M*1000/epsilon0/k/T + B = Na*alfa*rho/3/_global_M*1000/epsilon0 e = (1+A+5*B+(9+2*A+18*B+A**2+10*A*B+9*B**2)**0.5)/4/(1-B) return e -def _Refractive(rho, T, l=0.5893): +def _Refractive(rho: float, T: float, l: float = 0.5893) -> float: """Equation for the refractive index Parameters @@ -1047,7 +1059,7 @@ def _Refractive(rho, T, l=0.5893): return ((2*A+1)/(1-A))**0.5 -def _Kw(rho, T): +def _Kw(rho: float, T: float) -> float: """Equation for the ionization constant of ordinary water Parameters @@ -1090,7 +1102,7 @@ def _Kw(rho, T): Mw = 18.015268 gamma = [6.1415e-1, 4.825133e4, -6.770793e4, 1.01021e7] - pKg = 0 + pKg = 0.0 for i, g in enumerate(gamma): pKg += g/T**i @@ -1100,7 +1112,7 @@ def _Kw(rho, T): return pKw -def _Conductivity(rho, T): +def _Conductivity(rho: float, T: float) -> float: """Equation for the electrolytic conductivity of liquid and dense supercrítical water @@ -1142,8 +1154,8 @@ def _Conductivity(rho, T): B = [16., 11.6, 3.26e-4, -2.3e-6, 1.1e-8] t = T-273.15 - Loo = A[0]-1/(1/A[1]+sum([A[i+2]*t**(i+1) for i in range(4)])) # Eq 5 - rho_h = B[0]-1/(1/B[1]+sum([B[i+2]*t**(i+1) for i in range(3)])) # Eq 6 + Loo = A[0]-1/(1/A[1]+sum(A[i+2]*t**(i+1) for i in range(4))) # Eq 5 + rho_h = B[0]-1/(1/B[1]+sum(B[i+2]*t**(i+1) for i in range(3))) # Eq 6 # Eq 4 L_o = (rho_h-rho_)*Loo/rho_h @@ -1154,7 +1166,7 @@ def _Conductivity(rho, T): # Heavy water transport properties -def _D2O_Viscosity(rho, T): +def _D2O_Viscosity(rho: float, T: float) -> float: """Equation for the Viscosity of heavy water Parameters @@ -1185,7 +1197,7 @@ def _D2O_Viscosity(rho, T): rhor = rho/358.0 no = [1.0, 0.940695, 0.578377, -0.202044] - fi0 = Tr**0.5/sum([n/Tr**i for i, n in enumerate(no)]) + fi0 = Tr**0.5/sum(n/Tr**i for i, n in enumerate(no)) Li = [0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 0, 1, 2, 5, 0, 1, 2, 3, 0, 1, 3, 5, 0, 1, 5, 3] @@ -1203,7 +1215,7 @@ def _D2O_Viscosity(rho, T): return 55.2651e-6*fi0*fi1 -def _D2O_ThCond(rho, T): +def _D2O_ThCond(rho: float, T: float) -> float: """Equation for the thermal conductivity of heavy water Parameters @@ -1235,11 +1247,11 @@ def _D2O_ThCond(rho, T): tau = Tr/(abs(Tr-1.1)+1.1) no = [1.0, 37.3223, 22.5485, 13.0465, 0.0, -2.60735] - Lo = sum([Li*Tr**i for i, Li in enumerate(no)]) + Lo = sum(Li*Tr**i for i, Li in enumerate(no)) nr = [483.656, -191.039, 73.0358, -7.57467] Lr = -167.31*(1-exp(-2.506*rhor))+sum( - [Li*rhor**(i+1) for i, Li in enumerate(nr)]) + Li*rhor**(i+1) for i, Li in enumerate(nr)) f1 = exp(0.144847*Tr-5.64493*Tr**2) f2 = exp(-2.8*(rhor-1)**2)-0.080738543*exp(-17.943*(rhor-0.125698)**2) @@ -1252,7 +1264,7 @@ def _D2O_ThCond(rho, T): return 0.742128e-3*(Lo+Lr+Lc+Ll) -def _D2O_Tension(T): +def _D2O_Tension(T: float) -> float: """Equation for the surface tension of heavy water Parameters @@ -1290,7 +1302,7 @@ def _D2O_Tension(T): raise NotImplementedError("Incoming out of bound") -def _D2O_Sublimation_Pressure(T): +def _D2O_Sublimation_Pressure(T: float) -> float: """Sublimation Pressure correlation for heavy water Parameters @@ -1321,7 +1333,7 @@ def _D2O_Sublimation_Pressure(T): """ if 210 <= T <= 276.969: Tita = T/276.969 - suma = 0 + suma = 0.0 ai = [-0.1314226e2, 0.3212969e2] ti = [-1.73, -1.42] for a, t in zip(ai, ti): @@ -1331,7 +1343,7 @@ def _D2O_Sublimation_Pressure(T): raise NotImplementedError("Incoming out of bound") -def _D2O_Melting_Pressure(T, ice="Ih"): +def _D2O_Melting_Pressure(T: float, ice: str = "Ih") -> float: """Melting Pressure correlation for heavy water Parameters @@ -1371,7 +1383,7 @@ def _D2O_Melting_Pressure(T, ice="Ih"): Tita = T/276.969 ai = [-0.30153e5, 0.692503e6] ti = [5.5, 8.2] - suma = 1 + suma = 1.0 for a, t in zip(ai, ti): suma += a*(1-Tita**t) P = suma*0.00066159 @@ -1392,7 +1404,7 @@ def _D2O_Melting_Pressure(T, ice="Ih"): return P -def _Henry(T, gas, liquid="H2O"): +def _Henry(T: float, gas: str, liquid: str = "H2O") -> float: """Equation for the calculation of Henry's constant Parameters @@ -1484,7 +1496,7 @@ def _Henry(T, gas, liquid="H2O"): else: ai = [-7.896657, 24.73308, -27.81128, 9.355913, -9.220083] bi = [1, 1.89, 2, 3, 3.6] - ps = Pc*exp(1/Tr*sum([a*tau**b for a, b in zip(ai, bi)])) + ps = Pc*exp(1/Tr*sum(a*tau**b for a, b in zip(ai, bi))) # Select values from Table 2 par = { @@ -1516,7 +1528,7 @@ def _Henry(T, gas, liquid="H2O"): return kh -def _Kvalue(T, gas, liquid="H2O"): +def _Kvalue(T: float, gas: str, liquid: str = "H2O") -> float: """Equation for the vapor-liquid distribution constant Parameters @@ -1609,7 +1621,7 @@ def _Kvalue(T, gas, liquid="H2O"): ci = [2.7072, 0.58662, -1.3069, -45.663] di = [0.374, 1.45, 2.6, 12.3] q = -0.024552 - f = sum([c*tau**d for c, d in zip(ci, di)]) + f = sum(c*tau**d for c, d in zip(ci, di)) # Select values from Table 2 par = {"He": (2267.4082, -2.9616, -3.2604, 7.8819), diff --git a/iapws/_utils.py b/iapws/_utils.py index 868982f..8dce5b7 100644 --- a/iapws/_utils.py +++ b/iapws/_utils.py @@ -12,9 +12,10 @@ """ from __future__ import division +from typing import Optional, Any -def getphase(Tc, Pc, T, P, x, region): +def getphase(Tc: float, Pc: float, T: float, P: float, x: float, region: int) -> str: """Return fluid phase string name Parameters @@ -62,61 +63,94 @@ def getphase(Tc, Pc, T, P, x, region): class _fase(object): - """Class to implement a null phase""" - v = None - rho = None - - h = None - s = None - u = None - a = None - g = None - - cp = None - cv = None - cp_cv = None - w = None - Z = None - fi = None - f = None - - mu = None - k = None - nu = None - Prandt = None - epsilon = None - alfa = None - n = None - - alfap = None - betap = None - joule = None - Gruneisen = None - alfav = None - kappa = None - betas = None - gamma = None - Kt = None - kt = None - Ks = None - ks = None - dpdT_rho = None - dpdrho_T = None - drhodT_P = None - drhodP_T = None - dhdT_rho = None - dhdT_P = None - dhdrho_T = None - dhdrho_P = None - dhdP_T = None - dhdP_rho = None - - Z_rho = None - IntP = None - hInput = None - - -def deriv_H(state, z, x, y, fase): + """ + Class to implement a null phase. + + IAPWS95 and IAPWS97 both implement Liquid and Gas/Vapor phasee in + addition to being phases themselves. Confusingly minor + differences between derived classes impose different constraints + on this class. + """ + + def __init__(self) -> None: + # One always computed form the other + self.v = float('nan') + self.rho = float('nan') + + self.h = float('nan') + self.s = float('nan') + + self.cv = float('nan') + self.alfap = float('nan') + self.betap = float('nan') + self.cp = float('nan') + self.kappa = float('nan') + self.alfav = float('nan') + + self.g = float('nan') + self.fi = float('nan') + + self.w = float('nan') + self.Z = float('nan') + + self.drhodP_T = float('nan') + self.mu = float('nan') + self.cp_cv = float('nan') + self.k = float('nan') + + self.epsilon: Optional[float] = None + self.n: Optional[float] = None + + # -------------------------------------------- + # Calculated identically between 95 and 97 + self.u = float('nan') + self.a = float('nan') + self.nu = float('nan') + self.Prandt = float('nan') + self.alfa = float('nan') + self.f = float('nan') + + # Calculated similarly, but not identically? + self.joule = float('nan') + self.gamma = float('nan') + self.deltat = float('nan') + + # Calculated on 95 only from earlier variables and self.M + self.rhoM = float('nan') + self.hM = float('nan') + self.sM = float('nan') + self.uM = float('nan') + self.aM = float('nan') + self.gM = float('nan') + self.cvM = float('nan') + self.cpM = float('nan') + self.Z_rho = float('nan') + + # Derivatives calculated only in IAPWS95 + self.dpdT_rho = float('nan') + self.dpdrho_T = float('nan') + self.drhodT_P = float('nan') + self.dhdT_rho = float('nan') + self.dhdT_P = float('nan') + self.dhdrho_T = float('nan') + self.dhdrho_P = float('nan') + self.dhdP_T = float('nan') + self.dhdP_rho = float('nan') + self.kt = float('nan') + self.ks = float('nan') + self.Ks = float('nan') + self.Kt = float('nan') + self.betas = float('nan') + self.Gruneisen = float('nan') + self.IntP = float('nan') + self.hInput = float('nan') + + # Properties added because various methods set/access them? + self.xkappa = float('nan') + self.kappas = float('nan') + + +def deriv_H(state: Any, z: str, x: str, y: str, fase: _fase) -> float: r"""Calculate generic partial derivative :math:`\left.\frac{\partial z}{\partial x}\right|_{y}` from a fundamental helmholtz free energy equation of state @@ -161,16 +195,24 @@ def deriv_H(state, z, x, y, fase): # We use the relation between rho and v and his partial derivative # ∂v/∂b|c = -1/ρ² ∂ρ/∂b|c # ∂a/∂v|c = -ρ² ∂a/∂ρ|c - mul = 1 + mul = 1.0 if z == "rho": + assert(isinstance(fase.rho, float)) mul = -fase.rho**2 z = "v" if x == "rho": + assert(isinstance(fase.rho, float)) mul = -1/fase.rho**2 x = "v" if y == "rho": y = "v" + assert(isinstance(fase.alfap, float)) + assert(isinstance(fase.betap, float)) + assert(isinstance(fase.v, float)) + assert(isinstance(fase.cv, float)) + assert(isinstance(fase.s, float)) + dT = {"P": state.P*1000*fase.alfap, "T": 1, "v": 0, @@ -191,7 +233,7 @@ def deriv_H(state, z, x, y, fase): return mul*deriv -def deriv_G(state, z, x, y, fase): +def deriv_G(state: Any, z: str, x: str, y: str, fase: _fase) -> float: r"""Calculate generic partial derivative :math:`\left.\frac{\partial z}{\partial x}\right|_{y}` from a fundamental Gibbs free energy equation of state @@ -233,14 +275,22 @@ def deriv_G(state, z, x, y, fase): IAPWS, Revised Advisory Note No. 3: Thermodynamic Derivatives from IAPWS Formulations, http://www.iapws.org/relguide/Advise3.pdf """ - mul = 1 + mul = 1.0 if z == "rho": + assert(isinstance(fase.rho, float)) mul = -fase.rho**2 z = "v" if x == "rho": + assert(isinstance(fase.rho, float)) mul = -1/fase.rho**2 x = "v" + assert(isinstance(fase.alfav, float)) + assert(isinstance(fase.v, float)) + assert(isinstance(fase.cp, float)) + assert(isinstance(fase.s, float)) + assert(isinstance(fase.xkappa, float)) + dT = {"P": 0, "T": 1, "v": fase.v*fase.alfav, diff --git a/iapws/ammonia.py b/iapws/ammonia.py index b569cfc..905ecd2 100644 --- a/iapws/ammonia.py +++ b/iapws/ammonia.py @@ -13,9 +13,12 @@ from __future__ import division from math import exp, log, pi import warnings +from typing import Dict, Optional -from scipy.constants import Boltzmann as kb +from scipy.constants import Boltzmann from .iapws95 import MEoS, IAPWS95, mainClassDoc +from ._utils import _fase +from .helmholtz import ResidualContribution @mainClassDoc() @@ -30,11 +33,12 @@ class NH3(MEoS): R134a, R152a, and R123. Springer-Verlag, Berlin, 1994. http://doi.org/10.1007/978-3-642-79400-1 """ + name = "ammonia" CASNumber = "7664-41-7" formula = "NH3" synonym = "R-717" - rhoc = 225. + rhoc = 225.0 Tc = 405.40 Pc = 11.333 # MPa M = 17.03026 # g/mol @@ -43,28 +47,32 @@ class NH3(MEoS): f_acent = 0.25601 momentoDipolar = 1.470 - Fi0 = {"ao_log": [1, -1], - "pow": [0, 1, 1./3, -1.5, -1.75], + Fi0 = {"ao_log": [1.0, -1.0], + "pow": [0.0, 1.0, 1.0/3.0, -1.5, -1.75], "ao_pow": [-15.81502, 4.255726, 11.47434, -1.296211, 0.5706757], "ao_exp": [], "titao": [], "ao_hyp": [], "hyp": []} - _constants = { - "R": 8.314471, - - "nr1": [-0.1858814e01, 0.4554431e-1, 0.7238548, 0.1229470e-1, - 0.2141882e-10], - "d1": [1, 2, 1, 4, 15], - "t1": [1.5, -0.5, 0.5, 1., 3.], - - "nr2": [-0.1430020e-1, 0.3441324, -0.2873571, 0.2352589e-4, - -0.3497111e-1, 0.1831117e-2, 0.2397852e-1, -0.4085375e-1, - 0.2379275, -0.3548972e-1, -0.1823729, 0.2281556e-1, - -0.6663444e-2, -0.8847486e-2, 0.2272635e-2, -0.5588655e-3], - "d2": [3, 3, 1, 8, 2, 8, 1, 1, 2, 3, 2, 4, 3, 1, 2, 4], - "t2": [0, 3, 4, 4, 5, 5, 3, 6, 8, 8, 10, 10, 5, 7.5, 15, 30], - "c2": [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3], - "gamma2": [1]*16} + _constant_R = 8.314471 + _constant_rhoc = 225.0 + _constant_Tref = 405.40 + residual = ResidualContribution( + nr1=[-0.1858814e01, 0.4554431e-1, 0.7238548, 0.1229470e-1, + 0.2141882e-10], + d1=[1, 2, 1, 4, 15], + t1=[1.5, -0.5, 0.5, 1., 3.], + + nr2=[-0.1430020e-1, 0.3441324, -0.2873571, 0.2352589e-4, + -0.3497111e-1, 0.1831117e-2, 0.2397852e-1, -0.4085375e-1, + 0.2379275, -0.3548972e-1, -0.1823729, 0.2281556e-1, + -0.6663444e-2, -0.8847486e-2, 0.2272635e-2, -0.5588655e-3], + d2=[3, 3, 1, 8, 2, 8, 1, 1, 2, 3, 2, 4, 3, 1, 2, 4], + t2=[0, 3, 4, 4, 5, 5, 3, 6, 8, 8, 10, 10, 5, 7.5, 15, 30], + c2=[1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3], + + nr3=[], d3=[], t3=[], alfa3=[], beta3=[], gamma3=[], epsilon3=[], + nr4=[], a4=[], b4=[], A=[], B=[], C=[], D=[], beta4=[], + ) _melting = {"eq": 1, "Tref": Tt, "Pref": 1000, "Tmin": Tt, "Tmax": 700.0, @@ -72,20 +80,21 @@ class NH3(MEoS): "a3": [0.2533125e4], "exp3": [1]} _surf = {"sigma": [0.1028, -0.09453], "exp": [1.211, 5.585]} - _Pv = { - "eq": 5, - "ao": [-0.70993e1, -0.24330e1, 0.87591e1, -0.64091e1, -0.21185e1], - "exp": [1., 1.5, 1.7, 1.95, 4.2]} - _rhoL = { - "eq": 1, - "ao": [0.34488e2, -0.12849e3, 0.17382e3, -0.10699e3, 0.30339e2], - "exp": [0.58, 0.75, 0.9, 1.1, 1.3]} - _rhoG = { - "eq": 3, - "ao": [-.38435, -4.0846, -6.6634, -0.31881e2, 0.21306e3, -0.24648e3], - "exp": [0.218, 0.55, 1.5, 3.7, 5.5, 5.8]} - - def _visco(self, rho, T, fase=None): + # _Pv_eq = 5 # unused + _Pv_ao = [-0.70993e1, -0.24330e1, 0.87591e1, -0.64091e1, -0.21185e1] + _Pv_exp = [1.0, 1.5, 1.7, 1.95, 4.2] + _rhoL_eq = 1 + _rhoL_ao = [0.34488e2, -0.12849e3, 0.17382e3, -0.10699e3, 0.30339e2] + _rhoL_exp = [0.58, 0.75, 0.9, 1.1, 1.3] + _rhoG_eq = 3 + _rhoG_ao = [-.38435, -4.0846, -6.6634, -0.31881e2, 0.21306e3, -0.24648e3] + _rhoG_exp = [0.218, 0.55, 1.5, 3.7, 5.5, 5.8] + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + # fase is unused + def _visco(self, rho: float, T: float, fase: Optional[_fase] = None) -> float: """Equation for the Viscosity Parameters @@ -114,7 +123,7 @@ def _visco(self, rho, T, fase=None): # Eq 4 a = [4.99318220, -0.61122364, 0.0, 0.18535124, -0.11160946] - omega = exp(sum([ai*log(T_)**i for i, ai in enumerate(a)])) + omega = exp(sum(ai*log(T_)**i for i, ai in enumerate(a))) # Eq 2, Zero-Density Limit muo = 2.1357*(T*self.M)**0.5/sigma**2/omega @@ -124,7 +133,7 @@ def _visco(self, rho, T, fase=None): -0.13019164e5, 0.33414230e5, -0.58711743e5, 0.71426686e5, -0.59834012e5, 0.33652741e5, -0.1202735e5, 0.24348205e4, -0.20807957e3] - Bn = 0.6022137*sigma**3*sum([c*T_**(-i/2) for i, c in enumerate(cv)]) + Bn = 0.6022137*sigma**3*sum(c*T_**(-i/2) for i, c in enumerate(cv)) # Eq 7 mub = Bn*muo*rho @@ -133,13 +142,14 @@ def _visco(self, rho, T, fase=None): 1.67668649e-4, -1.49710093e-4, 0.77012274e-4] ji = [2, 4, 0, 1, 2, 3, 4] ii = [2, 2, 3, 3, 4, 4, 4] - mur = sum([d/T_**j*rho**i for d, j, i in zip(dij, ji, ii)]) + mur = sum(d/T_**j*rho**i for d, j, i in zip(dij, ji, ii)) # Eq 1 mu = muo + mub + mur return mu*1e-6 - def _thermo(self, rho, T, fase): + # fase is unused + def _thermo(self, rho: float, T: float, fase: Optional[_fase] = None) -> float: """Equation for the thermal conductivity Parameters @@ -166,24 +176,25 @@ def _thermo(self, rho, T, fase): # The paper use a diferent rhoc value to the EoS rhoc = 235 - if rho == rhoc and T == self.Tc: + if rho == rhoc and T == self._constant_Tref: warnings.warn("Thermal conductiviy undefined in critical point") - return None + return float('nan') # Eq 6 no = [0.3589e-1, -0.1750e-3, 0.4551e-6, 0.1685e-9, -0.4828e-12] - Lo = sum([n*T**i for i, n in enumerate(no)]) + Lo = sum(n*T**i for i, n in enumerate(no)) # Eq 7 nb = [0.16207e-3, 0.12038e-5, -0.23139e-8, 0.32749e-11] - L_ = sum([n*rho**(i+1) for i, n in enumerate(nb)]) + L_ = sum(n*rho**(i+1) for i, n in enumerate(nb)) # Critical enchancement t = abs(T-405.4)/405.4 dPT = 1e5*(2.18-0.12/exp(17.8*t)) - nb = 1e-5*(2.6+1.6*t) + nbx = 1e-5*(2.6+1.6*t) - DL = 1.2*kb*T**2/6/pi/nb/(1.34e-10/t**0.63*(1+t**0.5))*dPT**2 * \ + kb = float(Boltzmann) + DL = 1.2*kb*T**2/6/pi/nbx/(1.34e-10/t**0.63*(1+t**0.5))*dPT**2 * \ 0.423e-8/t**1.24*(1+t**0.5/0.7) # Add correction for entire range of temperature, Eq 10 @@ -204,10 +215,11 @@ def _thermo(self, rho, T, fase): class H2ONH3(object): + """Ammonia-water mixtures.""" # TODO: Add equilibrium routine - def _prop(self, rho, T, x): + def _prop(self, rho: float, T: float, x: float) -> Dict[str, float]: """Thermodynamic properties of ammonia-water mixtures Parameters @@ -277,15 +289,15 @@ def _prop(self, rho, T, x): prop["a"] = prop["u"]-T*prop["s"] cvR = -tau0**2*fiott - tau**2*firtt prop["cv"] = R*cvR - prop["cp"] = R*(cvR+(1+delta*fird-delta*tau*firdt)**2 / - (1+2*delta*fird+delta**2*firdd)) - prop["w"] = (R*T*1000*(1+2*delta*fird+delta**2*firdd + - (1+delta*fird-delta*tau*firdt)**2 / cvR))**0.5 + prop["cp"] = R*(cvR+(1+delta*fird-delta*tau*firdt)**2 + / (1+2*delta*fird+delta**2*firdd)) + prop["w"] = (R*T*1000*(1+2*delta*fird+delta**2*firdd + + (1+delta*fird-delta*tau*firdt)**2 / cvR))**0.5 prop["fugH2O"] = Z*exp(fir+delta*fird-x*F) prop["fugNH3"] = Z*exp(fir+delta*fird+(1-x)*F) return prop - def _phi0(self, rho, T, x): + def _phi0(self, rho: float, T: float, x: float) -> Dict[str, float]: """Ideal gas Helmholtz energy of binary mixtures and derivatives Parameters @@ -323,45 +335,44 @@ def _phi0(self, rho, T, x): tau = 500/T delta = rho/15/M - # Table 2 - Fi0 = { - "log_water": 3.006320, - "ao_water": [-7.720435, 8.649358], - "pow_water": [0, 1], - "ao_exp": [0.012436, 0.97315, 1.279500, 0.969560, 0.248730], - "titao": [1.666, 4.578, 10.018, 11.964, 35.600], - "log_nh3": -1.0, - "ao_nh3": [-16.444285, 4.036946, 10.69955, -1.775436, 0.82374034], - "pow_nh3": [0, 1, 1/3, -3/2, -7/4]} + # Table 2, previously an Fi0 dict + log_water = 3.006320 + ao_water = [-7.720435, 8.649358] + pow_water = [0, 1] + ao_exp = [0.012436, 0.97315, 1.279500, 0.969560, 0.248730] + titao = [1.666, 4.578, 10.018, 11.964, 35.600] + log_nh3 = -1.0 + ao_nh3 = [-16.444285, 4.036946, 10.69955, -1.775436, 0.82374034] + pow_nh3 = [0.0, 1.0, 1/3, -3/2, -7/4] fiod = 1/delta fiodd = -1/delta**2 - fiodt = 0 - fiow = fiotw = fiottw = 0 - fioa = fiota = fiotta = 0 + fiodt = 0.0 + fiow = fiotw = fiottw = 0.0 + fioa = fiota = fiotta = 0.0 # Water section if x < 1: - fiow = Fi0["log_water"]*log(tau) + log(1-x) - fiotw = Fi0["log_water"]/tau - fiottw = -Fi0["log_water"]/tau**2 - for n, t in zip(Fi0["ao_water"], Fi0["pow_water"]): - fiow += n*tau**t - if t != 0: - fiotw += t*n*tau**(t-1) - if t not in [0, 1]: - fiottw += n*t*(t-1)*tau**(t-2) - for n, t in zip(Fi0["ao_exp"], Fi0["titao"]): + fiow = log_water*log(tau) + log(1-x) + fiotw = log_water/tau + fiottw = -log_water/tau**2 + for n, ti in zip(ao_water, pow_water): + fiow += n*tau**ti + if ti != 0: + fiotw += ti*n*tau**(ti-1) + if ti not in [0, 1]: + fiottw += n*ti*(ti-1)*tau**(ti-2) + for n, t in zip(ao_exp, titao): fiow += n*log(1-exp(-tau*t)) fiotw += n*t*((1-exp(-t*tau))**-1-1) fiottw -= n*t**2*exp(-t*tau)*(1-exp(-t*tau))**-2 # ammonia section if x > 0: - fioa = Fi0["log_nh3"]*log(tau) + log(x) - fiota = Fi0["log_nh3"]/tau - fiotta = -Fi0["log_nh3"]/tau**2 - for n, t in zip(Fi0["ao_nh3"], Fi0["pow_nh3"]): + fioa = log_nh3*log(tau) + log(x) + fiota = log_nh3/tau + fiotta = -log_nh3/tau**2 + for n, t in zip(ao_nh3, pow_nh3): fioa += n*tau**t if t != 0: fiota += t*n*tau**(t-1) @@ -379,7 +390,7 @@ def _phi0(self, rho, T, x): prop["fiodt"] = fiodt return prop - def _phir(self, rho, T, x): + def _phir(self, rho: float, T: float, x: float) -> Dict[str, float]: """Residual contribution to the free Helmholtz energy Parameters @@ -414,7 +425,6 @@ def _phir(self, rho, T, x): Properties of Ammonia-Water Mixtures, http://www.iapws.org/relguide/nh3h2o.pdf, Eq 3 """ - # Temperature reducing value, Eq 4 Tc12 = 0.9648407/2*(IAPWS95.Tc+NH3.Tc) Tn = (1-x)**2*IAPWS95.Tc + x**2*NH3.Tc + 2*x*(1-x**1.125455)*Tc12 @@ -424,39 +434,39 @@ def _phir(self, rho, T, x): # Density reducing value, Eq 5 b = 0.8978069 rhoc12 = 1/(1.2395117/2*(1/IAPWS95.rhoc+1/NH3.rhoc)) - rhon = 1/((1-x)**2/IAPWS95.rhoc + x**2/NH3.rhoc + - 2*x*(1-x**b)/rhoc12) - drhonx = -(2*b*x**b/rhoc12 + 2*(1-x**b)/rhoc12 + - 2*x/NH3.rhoc - 2*(1-x)/IAPWS95.rhoc)/( - 2*x*(1-x**b)/rhoc12 + x**2/NH3.rhoc + - (1-x)**2/IAPWS95.rhoc)**2 + rhon = 1/((1-x)**2/IAPWS95.rhoc + x**2/NH3.rhoc + + 2*x*(1-x**b)/rhoc12) + drhonx = -(2*b*x**b/rhoc12 + 2*(1-x**b)/rhoc12 + + 2*x/NH3.rhoc - 2*(1-x)/IAPWS95.rhoc)/( + 2*x*(1-x**b)/rhoc12 + x**2/NH3.rhoc + + (1-x)**2/IAPWS95.rhoc)**2 tau = Tn/T delta = rho/rhon water = IAPWS95() - phi1 = water._phir(tau, delta) + phi1 = water.residual.helmholtz(tau, delta) ammonia = NH3() - phi2 = ammonia._phir(tau, delta) + phi2 = ammonia.residual.helmholtz(tau, delta) Dphi = self._Dphir(tau, delta, x) prop = {} prop["tau"] = tau prop["delta"] = delta - prop["fir"] = (1-x)*phi1["fir"] + x*phi2["fir"] + Dphi["fir"] - prop["firt"] = (1-x)*phi1["firt"] + x*phi2["firt"] + Dphi["firt"] - prop["firtt"] = (1-x)*phi1["firtt"] + x*phi2["firtt"] + Dphi["firtt"] - prop["fird"] = (1-x)*phi1["fird"] + x*phi2["fird"] + Dphi["fird"] - prop["firdd"] = (1-x)*phi1["firdd"] + x*phi2["firdd"] + Dphi["firdd"] - prop["firdt"] = (1-x)*phi1["firdt"] + x*phi2["firdt"] + Dphi["firdt"] - prop["firx"] = -phi1["fir"] + phi2["fir"] + Dphi["firx"] + prop["fir"] = (1-x)*phi1.fi + x*phi2.fi + Dphi["fir"] + prop["firt"] = (1-x)*phi1.fit + x*phi2.fit + Dphi["firt"] + prop["firtt"] = (1-x)*phi1.fitt + x*phi2.fitt + Dphi["firtt"] + prop["fird"] = (1-x)*phi1.fid + x*phi2.fid + Dphi["fird"] + prop["firdd"] = (1-x)*phi1.fidd + x*phi2.fidd + Dphi["firdd"] + prop["firdt"] = (1-x)*phi1.fidt + x*phi2.fidt + Dphi["firdt"] + prop["firx"] = -phi1.fi + phi2.fi + Dphi["firx"] prop["F"] = prop["firx"] - delta/rhon*drhonx*prop["fird"] + \ tau/Tn*dTnx*prop["firt"] return prop - def _Dphir(self, tau, delta, x): + def _Dphir(self, tau: float, delta: float, x: float) -> Dict[str, float]: """Departure function to the residual contribution to the free Helmholtz energy @@ -563,7 +573,7 @@ def _Dphir(self, tau, delta, x): return prop -def Ttr(x): +def Ttr(x: float) -> float: """Equation for the triple point of ammonia-water mixture Parameters diff --git a/iapws/helmholtz.py b/iapws/helmholtz.py new file mode 100644 index 0000000..70dcfe0 --- /dev/null +++ b/iapws/helmholtz.py @@ -0,0 +1,414 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +"""Calculations related to free Helmholtz Energy.""" + +from typing import Dict, List +from math import exp + + +class HelmholtzDerivatives(object): + """Helmholtz free energy and derivatives.""" + + def __init__(self, fi: float, fit: float, fid: float, + fitt: float, fidd: float, fidt: float) -> None: + self.fi = fi + self.fit = fit + self.fid = fid + self.fitt = fitt + self.fidd = fidd + self.fidt = fidt + + +class ResidualContribution(object): + """Residual contribution to the adimensional free Helmholtz energy""" + + def __init__(self, nr1: List[float], d1: List[float], t1: List[float], + nr2: List[float], d2: List[float], t2: List[float], + c2: List[int], nr3: List[float], d3: List[int], t3: List[float], + alfa3: List[float], epsilon3: List[float], beta3: List[float], + gamma3: List[float], nr4: List[float], a4: List[float], + b4: List[float], A: List[float], B: List[float], C: List[int], + D: List[int], beta4: List[float]) -> None: + # Polinomial terms + self.nr1 = nr1 + self.d1 = d1 + self.t1 = t1 + # Exponential terms + self.nr2 = nr2 + self.d2 = d2 + self.t2 = t2 + self.c2 = c2 + # Gaussian terms (optional, may be empty lists) + self.nr3 = nr3 + self.d3 = d3 + self.t3 = t3 + self.alfa3 = alfa3 + self.epsilon3 = epsilon3 + self.beta3 = beta3 + self.gamma3 = gamma3 + # Non analitic terms (optional, may be empty lists) + self.nr4 = nr4 + self.a4 = a4 + self.b4 = b4 + self.A = A + self.B = B + self.C = C + self.D = D + self.beta4 = beta4 + + def phir(self, tau: float, delta: float) -> float: + """Residual contribution to the adimensional free Helmholtz energy + + Parameters + ---------- + tau : float + Inverse reduced temperature Tc/T, [-] + delta : float + Reduced density rho/rhoc, [-] + coef : dict + Dictionary with equation of state parameters + + Returns + ------- + fir : float + Adimensional free Helmholtz energy + + References + ---------- + IAPWS, Revised Release on the IAPWS Formulation 1995 for the + Thermodynamic Properties of Ordinary Water Substance for General and + Scientific Use, September 2016, Table 5 + http://www.iapws.org/relguide/IAPWS-95.html + """ + # Ensure parameters are really floats. + tau = float(tau) + delta = float(delta) + + fir = 0.0 + # Polinomial terms + for n, d, t in zip(self.nr1, self.d1, self.t1): + fir += n*delta**d*tau**t + + # Exponential terms + for n, d, t, c in zip(self.nr2, self.d2, self.t2, self.c2): + fir += n*delta**d*tau**t*exp(-delta**c) + + # Gaussian terms + for n, d, t, a, e, b, g in zip(self.nr3, self.d3, self.t3, self.alfa3, + self.epsilon3, self.beta3, self.gamma3): + fir += n*delta**d*tau**t*exp(-a*(delta-e)**2-b*(tau-g)**2) + + # Non analitic terms + for n, a, b, A, B, C, D, bt in zip(self.nr4, self.a4, self.b4, self.A, + self.B, self.C, self.D, self.beta4): + Tita = (1-tau)+A*((delta-1)**2)**(0.5/bt) + F = exp(-C*(delta-1)**2-D*(tau-1)**2) + Delta = Tita**2+B*((delta-1)**2)**a + fir += n*Delta**b*delta*F + + return fir + + def phird(self, tau: float, delta: float) -> float: + r"""Residual contribution to the adimensional free Helmholtz energy, delta + derivative + + Parameters + ---------- + tau : float + Inverse reduced temperature Tc/T, [-] + delta : float + Reduced density rho/rhoc, [-] + coef : dict + Dictionary with equation of state parameters + + Returns + ------- + fird : float + .. math:: + \left.\frac{\partial \phi^r_{\delta}}{\partial \delta}\right|_{\tau} + + References + ---------- + IAPWS, Revised Release on the IAPWS Formulation 1995 for the + Thermodynamic Properties of Ordinary Water Substance for General and + Scientific Use, September 2016, Table 5 + http://www.iapws.org/relguide/IAPWS-95.html + """ + # Ensure parameters are really floats. + tau = float(tau) + delta = float(delta) + + fird = 0.0 + # Polinomial terms + for n, d, t in zip(self.nr1, self.d1, self.t1): + fird += n*d*delta**(d-1)*tau**t + + # Exponential terms + for n, d, t, c in zip(self.nr2, self.d2, self.t2, self.c2): + try: + expt = exp(-delta**c) + except OverflowError: + fird = float('nan') + break + fird += n*expt*delta**(d-1)*tau**t*(d-c*delta**c) + + # Gaussian terms + for n, d, t, a, e, b, g in zip(self.nr3, self.d3, self.t3, self.alfa3, + self.epsilon3, self.beta3, self.gamma3): + expt = exp(-a*(delta-e)**2-b*(tau-g)**2) + fird += n*delta**d*tau**t*expt*(d/delta-2*a*(delta-e)) + + # Non analitic terms + for n, a, b, A, B, C, D, bt in zip(self.nr4, self.a4, self.b4, self.A, + self.B, self.C, self.D, self.beta4): + Tita = (1-tau)+A*((delta-1)**2)**(0.5/bt) + F = exp(-C*(delta-1)**2-D*(tau-1)**2) + Fd = -2*C*F*(delta-1) + + Delta = Tita**2+B*((delta-1)**2)**a + Deltad = (delta-1)*(A*Tita*2/bt*((delta-1)**2)**(0.5/bt-1) + + 2*B*a*((delta-1)**2)**(a-1)) + DeltaBd = b*Delta**(b-1)*Deltad + + fird += n*(Delta**b*(F+delta*Fd)+DeltaBd*delta*F) + + return fird + + def phirt(self, tau: float, delta: float) -> float: + r"""Residual contribution to the adimensional free Helmholtz energy, tau + derivative + + Parameters + ---------- + tau : float + Inverse reduced temperature Tc/T, [-] + delta : float + Reduced density rho/rhoc, [-] + coef : dict + Dictionary with equation of state parameters + + Returns + ------- + firt : float + .. math:: + \left.\frac{\partial \phi^r_{\tau}}{\partial \tau}\right|_{\delta} + + References + ---------- + IAPWS, Revised Release on the IAPWS Formulation 1995 for the + Thermodynamic Properties of Ordinary Water Substance for General and + Scientific Use, September 2016, Table 5 + http://www.iapws.org/relguide/IAPWS-95.html + """ + # Ensure parameters are really floats. + tau = float(tau) + delta = float(delta) + + firt = 0.0 + # Polinomial terms + for n, d, t in zip(self.nr1, self.d1, self.t1): + firt += n*t*delta**d*tau**(t-1) + + # Exponential terms + for n, d, t, c in zip(self.nr2, self.d2, self.t2, self.c2): + firt += n*t*delta**d*tau**(t-1)*exp(-delta**c) + + # Gaussian terms + for n, d, t, a, e, b, g in zip(self.nr3, self.d3, self.t3, self.alfa3, + self.epsilon3, self.beta3, self.gamma3): + firt += n*delta**d*tau**t*exp(-a*(delta-e)**2-b*(tau-g)**2)*( + t/tau-2*b*(tau-g)) + + # Non analitic terms + for n, a, b, A, B, C, D, bt in zip(self.nr4, self.a4, self.b4, self.A, + self.B, self.C, self.D, self.beta4): + Tita = (1-tau)+A*((delta-1)**2)**(0.5/bt) + F = exp(-C*(delta-1)**2-D*(tau-1)**2) + Ft = -2*D*F*(tau-1) + Delta = Tita**2+B*((delta-1)**2)**a + DeltaBt = -2*Tita*b*Delta**(b-1) + firt += n*delta*(DeltaBt*F+Delta**b*Ft) + + return firt + + def helmholtz(self, tau: float, delta: float) -> HelmholtzDerivatives: + """Residual contribution to the free Helmholtz energy + + Parameters + ---------- + tau : float + Inverse reduced temperature Tc/T, [-] + delta : float + Reduced density rho/rhoc, [-] + + Returns + ------- + HelmholtzDerivatives class, with residual adimensional helmholtz + energy and deriv: + * fir + * firt: ∂fir/∂τ|δ,x + * fird: ∂fir/∂δ|τ,x + * firtt: ∂²fir/∂τ²|δ,x + * firdt: ∂²fir/∂τ∂δ|x + * firdd: ∂²fir/∂δ²|τ,x + + References + ---------- + IAPWS, Revised Release on the IAPWS Formulation 1995 for the + Thermodynamic Properties of Ordinary Water Substance for General and + Scientific Use, September 2016, Table 5 + http://www.iapws.org/relguide/IAPWS-95.html + """ + # Ensure parameters are really floats. + tau = float(tau) + delta = float(delta) + + fir = fird = firdd = firt = firtt = firdt = 0.0 + # Polinomial terms + for n, d, t in zip(self.nr1, self.d1, self.t1): + fir += n*delta**d*tau**t + fird += n*d*delta**(d-1)*tau**t + firdd += n*d*(d-1)*delta**(d-2)*tau**t + firt += n*t*delta**d*tau**(t-1) + firtt += n*t*(t-1)*delta**d*tau**(t-2) + firdt += n*t*d*delta**(d-1)*tau**(t-1) + + # Exponential terms + failed = False + for n, d, t, c in zip(self.nr2, self.d2, self.t2, self.c2): + try: + expdc = exp(-delta**c) + except OverflowError: + failed = True + expdc = float('inf') + fir += n*delta**d*tau**t*expdc + fird += n*expdc*delta**(d-1)*tau**t*(d-c*delta**c) + firdd += n*expdc*delta**(d-2)*tau**t * \ + ((d-c*delta**c)*(d-1-c*delta**c)-c**2*delta**c) + firt += n*t*delta**d*tau**(t-1)*expdc + firtt += n*t*(t-1)*delta**d*tau**(t-2)*expdc + firdt += n*t*delta**(d-1)*tau**(t-1)*(d-c*delta**c)*expdc + if failed: + fir = float('nan') + + # Gaussian terms + for n, d, t, a, e, b, g in zip(self.nr3, self.d3, self.t3, self.alfa3, + self.epsilon3, self.beta3, self.gamma3): + fir += n*delta**d*tau**t*exp(-a*(delta-e)**2-b*(tau-g)**2) + fird += n*delta**d*tau**t*exp(-a*(delta-e)**2-b*(tau-g)**2)*( + d/delta-2*a*(delta-e)) + firdd += n*tau**t*exp(-a*(delta-e)**2-b*(tau-g)**2)*( + -2*a*delta**d + 4*a**2*delta**d*(delta-e)**2 + - 4*d*a*delta**(d-1)*(delta-e) + d*(d-1)*delta**(d-2)) + firt += n*delta**d*tau**t*exp(-a*(delta-e)**2-b*(tau-g)**2)*( + t/tau-2*b*(tau-g)) + firtt += n*delta**d*tau**t*exp(-a*(delta-e)**2-b*(tau-g)**2)*( + (t/tau-2*b*(tau-g))**2-t/tau**2-2*b) + firdt += n*delta**d*tau**t*exp(-a*(delta-e)**2-b*(tau-g)**2)*( + t/tau-2*b*(tau-g))*(d/delta-2*a*(delta-e)) + + # Non analitic terms + for n, a, b, A, B, C, D, bt in zip(self.nr4, self.a4, self.b4, self.A, + self.B, self.C, self.D, self.beta4): + Tita = (1-tau)+A*((delta-1)**2)**(0.5/bt) + F = exp(-C*(delta-1)**2-D*(tau-1)**2) + Fd = -2*C*F*(delta-1) + Fdd = 2*C*F*(2*C*(delta-1)**2-1) + Ft = -2*D*F*(tau-1) + Ftt = 2*D*F*(2*D*(tau-1)**2-1) + Fdt = 4*C*D*F*(delta-1)*(tau-1) + + Delta = Tita**2+B*((delta-1)**2)**a + Deltad = (delta-1)*(A*Tita*2/bt*((delta-1)**2)**(0.5/bt-1) + + 2*B*a*((delta-1)**2)**(a-1)) + if delta == 1: + Deltadd = 0 + else: + Deltadd = Deltad/(delta-1)+(delta-1)**2*( + 4*B*a*(a-1)*((delta-1)**2)**(a-2) + + 2*A**2/bt**2*(((delta-1)**2)**(0.5/bt-1))**2 + + A*Tita*4/bt*(0.5/bt-1)*((delta-1)**2)**(0.5/bt-2)) + + DeltaBd = b*Delta**(b-1)*Deltad + DeltaBdd = b*(Delta**(b-1)*Deltadd+(b-1)*Delta**(b-2)*Deltad**2) + DeltaBt = -2*Tita*b*Delta**(b-1) + DeltaBtt = 2*b*Delta**(b-1)+4*Tita**2*b*(b-1)*Delta**(b-2) + DeltaBdt = -A*b*2/bt*Delta**(b-1)*(delta-1)*((delta-1)**2)**( + 0.5/bt-1)-2*Tita*b*(b-1)*Delta**(b-2)*Deltad + + fir += n*Delta**b*delta*F + fird += n*(Delta**b*(F+delta*Fd)+DeltaBd*delta*F) + firdd += n*(Delta**b*(2*Fd+delta*Fdd) + 2*DeltaBd*(F+delta*Fd) + + DeltaBdd*delta*F) + firt += n*delta*(DeltaBt*F+Delta**b*Ft) + firtt += n*delta*(DeltaBtt*F+2*DeltaBt*Ft+Delta**b*Ftt) + firdt += n*(Delta**b*(Ft+delta*Fdt)+delta*DeltaBd*Ft + + DeltaBt*(F+delta*Fd)+DeltaBdt*delta*F) + + return HelmholtzDerivatives(fir, firt, fird, firtt, firdd, firdt) + + def _virial(self, T: float, Tc: float) -> Dict[str, float]: + """Virial coefficient + + Parameters + ---------- + T : float + Temperature [K] + + Returns + ------- + prop : dict + Dictionary with residual adimensional helmholtz energy: + * B: ∂fir/∂δ|δ->0 + * C: ∂²fir/∂δ²|δ->0 + """ + tau = Tc/T + B = C = 0.0 + delta = 1e-200 + + # Polinomial terms + for n, d, t in zip(self.nr1, self.d1, self.t1): + B += n*d*delta**(d-1)*tau**t + C += n*d*(d-1)*delta**(d-2)*tau**t + + # Exponential terms + for n, d, t, c in zip(self.nr2, self.d2, self.t2, self.c2): + B += n*exp(-delta**c)*delta**(d-1)*tau**t*(d-c*delta**c) + C += n*exp(-delta**c)*(delta**(d-2)*tau**t*( + (d-c*delta**c)*(d-1-c*delta**c)-c**2*delta**c)) + + # Gaussian terms + for n, d, t, a, e, b, g in zip(self.nr3, self.d3, self.t3, self.alfa3, + self.epsilon3, self.beta3, self.gamma3): + B += n*delta**d*tau**t*exp(-a*(delta-e)**2-b*(tau-g)**2)*( + d/delta-2*a*(delta-e)) + C += n*tau**t*exp(-a*(delta-e)**2-b*(tau-g)**2)*( + -2*a*delta**d+4*a**2*delta**d*( + delta-e)**2-4*d*a*delta**2*( + delta-e)+d*2*delta) + + # Non analitic terms + for n, a, b, A, B_, C_, D, bt in zip(self.nr4, self.a4, self.b4, self.A, + self.B, self.C, self.D, self.beta4): + Tita = (1-tau)+A*((delta-1)**2)**(0.5/bt) + Delta = Tita**2+B_*((delta-1)**2)**a + Deltad = (delta-1)*(A*Tita*2/bt*((delta-1)**2)**( + 0.5/bt-1)+2*B_*a*((delta-1)**2)**(a-1)) + Deltadd = Deltad/(delta-1) + (delta-1)**2*( + 4*B_*a*(a-1)*((delta-1)**2)**(a-2) + + 2*A**2/bt**2*(((delta-1)**2)**(0.5/bt-1))**2 + + A*Tita*4/bt*(0.5/bt-1)*((delta-1)**2)**(0.5/bt-2)) + DeltaBd = b*Delta**(b-1)*Deltad + DeltaBdd = b*(Delta**(b-1)*Deltadd+(b-1)*Delta**(b-2)*Deltad**2) + F = exp(-C_*(delta-1)**2-D*(tau-1)**2) + Fd = -2*C_*F*(delta-1) + Fdd = 2*C_*F*(2*C_*(delta-1)**2-1) + + B += n*(Delta**b*(F+delta*Fd)+DeltaBd*delta*F) + C += n*(Delta**b*(2*Fd+delta*Fdd)+2*DeltaBd*(F+delta*Fd) + + DeltaBdd*delta*F) + + prop = {} + prop["B"] = B + prop["C"] = C + return prop diff --git a/iapws/humidAir.py b/iapws/humidAir.py index 70ff7c1..5b2c1c3 100644 --- a/iapws/humidAir.py +++ b/iapws/humidAir.py @@ -16,20 +16,22 @@ from __future__ import division from math import exp, log, pi, atan import warnings +from typing import Tuple, Dict, Optional, Any from scipy.optimize import fsolve -from ._iapws import M as Mw +from ._iapws import _global_M from ._iapws import _Ice -from ._utils import deriv_G +from ._utils import _fase, deriv_G from .iapws95 import MEoS, IAPWS95, mainClassDoc +from .helmholtz import ResidualContribution Ma = 28.96546 # g/mol R = 8.314472 # J/molK -def _virial(T): +def _virial(T: float) -> Dict[str, float]: """Virial equations for humid air Parameters @@ -105,7 +107,7 @@ def _virial(T): # The paper use the specific formulation of virial, here using the general # formulation from helmholtz mEoS wt = IAPWS95() - vir = wt._virial(T) + vir = wt.residual._virial(T, wt._constant_Tref) Bww = vir["B"]/wt.rhoc*wt.M Cwww = vir["C"]/wt.rhoc**2*wt.M**2 @@ -115,32 +117,32 @@ def _virial(T): ci = [66.5687, -238.834, -176.755] di = [-0.237, -1.048, -3.183] - Baw = 1e-6*sum([c*T_**d for c, d in zip(ci, di)]) # Eq 7 - Caaw = 1e-6*sum([a/T_**i for i, a in enumerate(ai)]) # Eq 8 - Caww = -1e-6*exp(sum([b/T_**i for i, b in enumerate(bi)])) # Eq 9 + Baw = 1e-6*sum(c*T_**d for c, d in zip(ci, di)) # Eq 7 + Caaw = 1e-6*sum(a/T_**i for i, a in enumerate(ai)) # Eq 8 + Caww = -1e-6*exp(sum(b/T_**i for i, b in enumerate(bi))) # Eq 9 # Eq T56 - Bawt = 1e-6*T_/T*sum([c*d*T_**(d-1) for c, d in zip(ci, di)]) + Bawt = 1e-6*T_/T*sum(c*d*T_**(d-1) for c, d in zip(ci, di)) # Eq T57 Bawtt = 1e-6*T_**2/T**2*sum( - [c*d*(d-1)*T_**(d-2) for c, d in zip(ci, di)]) + c*d*(d-1)*T_**(d-2) for c, d in zip(ci, di)) # Eq T59 - Caawt = -1e-6*T_/T*sum([i*a*T_**(-i-1) for i, a in enumerate(ai)]) + Caawt = -1e-6*T_/T*sum(i*a*T_**(-i-1) for i, a in enumerate(ai)) # Eq T60 Caawtt = 1e-6*T_**2/T**2*sum( - [i*(i+1)*a*T_**(-i-2) for i, a in enumerate(ai)]) + i*(i+1)*a*T_**(-i-2) for i, a in enumerate(ai)) # Eq T62 - Cawwt = 1e-6*T_/T*sum([i*b*T_**(-i-1) for i, b in enumerate(bi)]) * \ - exp(sum([b/T_**i for i, b in enumerate(bi)])) + Cawwt = 1e-6*T_/T*sum(i*b*T_**(-i-1) for i, b in enumerate(bi)) * \ + exp(sum(b/T_**i for i, b in enumerate(bi))) # Eq T63 Cawwtt = -1e-6*T_**2/T**2*(( - sum([i*(i+1)*b*T_**(-i-2) for i, b in enumerate(bi)]) + - sum([i*b*T_**(-i-1) for i, b in enumerate(bi)])**2) * - exp(sum([b/T_**i for i, b in enumerate(bi)]))) + sum(i*(i+1)*b*T_**(-i-2) for i, b in enumerate(bi)) + + sum(i*b*T_**(-i-1) for i, b in enumerate(bi))**2) + * exp(sum(b/T_**i for i, b in enumerate(bi)))) # Virial coefficient for air, using too the general virial procedure air = Air() - vir = air._virial(T) + vir = air.residual._virial(T, air._constant_Tref) Baa = vir["B"]/air.rhoc*air.M Caaa = vir["C"]/air.rhoc**2*air.M**2 @@ -161,7 +163,7 @@ def _virial(T): return prop -def _fugacity(T, P, x): +def _fugacity(T: float, P: float, x: float) -> float: """Fugacity equation for humid air Parameters @@ -222,10 +224,18 @@ def _fugacity(T, P, x): class MEoSBlend(MEoS): - """Special meos class to implement pseudocomponent blend and defining its - ancillary dew and bubble point""" + """ + Special meos class to implement pseudocomponent blend and defining its + ancillary dew and bubble point + """ + + _blend: Dict[str, Any] + + def __init__(self, **kwargs): + super().__init__(**kwargs) + @classmethod - def _dewP(cls, T): + def _dewP(cls, T: float) -> float: """Using ancillary equation return the pressure of dew point""" c = cls._blend["dew"] Tj = cls._blend["Tj"] @@ -239,7 +249,7 @@ def _dewP(cls, T): return P @classmethod - def _bubbleP(cls, T): + def _bubbleP(cls, T: float) -> float: """Using ancillary equation return the pressure of bubble point""" c = cls._blend["bubble"] Tj = cls._blend["Tj"] @@ -265,6 +275,7 @@ class Air(MEoSBlend): 2000 K at Pressures to 2000 MPa. J. Phys. Chem. Ref. Data 29, 331 (2000). http://dx.doi.org/10.1063/1.1285884 """ + name = "air" CASNumber = "1" formula = "N2+Ar+O2" @@ -278,35 +289,38 @@ class Air(MEoSBlend): f_acent = 0.0335 momentoDipolar = 0.0 - Fi0 = {"ao_log": [1, 2.490888032], - "pow": [-3, -2, -1, 0, 1, 1.5], + Fi0 = {"ao_log": [1.0, 2.490888032], + "pow": [-3.0, -2.0, -1.0, 0.0, 1.0, 1.5], "ao_pow": [0.6057194e-7, -0.210274769e-4, -0.158860716e-3, 9.7450251743948, 10.0986147428912, -0.19536342e-3], "ao_exp": [0.791309509, 0.212236768], "titao": [25.36365, 16.90741], "ao_exp2": [-0.197938904], "titao2": [87.31279], - "sum2": [2./3] + "sum2": [2.0/3.0], } - _constants = { - "R": 8.31451, - "Tref": 132.6312, "rhoref": 10.4477*Ma, - - "nr1": [0.118160747229, 0.713116392079, -0.161824192067e1, - 0.714140178971e-1, -0.865421396646e-1, 0.134211176704, - 0.112626704218e-1, -0.420533228842e-1, 0.349008431982e-1, - 0.164957183186e-3], - "d1": [1, 1, 1, 2, 3, 3, 4, 4, 4, 6], - "t1": [0, 0.33, 1.01, 0, 0, 0.15, 0, 0.2, 0.35, 1.35], - - "nr2": [-0.101365037912, -0.173813690970, -0.472103183731e-1, - -0.122523554253e-1, -0.146629609713, -0.316055879821e-1, - 0.233594806142e-3, 0.148287891978e-1, -0.938782884667e-2], - "d2": [1, 3, 5, 6, 1, 3, 11, 1, 3], - "t2": [1.6, 0.8, 0.95, 1.25, 3.6, 6, 3.25, 3.5, 15], - "c2": [1, 1, 1, 1, 2, 2, 2, 3, 3], - "gamma2": [1]*9} + _constant_R = 8.31451 + _constant_rhoc = 10.4477*Ma + _constant_Tref = 132.6312 + residual = ResidualContribution( + nr1=[0.118160747229, 0.713116392079, -0.161824192067e1, + 0.714140178971e-1, -0.865421396646e-1, 0.134211176704, + 0.112626704218e-1, -0.420533228842e-1, 0.349008431982e-1, + 0.164957183186e-3], + d1=[1, 1, 1, 2, 3, 3, 4, 4, 4, 6], + t1=[0, 0.33, 1.01, 0, 0, 0.15, 0, 0.2, 0.35, 1.35], + + nr2=[-0.101365037912, -0.173813690970, -0.472103183731e-1, + -0.122523554253e-1, -0.146629609713, -0.316055879821e-1, + 0.233594806142e-3, 0.148287891978e-1, -0.938782884667e-2], + d2=[1, 3, 5, 6, 1, 3, 11, 1, 3], + t2=[1.6, 0.8, 0.95, 1.25, 3.6, 6, 3.25, 3.5, 15], + c2=[1, 1, 1, 1, 2, 2, 2, 3, 3], + + nr3=[], d3=[], t3=[], alfa3=[], beta3=[], gamma3=[], epsilon3=[], + nr4=[], a4=[], b4=[], A=[], B=[], C=[], D=[], beta4=[], + ) _blend = { "Tj": 132.6312, "Pj": 3.78502, @@ -322,16 +336,21 @@ class Air(MEoSBlend): "exp1": [0, 0.178963e1, 0], "a2": [], "exp2": [], "a3": [], "exp3": []} _surf = {"sigma": [0.03046], "exp": [1.28]} - _rhoG = { - "eq": 3, - "ao": [-0.20466e1, -0.4752e1, -0.13259e2, -0.47652e2], - "exp": [0.41, 1, 2.8, 6.5]} - _Pv = { - "ao": [-0.1567266, -0.5539635e1, 0.7567212, -0.3514322e1], - "exp": [0.5, 1, 2.5, 4]} + _Pv_ao = [-0.1567266, -0.5539635e1, 0.7567212, -0.3514322e1] + _Pv_exp = [0.5, 1.0, 2.5, 4.0] + # No rhoL equation for humidAir because _Liquid_Density() is overridden instead. + _rhoL_eq = 0 + _rhoL_ao = [0.0] + _rhoL_exp = [0.0] + _rhoG_eq = 3 + _rhoG_ao = [-0.20466e1, -0.4752e1, -0.13259e2, -0.47652e2] + _rhoG_exp = [0.41, 1.0, 2.8, 6.5] + + def __init__(self, **kwargs): + super().__init__(**kwargs) @classmethod - def _Liquid_Density(cls, T): + def _Liquid_Density(cls, T: float) -> float: """Auxiliary equation for the density or saturated liquid Parameters @@ -344,20 +363,23 @@ def _Liquid_Density(cls, T): rho : float Saturated liquid density [kg/m³] """ + # Convert numpy.Float64 back into Python floats. + T = float(T) + Tc = 132.6312 rhoc = 10.4477*cls.M Ni = [44.3413, -240.073, 285.139, -88.3366] ti = [0.65, 0.85, 0.95, 1.1] Tita = 1-T/Tc - suma = 1 + suma = 1.0 for n, t in zip(Ni, ti): suma += n*Tita**t suma -= 0.892181*log(T/Tc) rho = suma*rhoc return rho - @staticmethod - def _visco(rho, T, fase=None): + # fase is unused + def _visco(self, rho: float, T: float, fase: Optional[_fase] = None) -> float: """Equation for the Viscosity Parameters @@ -387,7 +409,7 @@ def _visco(rho, T, fase=None): b = [0.431, -0.4623, 0.08406, 0.005341, -0.00331] T_ = log(T/ek) - suma = 0 + suma = 0.0 for i, bi in enumerate(b): suma += bi*T_**i omega = exp(suma) @@ -402,7 +424,7 @@ def _visco(rho, T, fase=None): g_poly = [0, 0, 0, 1, 1] # Eq 3 - mur = 0 + mur = 0.0 for n, t, d, l, g in zip(n_poly, t_poly, d_poly, l_poly, g_poly): mur += n*tau**t*delta**d*exp(-g*delta**l) @@ -410,7 +432,7 @@ def _visco(rho, T, fase=None): mu = muo+mur return mu*1e-6 - def _thermo(self, rho, T, fase=None): + def _thermo(self, rho: float, T: float, fase: Optional[_fase] = None) -> float: """Equation for the thermal conductivity Parameters @@ -442,7 +464,7 @@ def _thermo(self, rho, T, fase=None): b = [0.431, -0.4623, 0.08406, 0.005341, -0.00331] T_ = log(T/ek) - suma = 0 + suma = 0.0 for i, bi in enumerate(b): suma += bi*T_**i omega = exp(suma) @@ -452,8 +474,8 @@ def _thermo(self, rho, T, fase=None): # Eq 5 N = [1.308, 1.405, -1.036] - t = [-1.1, -0.3] - lo = N[0]*muo+N[1]*tau**t[0]+N[2]*tau**t[1] + te5 = [-1.1, -0.3] + lo = N[0]*muo+N[1]*tau**te5[0]+N[2]*tau**te5[1] n_poly = [8.743, 14.76, -16.62, 3.793, -6.142, -0.3778] t_poly = [0.1, 0, 0.5, 2.7, 0.3, 1.3] @@ -462,13 +484,15 @@ def _thermo(self, rho, T, fase=None): l_poly = [0, 0, 2, 2, 2, 2] # Eq 6 - lr = 0 + lr = 0.0 for n, t, d, l, g in zip(n_poly, t_poly, d_poly, l_poly, g_poly): lr += n*tau**t*delta**d*exp(-g*delta**l) - lc = 0 + lc = 0.0 # FIXME: Tiny desviation in the test in paper, 0.06% at critical point if fase: + assert(isinstance(fase.drhodP_T, float)) + qd = 0.31 Gamma = 0.055 Xio = 0.11 @@ -480,26 +504,30 @@ def _thermo(self, rho, T, fase=None): ref = Air() st = ref._Helmholtz(rho, Tref) - drho = 1e3/self.R/Tref/(1+2*delta*st["fird"]+delta**2*st["firdd"]) + drho = 1e3/self.R/Tref/(1+2*delta*st.fird+delta**2*st.firdd) Xref = self.Pc*rho/rhoc**2*drho # Eq 10 bracket = X-Xref*Tref/T if bracket > 0: + assert(isinstance(fase.cp, float)) + assert(isinstance(fase.cv, float)) + assert(isinstance(fase.mu, float)) + Xi = Xio*(bracket/Gamma)**(0.63/1.2415) Xq = Xi/qd # Eq 8 - Omega = 2/pi*((fase.cp-fase.cv)/fase.cp*atan(Xq) + - fase.cv/fase.cp*(Xq)) + Omega = 2/pi*((fase.cp-fase.cv)/fase.cp*atan(Xq) + + fase.cv/fase.cp*(Xq)) # Eq 9 Omega0 = 2/pi*(1-exp(-1/(1/Xq+Xq**2/3*rhoc**2/rho**2))) # Eq 7 lc = rho*fase.cp*k*1.01*T/6/pi/Xi/fase.mu*(Omega-Omega0)*1e15 else: - lc = 0 + lc = 0.0 # Eq 4 k = lo+lr+lc @@ -507,7 +535,7 @@ def _thermo(self, rho, T, fase=None): return k*1e-3 -class HumidAir(object): +class HumidAir(_fase): """ Humid air class with complete functionality @@ -566,6 +594,7 @@ class HumidAir(object): * HR: Humidity ratio, [-] * RH: Relative humidity, [-] """ + kwargs = {"T": 0.0, "P": 0.0, "rho": 0.0, @@ -579,10 +608,11 @@ class HumidAir(object): def __init__(self, **kwargs): """Constructor, define common constant and initinialice kwargs""" + super().__init__() self.kwargs = HumidAir.kwargs.copy() self.__call__(**kwargs) - def __call__(self, **kwargs): + def __call__(self, **kwargs) -> None: """Make instance callable to can add input parameter one to one""" # Check alernate input parameters if kwargs.get("v", 0): @@ -603,7 +633,7 @@ def __call__(self, **kwargs): self.msg = "" @property - def calculable(self): + def calculable(self) -> bool: """Check if inputs are enough to define state""" self._mode = "" if self.kwargs["T"] and self.kwargs["P"]: @@ -622,7 +652,7 @@ def calculable(self): return bool(self._mode) and bool(self._composition) - def calculo(self): + def calculo(self) -> None: """Calculate procedure""" T = self.kwargs["T"] rho = self.kwargs["rho"] @@ -633,20 +663,30 @@ def calculo(self): A = self.kwargs["A"] elif self._composition == "xa": xa = self.kwargs["xa"] - A = xa/(1-(1-xa)*(1-Mw/Ma)) + assert(isinstance(xa, float)) + A = xa/(1-(1-xa)*(1-_global_M/Ma)) + assert(isinstance(A, float)) # Thermodynamic definition if self._mode == "TP": - def f(rho): - fav = self._fav(T, rho, A) - return rho**2*fav["fird"]/1000-P - rho = fsolve(f, 1)[0] + def rho_func(rhopar: float) -> float: + assert(T is not None) + assert(P is not None) + assert(A is not None) + fav = self._fav(T, rhopar, A) + return rhopar**2*fav["fird"]/1000-P + rho = float(fsolve(rho_func, 1)[0]) elif self._mode == "Prho": - def f(T): - fav = self._fav(T, rho, A) + def t_func(Tpar: float) -> float: + assert(P is not None) + assert(rho is not None) + assert(A is not None) + fav = self._fav(Tpar, rho, A) return rho**2*fav["fird"]/1000-P - T = fsolve(f, 300)[0] + T = float(fsolve(t_func, 300)[0]) + assert(T is not None) + assert(isinstance(rho, float)) # General calculation procedure fav = self._fav(T, rho, A) @@ -681,15 +721,17 @@ def f(T): # Saturation related properties A_sat = self._eq(self.T, self.P) - self.xa_sat = A_sat*Mw/Ma/(1-A_sat*(1-Mw/Ma)) + self.xa_sat = A_sat*_global_M/Ma/(1-A_sat*(1-_global_M/Ma)) self.RH = (1-self.xa)/(1-self.xa_sat) - def derivative(self, z, x, y): - """Wrapper derivative for custom derived properties - where x, y, z can be: P, T, v, rho, u, h, s, g, a""" + def derivative(self, z: str, x: str, y: str) -> float: + """ + Wrapper derivative for custom derived properties + where x, y, z can be: P, T, v, rho, u, h, s, g, a + """ return deriv_G(self, z, x, y, self) - def _eq(self, T, P): + def _eq(self, T: float, P: float) -> float: """Procedure for calculate the composition in saturation state Parameters @@ -709,9 +751,11 @@ def _eq(self, T, P): gw = ice["g"] else: water = IAPWS95(T=T, P=P) + # water.g is actually a numpy.float64? + assert(water.g is not None) gw = water.g - def f(parr): + def f(parr: Tuple[float, float]) -> Tuple[float, float]: rho, a = parr if a > 1: a = 1 @@ -719,11 +763,10 @@ def f(parr): muw = fa["fir"]+rho*fa["fird"]-a*fa["fira"] return gw-muw, rho**2*fa["fird"]/1000-P - rinput = fsolve(f, [1, 0.95], full_output=True) - Asat = rinput[0][1] + Asat = float(fsolve(f, [1, 0.95], full_output=True)[0][1]) return Asat - def _prop(self, T, rho, fav): + def _prop(self, T: float, rho: float, fav: Dict[str, float]) -> Dict[str, float]: """Thermodynamic properties of humid air Parameters @@ -771,11 +814,11 @@ def _prop(self, T, rho, fav): prop["xkappa"] = 1e3/(rho**2*(2*fav["fird"]+rho*fav["firdd"])) # Eq T8 prop["ks"] = 1000*fav["firtt"]/rho**2/( # Eq T9 fav["firtt"]*(2*fav["fird"]+rho*fav["firdd"])-rho*fav["firdt"]**2) - prop["w"] = (rho**2*1000*(fav["firtt"]*fav["firdd"]-fav["firdt"]**2) / - fav["firtt"]+2*rho*fav["fird"]*1000)**0.5 # Eq T10 + prop["w"] = (rho**2*1000*(fav["firtt"]*fav["firdd"]-fav["firdt"]**2) + / fav["firtt"]+2*rho*fav["fird"]*1000)**0.5 # Eq T10 return prop - def _coligative(self, rho, A, fav): + def _coligative(self, rho: float, A: float, fav: Dict[str, float]) -> Dict[str, float]: """Miscelaneous properties of humid air Parameters @@ -806,6 +849,8 @@ def _coligative(self, rho, A, fav): Thermodynamic Properties of Seawater, Table 12, http://www.iapws.org/relguide/SeaAir.html """ + Mw = _global_M + prop = {} prop["mu"] = fav["fira"] prop["muw"] = fav["fir"]+rho*fav["fird"]-A*fav["fira"] @@ -815,7 +860,7 @@ def _coligative(self, rho, A, fav): prop["xw"] = 1-prop["xa"] return prop - def _fav(self, T, rho, A): + def _fav(self, T: float, rho: float, A: float) -> Dict[str, float]: r"""Specific Helmholtz energy of humid air and derivatives Parameters @@ -862,32 +907,30 @@ def _fav(self, T, rho, A): prop = {} # Eq T11 - prop["fir"] = (1-A)*fv["fir"] + A*fa["fir"] + fmix["fir"] + prop["fir"] = (1-A)*fv.fi + A*fa.fi + fmix["fir"] # Eq T12 - prop["fira"] = -fv["fir"]-rhov*fv["fird"]+fa["fir"] + \ - rhoa*fa["fird"]+fmix["fira"] + prop["fira"] = -fv.fi-rhov*fv.fid+fa.fi + rhoa*fa.fid+fmix["fira"] # Eq T13 - prop["firt"] = (1-A)*fv["firt"]+A*fa["firt"]+fmix["firt"] + prop["firt"] = (1-A)*fv.fit+A*fa.fit+fmix["firt"] # Eq T14 - prop["fird"] = (1-A)**2*fv["fird"]+A**2*fa["fird"]+fmix["fird"] + prop["fird"] = (1-A)**2*fv.fid+A**2*fa.fid+fmix["fird"] # Eq T15 - prop["firaa"] = rho*(2*fv["fird"]+rhov*fv["firdd"] + - 2*fa["fird"]+rhoa*fa["firdd"])+fmix["firaa"] + prop["firaa"] = rho*(2*fv.fid+rhov*fv.fidd + + 2*fa.fid+rhoa*fa.fidd)+fmix["firaa"] # Eq T16 - prop["firat"] = -fv["firt"]-rhov*fv["firdt"]+fa["firt"] + \ - rhoa*fa["firdt"]+fmix["firat"] + prop["firat"] = -fv.fit-rhov*fv.fidt+fa.fit + rhoa*fa.fidt+fmix["firat"] # Eq T17 - prop["firad"] = -(1-A)*(2*fv["fird"]+rhov*fv["firdd"]) + \ - A*(2*fa["fird"]+rhoa*fa["firdd"])+fmix["firad"] + prop["firad"] = -(1-A)*(2*fv.fid+rhov*fv.fidd) + \ + A*(2*fa.fid+rhoa*fa.fidd)+fmix["firad"] # Eq T18 - prop["firtt"] = (1-A)*fv["firtt"]+A*fa["firtt"]+fmix["firtt"] + prop["firtt"] = (1-A)*fv.fitt+A*fa.fitt+fmix["firtt"] # Eq T19 - prop["firdt"] = (1-A)**2*fv["firdt"]+A**2*fa["firdt"]+fmix["firdt"] + prop["firdt"] = (1-A)**2*fv.fidt+A**2*fa.fidt+fmix["firdt"] # Eq T20 - prop["firdd"] = (1-A)**3*fv["firdd"]+A**3*fa["firdd"]+fmix["firdd"] + prop["firdd"] = (1-A)**3*fv.fidd+A**3*fa.fidd+fmix["firdd"] return prop - def _fmix(self, T, rho, A): + def _fmix(self, T: float, rho: float, A: float) -> Dict[str, float]: r"""Specific Helmholtz energy of air-water interaction Parameters diff --git a/iapws/iapws08.py b/iapws/iapws08.py index bd00e47..9dc1088 100644 --- a/iapws/iapws08.py +++ b/iapws/iapws08.py @@ -21,13 +21,14 @@ from __future__ import division from math import exp, log import warnings +from typing import Dict, Tuple, Optional from scipy.optimize import fsolve from .iapws95 import IAPWS95 from .iapws97 import IAPWS97, _Region1, _Region2 from ._iapws import _ThCond, Tc, Pc, rhoc, _Ice, _Tension -from ._utils import deriv_G +from ._utils import _fase, deriv_G # Constants @@ -41,7 +42,7 @@ To = 273.15 -class SeaWater(object): +class SeaWater(_fase): """ Class to model seawater with standard IAPWS-08 @@ -161,6 +162,7 @@ class SeaWater(object): >>> salt.haline 0.7311487666026304 """ + kwargs = {"T": 0.0, "P": 0.0, "S": None, @@ -171,10 +173,27 @@ class SeaWater(object): def __init__(self, **kwargs): """Constructor, initinialice kwargs""" + super().__init__() + self.gs = float('nan') + self.gt = float('nan') + self.gp = float('nan') + self.gsp = float('nan') + self.gtt = float('nan') + self.gtp = float('nan') + self.gpp = float('nan') + + self.k: Optional[float] = None + self.sigma: Optional[float] = None + self.mu: Optional[float] = None + self.muw: Optional[float] = None + self.mus: Optional[float] = None + self.osm: Optional[float] = None + self.haline: Optional[float] = None + self.kwargs = SeaWater.kwargs.copy() self.__call__(**kwargs) - def __call__(self, **kwargs): + def __call__(self, **kwargs) -> None: """Make instance callable to can add input parameter one to one""" self.kwargs.update(kwargs) @@ -184,16 +203,19 @@ def __call__(self, **kwargs): self.calculo() self.msg = "" - def calculo(self): + def calculo(self) -> None: """Calculate procedure""" - T = self.kwargs["T"] - P = self.kwargs["P"] - S = self.kwargs["S"] + assert(self.kwargs["P"] is not None) + assert(self.kwargs["T"] is not None) + assert(self.kwargs["S"] is not None) + T = float(self.kwargs["T"]) + P = float(self.kwargs["P"]) + S = float(self.kwargs["S"]) self.m = S/(1-S)/Ms - if self.kwargs["fast"] and T <= 313.15: + if bool(self.kwargs["fast"]) and T <= 313.15: pw = self._waterSupp(T, P) - elif self.kwargs["IF97"]: + elif bool(self.kwargs["IF97"]): pw = self._waterIF97(T, P) else: pw = self._water(T, P) @@ -219,8 +241,8 @@ def calculo(self): self.xkappa = -prop["gpp"]/prop["gp"] self.ks = (prop["gtp"]**2-prop["gtt"]*prop["gpp"])/prop["gp"] / \ prop["gtt"] - self.w = prop["gp"]*(prop["gtt"]*1000/(prop["gtp"]**2 - - prop["gtt"]*1000*prop["gpp"]*1e-6))**0.5 + self.w = prop["gp"]*(prop["gtt"]*1000/( + prop["gtp"]**2 - prop["gtt"]*1000*prop["gpp"]*1e-6))**0.5 # Thermal conductivity calculation if "thcond" in pw: @@ -251,15 +273,25 @@ def calculo(self): self.osm = None self.haline = None - def derivative(self, z, x, y): - """Wrapper derivative for custom derived properties - where x, y, z can be: P, T, v, u, h, s, g, a""" + def derivative(self, z: str, x: str, y: str) -> float: + """ + Wrapper derivative for custom derived properties + where x, y, z can be: P, T, v, u, h, s, g, a + """ return deriv_G(self, z, x, y, self) @classmethod - def _water(cls, T, P): + def _water(cls, T: float, P: float) -> Dict[str, float]: """Get properties of pure water, Table4 pag 8""" water = IAPWS95(P=P, T=T) + assert(isinstance(water.h, float)) + assert(isinstance(water.s, float)) + assert(isinstance(water.rho, float)) + assert(isinstance(water.cp, float)) + assert(isinstance(water.betas, float)) + assert(isinstance(water.w, float)) + assert(isinstance(water.k, float)) + prop = {} prop["g"] = water.h-T*water.s prop["gt"] = -water.s @@ -273,7 +305,7 @@ def _water(cls, T, P): return prop @classmethod - def _waterIF97(cls, T, P): + def _waterIF97(cls, T: float, P: float) -> Dict[str, float]: water = IAPWS97(P=P, T=T) betas = water.derivative("T", "P", "s", water) prop = {} @@ -288,9 +320,11 @@ def _waterIF97(cls, T, P): return prop @classmethod - def _waterSupp(cls, T, P): - """Get properties of pure water using the supplementary release SR7-09, - Table4 pag 6""" + def _waterSupp(cls, T: float, P: float) -> Dict[str, float]: + """ + Get properties of pure water using the supplementary release SR7-09, + Table4 pag 6 + """ tau = (T-273.15)/40 pi = (P-0.101325)/100 @@ -313,7 +347,7 @@ def _waterSupp(cls, T, P): 0.635113936641785e2, -0.222897317140459e2, 0.817060541818112e1, 0.305081646487967e1, -0.963108119393062e1] - g, gt, gp, gtt, gtp, gpp = 0, 0, 0, 0, 0, 0 + g = gt = gp = gtt = gtp = gpp = 0.0 for j, k, gi in zip(J, K, G): g += gi*tau**j*pi**k if j >= 1: @@ -334,14 +368,13 @@ def _waterSupp(cls, T, P): prop["gtt"] = gtt/40**2*1e-3 prop["gtp"] = gtp/40/100*1e-6 prop["gpp"] = gpp/100**2*1e-6 - prop["gs"] = 0 - prop["gsp"] = 0 + prop["gs"] = 0.0 + prop["gsp"] = 0.0 return prop @classmethod - def _saline(cls, T, P, S): + def _saline(cls, T: float, P: float, S: float) -> Dict[str, float]: """Eq 4""" - # Check input in range of validity if T <= 261 or T > 353 or P <= 0 or P > 100 or S < 0 or S > 0.12: warnings.warn("Incoming out of bound") @@ -383,7 +416,7 @@ def _saline(cls, T, P, S): -0.111282734326413e2, -0.262480156590992e1, 0.704658803315449e1, -0.792001547211682e1] - g, gt, gp, gtt, gtp, gpp, gs, gsp = 0, 0, 0, 0, 0, 0, 0, 0 + g = gt = gp = gtt = gtp = gpp = gs = gsp = 0.0 # Calculate only for some salinity if S != 0: @@ -421,7 +454,7 @@ def _saline(cls, T, P, S): return prop -def _Tb(P, S): +def _Tb(P: float, S: float) -> float: """Procedure to calculate the boiling temperature of seawater Parameters @@ -441,21 +474,21 @@ def _Tb(P, S): IAPWS, Advisory Note No. 5: Industrial Calculation of the Thermodynamic Properties of Seawater, http://www.iapws.org/relguide/Advise5.html, Eq 7 """ - def f(T): + def f(T: float) -> float: pw = _Region1(T, P) - gw = pw["h"]-T*pw["s"] + gw = pw.h-T*pw.s pv = _Region2(T, P) - gv = pv["h"]-T*pv["s"] + gv = pv.h-T*pv.s ps = SeaWater._saline(T, P, S) return -ps["g"]+S*ps["gs"]-gw+gv Tb = fsolve(f, 300)[0] - return Tb + return float(Tb) -def _Tf(P, S): +def _Tf(P: float, S: float) -> float: """Procedure to calculate the freezing temperature of seawater Parameters @@ -475,10 +508,10 @@ def _Tf(P, S): IAPWS, Advisory Note No. 5: Industrial Calculation of the Thermodynamic Properties of Seawater, http://www.iapws.org/relguide/Advise5.html, Eq 12 """ - def f(T): + def f(T: float) -> float: T = float(T) pw = _Region1(T, P) - gw = pw["h"]-T*pw["s"] + gw = pw.h-T*pw.s gih = _Ice(T, P)["g"] @@ -486,10 +519,10 @@ def f(T): return -ps["g"]+S*ps["gs"]-gw+gih Tf = fsolve(f, 300)[0] - return Tf + return float(Tf) -def _Triple(S): +def _Triple(S: float) -> Dict[str, float]: """Procedure to calculate the triple point pressure and temperature for seawater @@ -511,13 +544,13 @@ def _Triple(S): IAPWS, Advisory Note No. 5: Industrial Calculation of the Thermodynamic Properties of Seawater, http://www.iapws.org/relguide/Advise5.html, Eq 7 """ - def f(parr): + def f(parr: Tuple[float, float]) -> Tuple[float, float]: T, P = parr pw = _Region1(T, P) - gw = pw["h"]-T*pw["s"] + gw = pw.h-T*pw.s pv = _Region2(T, P) - gv = pv["h"]-T*pv["s"] + gv = pv.h-T*pv.s gih = _Ice(T, P)["g"] ps = SeaWater._saline(T, P, S) @@ -527,12 +560,12 @@ def f(parr): Tt, Pt = fsolve(f, [273, 6e-4]) prop = {} - prop["Tt"] = Tt - prop["Pt"] = Pt + prop["Tt"] = float(Tt) + prop["Pt"] = float(Pt) return prop -def _OsmoticPressure(T, P, S): +def _OsmoticPressure(T: float, P: float, S: float) -> float: """Procedure to calculate the osmotic pressure of seawater Parameters @@ -555,19 +588,19 @@ def _OsmoticPressure(T, P, S): Properties of Seawater, http://www.iapws.org/relguide/Advise5.html, Eq 15 """ pw = _Region1(T, P) - gw = pw["h"]-T*pw["s"] + gw = pw.h-T*pw.s - def f(Posm): + def f(Posm: float) -> float: pw2 = _Region1(T, P+Posm) - gw2 = pw2["h"]-T*pw2["s"] + gw2 = pw2.h-T*pw2.s ps = SeaWater._saline(T, P+Posm, S) return -ps["g"]+S*ps["gs"]-gw+gw2 Posm = fsolve(f, 0)[0] - return Posm + return float(Posm) -def _ThCond_SeaWater(T, P, S): +def _ThCond_SeaWater(T: float, P: float, S: float) -> float: """Equation for the thermal conductivity of seawater Parameters @@ -621,7 +654,8 @@ def _ThCond_SeaWater(T, P, S): DL = a*(1000*S)**(1+b) return DL -def _Tension_SeaWater(T, S): + +def _Tension_SeaWater(T: float, S: float) -> float: """Equation for the surface tension of seawater Parameters @@ -653,7 +687,6 @@ def _Tension_SeaWater(T, S): IAPWS, Guideline on the Surface Tension of Seawater, http://www.iapws.org/relguide/Seawater-Surf.html """ - # Check input parameters if 248.15 < T < 274.15: if S < 0 or S > 0.038: @@ -662,13 +695,14 @@ def _Tension_SeaWater(T, S): if S < 0 or S > 0.131: raise NotImplementedError("Incoming out of bound") else: - raise NotImplementedError("Incoming out of bound") + raise NotImplementedError("Incoming out of bound") sw = _Tension(T) sigma = sw*(1+3.766e-1*S+2.347e-3*S*(T-273.15)) return sigma -def _solNa2SO4(T, mH2SO4, mNaCl): + +def _solNa2SO4(T: float, mH2SO4: float, mNaCl: float) -> float: """Equation for the solubility of sodium sulfate in aqueous mixtures of sodium chloride and sulfuric acid @@ -725,7 +759,7 @@ def _solNa2SO4(T, mH2SO4, mNaCl): return S -def _critNaCl(x): +def _critNaCl(x: float) -> Dict[str, float]: """Equation for the critical locus of aqueous solutions of sodium chloride Parameters @@ -763,8 +797,8 @@ def _critNaCl(x): raise NotImplementedError("Incoming out of bound") T1 = Tc*(1 + 2.3e1*x - 3.3e2*x**1.5 - 1.8e3*x**2) - T2 = Tc*(1 + 1.757e1*x - 3.026e2*x**1.5 + 2.838e3*x**2 - 1.349e4*x**2.5 + - 3.278e4*x**3 - 3.674e4*x**3.5 + 1.437e4*x**4) + T2 = Tc*(1 + 1.757e1*x - 3.026e2*x**1.5 + 2.838e3*x**2 - 1.349e4*x**2.5 + + 3.278e4*x**3 - 3.674e4*x**3.5 + 1.437e4*x**4) f1 = (abs(10000*x-10-1)-abs(10000*x-10+1))/4+0.5 f2 = (abs(10000*x-10+1)-abs(10000*x-10-1))/4+0.5 @@ -772,9 +806,9 @@ def _critNaCl(x): tc = f1*T1+f2*T2 # Eq 7 - rc = rhoc*(1 + 1.7607e2*x - 2.9693e3*x**1.5 + 2.4886e4*x**2 - - 1.1377e5*x**2.5 + 2.8847e5*x**3 - 3.8195e5*x**3.5 + - 2.0633e5*x**4) + rc = rhoc*(1 + 1.7607e2*x - 2.9693e3*x**1.5 + 2.4886e4*x**2 + - 1.1377e5*x**2.5 + 2.8847e5*x**3 - 3.8195e5*x**3.5 + + 2.0633e5*x**4) # Eq 8 DT = tc-Tc diff --git a/iapws/iapws95.py b/iapws/iapws95.py index 795c37b..3b6fc56 100644 --- a/iapws/iapws95.py +++ b/iapws/iapws95.py @@ -9,247 +9,40 @@ * :class:`D2O`: 2017 formulation for heavy water. """ - from __future__ import division import os import platform import warnings +from math import exp, log +from typing import Tuple, Dict, Optional, List, Callable, Any -from scipy import exp, log, ndarray from scipy.optimize import fsolve from .iapws97 import _TSat_P, IAPWS97 -from ._iapws import M, Tc, Pc, rhoc, Tc_D2O, Pc_D2O, rhoc_D2O +from ._iapws import _global_M, Tc, Pc, rhoc, Tc_D2O, Pc_D2O, rhoc_D2O from ._iapws import _Viscosity, _ThCond, _Dielectric, _Refractive, _Tension from ._iapws import _D2O_Viscosity, _D2O_ThCond, _D2O_Tension from ._utils import _fase, getphase, deriv_H +from .helmholtz import ResidualContribution, HelmholtzDerivatives -def _phir(tau, delta, coef): - """Residual contribution to the adimensional free Helmholtz energy - - Parameters - ---------- - tau : float - Inverse reduced temperature Tc/T, [-] - delta : float - Reduced density rho/rhoc, [-] - coef : dict - Dictionary with equation of state parameters - - Returns - ------- - fir : float - Adimensional free Helmholtz energy - - References - ---------- - IAPWS, Revised Release on the IAPWS Formulation 1995 for the - Thermodynamic Properties of Ordinary Water Substance for General and - Scientific Use, September 2016, Table 5 - http://www.iapws.org/relguide/IAPWS-95.html - """ - fir = 0 - - # Polinomial terms - nr1 = coef.get("nr1", []) - d1 = coef.get("d1", []) - t1 = coef.get("t1", []) - for n, d, t in zip(nr1, d1, t1): - fir += n*delta**d*tau**t - - # Exponential terms - nr2 = coef.get("nr2", []) - d2 = coef.get("d2", []) - g2 = coef.get("gamma2", []) - t2 = coef.get("t2", []) - c2 = coef.get("c2", []) - for n, d, g, t, c in zip(nr2, d2, g2, t2, c2): - fir += n*delta**d*tau**t*exp(-g*delta**c) - - # Gaussian terms - nr3 = coef.get("nr3", []) - d3 = coef.get("d3", []) - t3 = coef.get("t3", []) - a3 = coef.get("alfa3", []) - e3 = coef.get("epsilon3", []) - b3 = coef.get("beta3", []) - g3 = coef.get("gamma3", []) - for n, d, t, a, e, b, g in zip(nr3, d3, t3, a3, e3, b3, g3): - fir += n*delta**d*tau**t*exp(-a*(delta-e)**2-b*(tau-g)**2) - - # Non analitic terms - nr4 = coef.get("nr4", []) - a4 = coef.get("a4", []) - b4 = coef.get("b4", []) - Ai = coef.get("A", []) - Bi = coef.get("B", []) - Ci = coef.get("C", []) - Di = coef.get("D", []) - bt4 = coef.get("beta4", []) - for n, a, b, A, B, C, D, bt in zip(nr4, a4, b4, Ai, Bi, Ci, Di, bt4): - Tita = (1-tau)+A*((delta-1)**2)**(0.5/bt) - F = exp(-C*(delta-1)**2-D*(tau-1)**2) - Delta = Tita**2+B*((delta-1)**2)**a - fir += n*Delta**b*delta*F - - return fir - - -def _phird(tau, delta, coef): - r"""Residual contribution to the adimensional free Helmholtz energy, delta - derivative - - Parameters - ---------- - tau : float - Inverse reduced temperature Tc/T, [-] - delta : float - Reduced density rho/rhoc, [-] - coef : dict - Dictionary with equation of state parameters - - Returns - ------- - fird : float - .. math:: - \left.\frac{\partial \phi^r_{\delta}}{\partial \delta}\right|_{\tau} - - References - ---------- - IAPWS, Revised Release on the IAPWS Formulation 1995 for the - Thermodynamic Properties of Ordinary Water Substance for General and - Scientific Use, September 2016, Table 5 - http://www.iapws.org/relguide/IAPWS-95.html - """ - fird = 0 - - # Polinomial terms - nr1 = coef.get("nr1", []) - d1 = coef.get("d1", []) - t1 = coef.get("t1", []) - for n, d, t in zip(nr1, d1, t1): - fird += n*d*delta**(d-1)*tau**t - - # Exponential terms - nr2 = coef.get("nr2", []) - d2 = coef.get("d2", []) - g2 = coef.get("gamma2", []) - t2 = coef.get("t2", []) - c2 = coef.get("c2", []) - for n, d, g, t, c in zip(nr2, d2, g2, t2, c2): - fird += n*exp(-g*delta**c)*delta**(d-1)*tau**t*(d-g*c*delta**c) - - # Gaussian terms - nr3 = coef.get("nr3", []) - d3 = coef.get("d3", []) - t3 = coef.get("t3", []) - a3 = coef.get("alfa3", []) - e3 = coef.get("epsilon3", []) - b3 = coef.get("beta3", []) - g3 = coef.get("gamma3", []) - for n, d, t, a, e, b, g in zip(nr3, d3, t3, a3, e3, b3, g3): - fird += n*delta**d*tau**t*exp(-a*(delta-e)**2-b*(tau-g)**2)*( - d/delta-2*a*(delta-e)) - - # Non analitic terms - nr4 = coef.get("nr4", []) - a4 = coef.get("a4", []) - b4 = coef.get("b4", []) - Ai = coef.get("A", []) - Bi = coef.get("B", []) - Ci = coef.get("C", []) - Di = coef.get("D", []) - bt4 = coef.get("beta4", []) - for n, a, b, A, B, C, D, bt in zip(nr4, a4, b4, Ai, Bi, Ci, Di, bt4): - Tita = (1-tau)+A*((delta-1)**2)**(0.5/bt) - F = exp(-C*(delta-1)**2-D*(tau-1)**2) - Fd = -2*C*F*(delta-1) - - Delta = Tita**2+B*((delta-1)**2)**a - Deltad = (delta-1)*(A*Tita*2/bt*((delta-1)**2)**(0.5/bt-1) + - 2*B*a*((delta-1)**2)**(a-1)) - DeltaBd = b*Delta**(b-1)*Deltad - - fird += n*(Delta**b*(F+delta*Fd)+DeltaBd*delta*F) - - return fird - - -def _phirt(tau, delta, coef): - r"""Residual contribution to the adimensional free Helmholtz energy, tau - derivative - - Parameters - ---------- - tau : float - Inverse reduced temperature Tc/T, [-] - delta : float - Reduced density rho/rhoc, [-] - coef : dict - Dictionary with equation of state parameters - - Returns - ------- - firt : float - .. math:: - \left.\frac{\partial \phi^r_{\tau}}{\partial \tau}\right|_{\delta} +class MEoSProperties(object): + """The properties required to fill a MEoS phase.""" - References - ---------- - IAPWS, Revised Release on the IAPWS Formulation 1995 for the - Thermodynamic Properties of Ordinary Water Substance for General and - Scientific Use, September 2016, Table 5 - http://www.iapws.org/relguide/IAPWS-95.html - """ - firt = 0 - - # Polinomial terms - nr1 = coef.get("nr1", []) - d1 = coef.get("d1", []) - t1 = coef.get("t1", []) - for n, d, t in zip(nr1, d1, t1): - firt += n*t*delta**d*tau**(t-1) - - # Exponential terms - nr2 = coef.get("nr2", []) - d2 = coef.get("d2", []) - g2 = coef.get("gamma2", []) - t2 = coef.get("t2", []) - c2 = coef.get("c2", []) - for n, d, g, t, c in zip(nr2, d2, g2, t2, c2): - firt += n*t*delta**d*tau**(t-1)*exp(-g*delta**c) - - # Gaussian terms - nr3 = coef.get("nr3", []) - d3 = coef.get("d3", []) - t3 = coef.get("t3", []) - a3 = coef.get("alfa3", []) - e3 = coef.get("epsilon3", []) - b3 = coef.get("beta3", []) - g3 = coef.get("gamma3", []) - for n, d, t, a, e, b, g in zip(nr3, d3, t3, a3, e3, b3, g3): - firt += n*delta**d*tau**t*exp(-a*(delta-e)**2-b*(tau-g)**2)*( - t/tau-2*b*(tau-g)) - - # Non analitic terms - nr4 = coef.get("nr4", []) - a4 = coef.get("a4", []) - b4 = coef.get("b4", []) - Ai = coef.get("A", []) - Bi = coef.get("B", []) - Ci = coef.get("C", []) - Di = coef.get("D", []) - bt4 = coef.get("beta4", []) - for n, a, b, A, B, C, D, bt in zip(nr4, a4, b4, Ai, Bi, Ci, Di, bt4): - Tita = (1-tau)+A*((delta-1)**2)**(0.5/bt) - F = exp(-C*(delta-1)**2-D*(tau-1)**2) - Ft = -2*D*F*(tau-1) - Delta = Tita**2+B*((delta-1)**2)**a - DeltaBt = -2*Tita*b*Delta**(b-1) - firt += n*delta*(DeltaBt*F+Delta**b*Ft) - - return firt + def __init__(self, rho: float, P: float, h: float, s: float, cv: + float, alfap: float, betap: float, fir: float, fird: + float, firdd: float, delta: float) -> None: + self.rho = rho + self.P = P + self.h = h + self.s = s + self.cv = cv + self.alfap = alfap + self.betap = betap + self.fir = fir + self.fird = fird + self.firdd = firdd + self.delta = delta class MEoS(_fase): @@ -368,15 +161,30 @@ class MEoS(_fase): * invT: Negative reciprocal temperature, [1/K] * hInput: Specific heat input, [kJ/kg] """ + CP = None - _Pv = None - _rhoL = None - _rhoG = None - - kwargs = {"T": 0.0, - "P": 0.0, - "rho": 0.0, - "v": 0.0, + # The lists vary from 4 to 6 terms, which is why they're not + # tuples of explicit length. The equation numbers are used for + # switching implementations based on class. + _Pv_ao: List[float] + _Pv_exp: List[float] + _rhoG_eq: int + _rhoG_ao: List[float] + _rhoG_exp: List[float] + _rhoL_eq: int + _rhoL_ao: List[float] + _rhoL_exp: List[float] + # Defined in derived classes NH3 and Air. + _surf: Dict[str, List[float]] + Fi0: Dict[str, List[float]] + # Defined by derived classes + _constant_rhoc: float + _constant_Tref: float + + kwargs = {"T": None, + "P": None, + "rho": None, + "v": None, "h": None, "s": None, "u": None, @@ -388,14 +196,47 @@ class MEoS(_fase): status = 0 msg = "Undefined" + # These are used in MeOS but are NOT actually defined by the + # class. They have to be defined by one of the subclasses which + # is a little messy. By statically typing them here, we at least + # let mypy know that they're supposed to be set... + rhoc: float + Tc: float + Pc: float + Tt: float + _constant_R: float + _constants: Dict[str, List[float]] + residual: ResidualContribution + M: float + # The name of the substance. + # Set in NH3, Air, IAPWS95, D2O, and IAPWS97. + name: str + def __init__(self, **kwargs): """Constructor, define common constant and initinialice kwargs""" - self.R = self._constants["R"]/self._constants.get("M", self.M) - self.Zc = self.Pc/self.rhoc/self.R/self.Tc + super().__init__() + + # These class variables must be defined by the subclass. + assert(isinstance(self._Pv_ao, list)) + assert(isinstance(self._Pv_exp, list)) + assert(isinstance(self._rhoL_eq, int)) + assert(isinstance(self._rhoL_ao, list)) + assert(isinstance(self._rhoL_exp, list)) + assert(isinstance(self._rhoG_eq, int)) + assert(isinstance(self._rhoG_ao, list)) + assert(isinstance(self._rhoG_exp, list)) + assert(isinstance(self.M, float)) + + self.sigma: Optional[float] = None + self.Hvap: Optional[float] = None + self.Svap: Optional[float] = None + + self.R = self.__class__._constant_R/self.M + self.Zc = self.Pc/self._constant_rhoc/self.R/self._constant_Tref self.kwargs = MEoS.kwargs.copy() self.__call__(**kwargs) - def __call__(self, **kwargs): + def __call__(self, **kwargs) -> None: """Make instance callable to can add input parameter one to one""" # Alternative rho input if "rhom" in kwargs: @@ -409,84 +250,44 @@ def __call__(self, **kwargs): del kwargs["vm"] self.kwargs.update(kwargs) - if self.calculable: - try: - self.status = 1 - self.calculo() - self.msg = "" - except RuntimeError as err: - self.status = 0 - self.msg = err.args[0] - raise(err) - - # Add msg for extrapolation state - if self.name == "water" and 130 <= self.T < 273.15: - self.msg = "Extrapolated state" - self.status = 3 - warnings.warn("Using extrapolated values") - elif self.name == "water" and 50 <= self.T < 130: - self.msg = "Extrapolated state using Low-Temperature extension" - self.status = 3 - warnings.warn("Using extrapolated values and Low-Temperature" - "extension") + try: + self.calculo() + self.msg = "" + if self.status == 0: + return + except RuntimeError as err: + self.status = 0 + self.msg = err.args[0] + raise(err) + + # Add msg for extrapolation state + if self.name == "water" and 130 <= self.T < 273.15: + self.msg = "Extrapolated state" + self.status = 3 + warnings.warn("Using extrapolated values") + elif self.name == "water" and 50 <= self.T < 130: + self.msg = "Extrapolated state using Low-Temperature extension" + self.status = 3 + warnings.warn("Using extrapolated values and Low-Temperature extension") @property - def calculable(self): + def calculable(self) -> bool: """Check if inputs are enough to define state""" - self._mode = "" - if self.kwargs["T"] and self.kwargs["P"]: - self._mode = "TP" - elif self.kwargs["T"] and self.kwargs["rho"]: - self._mode = "Trho" - elif self.kwargs["T"] and self.kwargs["h"] is not None: - self._mode = "Th" - elif self.kwargs["T"] and self.kwargs["s"] is not None: - self._mode = "Ts" - elif self.kwargs["T"] and self.kwargs["u"] is not None: - self._mode = "Tu" - elif self.kwargs["P"] and self.kwargs["rho"]: - self._mode = "Prho" - elif self.kwargs["P"] and self.kwargs["h"] is not None: - self._mode = "Ph" - elif self.kwargs["P"] and self.kwargs["s"] is not None: - self._mode = "Ps" - elif self.kwargs["P"] and self.kwargs["u"] is not None: - self._mode = "Pu" - elif self.kwargs["rho"] and self.kwargs["h"] is not None: - self._mode = "rhoh" - elif self.kwargs["rho"] and self.kwargs["s"] is not None: - self._mode = "rhos" - elif self.kwargs["rho"] and self.kwargs["u"] is not None: - self._mode = "rhou" - elif self.kwargs["h"] is not None and self.kwargs["s"] is not None: - self._mode = "hs" - elif self.kwargs["h"] is not None and self.kwargs["u"] is not None: - self._mode = "hu" - elif self.kwargs["s"] is not None and self.kwargs["u"] is not None: - self._mode = "su" - elif self.kwargs["T"] and self.kwargs["x"] is not None: - self._mode = "Tx" - elif self.kwargs["P"] and self.kwargs["x"] is not None: - self._mode = "Px" return bool(self._mode) - def calculo(self): - """Calculate procedure""" - T = self.kwargs["T"] - rho = self.kwargs["rho"] - P = self.kwargs["P"] - s = self.kwargs["s"] - h = self.kwargs["h"] - u = self.kwargs["u"] - x = self.kwargs["x"] - + def get_To_rhoo_x0(self, T0: Optional[float], + rho0: Optional[float]) -> Tuple[float, float, Optional[float]]: + """Compute three values from kwargs.""" # Initial values T0 = self.kwargs["T0"] rho0 = self.kwargs["rho0"] + x0: Optional[float] = None if T0 or rho0: - To = T0 - rhoo = rho0 + if T0 is not None: + To = float(T0) + if rho0 is not None: + rhoo = float(rho0) elif self.name == "air": To = 300 rhoo = 1e-3 @@ -500,908 +301,1070 @@ def calculo(self): if st0.status: To = st0.T rhoo = st0.rho + x0 = st0.x else: To = 300 rhoo = 900 + return(To, rhoo, x0) - self.R = self._constants["R"]/self._constants.get("M", self.M) - rhoc = self._constants.get("rhoref", self.rhoc) - Tc = self._constants.get("Tref", self.Tc) + def calculo(self) -> None: + """Calculate procedure""" + T = self.kwargs["T"] + rho = self.kwargs["rho"] + P = self.kwargs["P"] + s = self.kwargs["s"] + h = self.kwargs["h"] + u = self.kwargs["u"] + x = self.kwargs["x"] + T0 = self.kwargs["T0"] + rho0 = self.kwargs["rho0"] - propiedades = None + To, rhoo, x0 = self.get_To_rhoo_x0(T0, rho0) - if self._mode not in ("Tx", "Px"): - # Method with iteration necessary to get x - if self._mode == "TP": - try: - if self.name != "water": - raise NotImplementedError - st0 = IAPWS97(**self.kwargs) - rhoo = st0.rho - except NotImplementedError: - if rho0: - rhoo = rho0 - elif T < self.Tc and P < self.Pc and \ - self._Vapor_Pressure(T) < P: - rhoo = self._Liquid_Density(T) - elif T < self.Tc and P < self.Pc: - rhoo = self._Vapor_Density(T) - else: - rhoo = self.rhoc*3 - - def f(rho): - delta = rho/rhoc - tau = Tc/T - - fird = _phird(tau, delta, self._constants) - Po = (1+delta*fird)*self.R*T*rho - return Po-P*1000 - - rho = fsolve(f, rhoo)[0] - - # Calculate quality - if T > self.Tc: - x = 1 - else: - Ps = self._Vapor_Pressure(T) - if Ps*0.95 < P < Ps*1.05: - rhol, rhov, Ps = self._saturation(T) - Ps *= 1e-3 - - if Ps > P: - x = 1 - else: - x = 0 - - elif self._mode == "Th": - tau = Tc/T - ideal = self._phi0(tau, 1) - fiot = ideal["fiot"] - - def f(rho): - delta = rho/rhoc - fird = _phird(tau, delta, self._constants) - firt = _phirt(tau, delta, self._constants) - ho = self.R*T*(1+tau*(fiot+firt)+delta*fird) - return ho-h - - if T >= self.Tc: - rhoo = self.rhoc - rho = fsolve(f, rhoo)[0] - else: - x0 = self.kwargs["x0"] - rhov = self._Vapor_Density(T) - rhol = self._Liquid_Density(T) - deltaL = rhol/rhoc - deltaG = rhov/rhoc - - firdL = _phird(tau, deltaL, self._constants) - firtL = _phirt(tau, deltaL, self._constants) - firdG = _phird(tau, deltaG, self._constants) - firtG = _phirt(tau, deltaG, self._constants) - hl = self.R*T*(1+tau*(fiot+firtL)+deltaL*firdL) - hv = self.R*T*(1+tau*(fiot+firtG)+deltaG*firdG) - if x0 not in (0, 1) and hl <= h <= hv: - rhol, rhov, Ps = self._saturation(T) - vapor = self._Helmholtz(rhov, T) - liquido = self._Helmholtz(rhol, T) - hv = vapor["h"] - hl = liquido["h"] - x = (h-hl)/(hv-hl) - rho = 1/(x/rhov+(1-x)/rhol) - P = Ps/1000 - else: - if h > hv: - rhoo = rhov - else: - rhoo = rhol - rho = fsolve(f, rhoo)[0] - - elif self._mode == "Ts": - tau = Tc/T - - def f(rho): - if rho < 0: - rho = 1e-20 - delta = rho/rhoc - - ideal = self._phi0(tau, delta) - fio = ideal["fio"] - fiot = ideal["fiot"] - fir = _phir(tau, delta, self._constants) - firt = _phirt(tau, delta, self._constants) - so = self.R*(tau*(fiot+firt)-fio-fir) - return so-s - - if T >= self.Tc: - rhoo = self.rhoc - rho = fsolve(f, rhoo)[0] + self._mode = "" + # Method with iteration necessary to get x + if T is not None and P is not None: + self._mode = "Trho" + self.solve_T_P(T, P, rho0, rhoo) + elif T is not None and rho is not None: + self._mode = "Trho" + self.solve_T_rho(T, rho) + elif T is not None and h is not None: + self._mode = "Th" + self.solve_T_h(T, h) + elif T is not None and s is not None: + self._mode = "Ts" + self.solve_T_s(T, s) + elif T is not None and u is not None: + self._mode = "Tu" + self.solve_T_u(T, u) + elif P is not None and rho is not None: + self._mode = "Prho" + self.solve_P_rho(P, rho, To) + elif P is not None and h is not None: + self._mode = "Ph" + self.solve_P_h(P, h, rhoo, To) + elif P is not None and s is not None: + self._mode = "Ps" + self.solve_P_s(P, s, rhoo, To, x0) + elif P is not None and u is not None: + self._mode = "Pu" + self.solve_P_u(P, u, rhoo, To) + elif rho is not None and h is not None: + self._mode = "rhoh" + self.solve_rho_h(rho, h, To) + elif rho is not None and s is not None: + self._mode = "rhos" + self.solve_rho_s(rho, s, To) + elif rho is not None and u is not None: + self._mode = "rhou" + self.solve_rho_u(rho, u, rhoo, To) + elif h is not None and s is not None: + self._mode = "hs" + self.solve_h_s(h, s, rhoo, To) + elif h is not None and u is not None: + self._mode = "hu" + self.solve_h_u(h, u, rhoo, To) + elif s is not None and u is not None: + self._mode = "su" + self.solve_s_u(s, u, rhoo, To) + elif T is not None and x is not None: + self._mode = "Tx" + self.solve_T_x(T, x) + elif P is not None and x is not None: + self._mode = "Px" + self.solve_P_x(P, x, T0) + # if self._mode is unset, calculable() will return false. + + def solve_T_P(self, T: float, P: float, + rho0: Optional[float], rhoo: float) -> None: + """Solve for properties using T & P.""" + try: + if self.name != "water": + raise NotImplementedError + st0 = IAPWS97(**self.kwargs) + rhoo = st0.rho + except NotImplementedError: + if rho0: + rhoo = rho0 + elif T < self._constant_Tref and P < self.Pc and \ + self._Vapor_Pressure(T) < P: + rhoo = self._Liquid_Density(T) + elif T < self._constant_Tref and P < self.Pc: + rhoo = self._Vapor_Density(T) + else: + rhoo = self._constant_rhoc*3 + + def rho_func(rho: float) -> float: + delta = rho/self._constant_rhoc + tau = self._constant_Tref/T + + fird = self.residual.phird(tau, delta) + Po = (1+delta*fird)*self.R*T*rho + return Po-P*1000 + + rho = float(fsolve(rho_func, rhoo)[0]) + + # Calculate quality + if T > self._constant_Tref: + x = 1.0 + else: + Ps = self._Vapor_Pressure(T) + if Ps*0.95 < P < Ps*1.05: + rhol, rhov, Ps = self._saturation(T) + Ps *= 1e-3 + + if Ps > P: + x = 1.0 + else: + x = 0.0 + + self.solve_case1(T, P, x, rho, None, None) + + def solve_T_h(self, T: float, h: float) -> None: + """Solve for properties using T & h.""" + tau = self._constant_Tref/T + ideal = self._phi0(tau, 1) + fiot = ideal.fit + x = None + P = 0.0 + + def rho_func(rho: float) -> float: + delta = rho/self._constant_rhoc + fird = self.residual.phird(tau, delta) + firt = self.residual.phirt(tau, delta) + ho = self.R*T*(1+tau*(fiot+firt)+delta*fird) + return ho-h + + liquido = None + vapor = None + if T >= self._constant_Tref: + rhoo = self._constant_rhoc + rho = float(fsolve(rho_func, rhoo)[0]) + else: + x0 = self.kwargs["x0"] + rhov = self._Vapor_Density(T) + rhol = self._Liquid_Density(T) + deltaL = rhol/self._constant_rhoc + deltaG = rhov/self._constant_rhoc + + firdL = self.residual.phird(tau, deltaL) + firtL = self.residual.phirt(tau, deltaL) + firdG = self.residual.phird(tau, deltaG) + firtG = self.residual.phirt(tau, deltaG) + hl = self.R*T*(1+tau*(fiot+firtL)+deltaL*firdL) + hv = self.R*T*(1+tau*(fiot+firtG)+deltaG*firdG) + if x0 not in (0, 1) and hl <= h <= hv: + rhol, rhov, Ps = self._saturation(T) + vapor = self._Helmholtz(rhov, T) + liquido = self._Helmholtz(rhol, T) + hv = vapor.h + hl = liquido.h + x = (h-hl)/(hv-hl) + rho = 1/(x/rhov+(1-x)/rhol) + P = Ps/1000 + else: + if h > hv: + rhoo = rhov else: - rhov = self._Vapor_Density(T) - rhol = self._Liquid_Density(T) - deltaL = rhol/rhoc - deltaG = rhov/rhoc - - idealL = self._phi0(tau, deltaL) - idealG = self._phi0(tau, deltaG) - fioL = idealL["fio"] - fioG = idealG["fio"] - fiot = idealL["fiot"] - firL = _phir(tau, deltaL, self._constants) - firtL = _phirt(tau, deltaL, self._constants) - sl = self.R*(tau*(fiot+firtL)-fioL-firL) - firG = _phir(tau, deltaG, self._constants) - firtG = _phirt(tau, deltaG, self._constants) - sv = self.R*(tau*(fiot+firtG)-fioG-firG) - - if sl <= s <= sv: - rhol, rhov, Ps = self._saturation(T) - vapor = self._Helmholtz(rhov, T) - liquido = self._Helmholtz(rhol, T) - sv = vapor["s"] - sl = liquido["s"] - x = (s-sl)/(sv-sl) - rho = 1/(x/rhov+(1-x)/rhol) - P = Ps/1000 - else: - if s > sv: - rhoo = rhov - else: - rhoo = rhol - rho = fsolve(f, rhoo)[0] - - elif self._mode == "Tu": - tau = Tc/T - ideal = self._phi0(tau, 1) - fiot = ideal["fiot"] - - def f(rho): - delta = rho/rhoc - - fird = _phird(tau, delta, self._constants) - firt = _phirt(tau, delta, self._constants) - Po = (1+delta*fird)*self.R*T*rho - ho = self.R*T*(1+tau*(fiot+firt)+delta*fird) - - return ho-Po/rho-u - - if T >= self.Tc: - rhoo = self.rhoc - rho = fsolve(f, rhoo)[0] + rhoo = rhol + rho = float(fsolve(rho_func, rhoo)[0]) + + self.solve_case1(T, P, x, rho, liquido, vapor) + + def solve_T_s(self, T: float, s: float) -> None: + """Solve for properties using T & s.""" + tau = self._constant_Tref/T + x = None + P = 0.0 + + def rho_func(rho: float) -> float: + if rho < 0: + rho = 1e-20 + delta = rho/rhoc + + ideal = self._phi0(tau, delta) + fir = self.residual.phir(tau, delta) + firt = self.residual.phirt(tau, delta) + so = self.R*(tau*(ideal.fit+firt)-ideal.fi-fir) + return so-s + + liquido = None + vapor = None + if T >= self._constant_Tref: + rhoo = self._constant_rhoc + rho = float(fsolve(rho_func, rhoo)[0]) + else: + rhov = self._Vapor_Density(T) + rhol = self._Liquid_Density(T) + deltaL = rhol/self._constant_rhoc + deltaG = rhov/self._constant_rhoc + + idealL = self._phi0(tau, deltaL) + idealG = self._phi0(tau, deltaG) + + firL = self.residual.phir(tau, deltaL) + firtL = self.residual.phirt(tau, deltaL) + sl = self.R*(tau*(idealL.fit+firtL)-idealL.fi-firL) + firG = self.residual.phir(tau, deltaG) + firtG = self.residual.phirt(tau, deltaG) + sv = self.R*(tau*(idealL.fit+firtG)-idealG.fi-firG) + + if sl <= s <= sv: + rhol, rhov, Ps = self._saturation(T) + vapor = self._Helmholtz(rhov, T) + liquido = self._Helmholtz(rhol, T) + sv = vapor.s + sl = liquido.s + x = (s-sl)/(sv-sl) + rho = 1/(x/rhov+(1-x)/rhol) + P = Ps/1000 + else: + if s > sv: + rhoo = rhov else: - rhov = self._Vapor_Density(T) - rhol = self._Liquid_Density(T) - deltaL = rhol/rhoc - deltaG = rhov/rhoc - - firdL = _phird(tau, deltaL, self._constants) - firtL = _phirt(tau, deltaL, self._constants) - firdG = _phird(tau, deltaG, self._constants) - firtG = _phirt(tau, deltaG, self._constants) - PoL = (1+deltaL*firdL)*self.R*T*rhol - PoG = (1+deltaG*firdG)*self.R*T*rhov - hoL = self.R*T*(1+tau*(fiot+firtL)+deltaL*firdL) - hoG = self.R*T*(1+tau*(fiot+firtG)+deltaG*firdG) - - uv = hoG-PoG/rhov - ul = hoL-PoL/rhol - if ul <= u <= uv: - rhol, rhov, Ps = self._saturation(T) - vapor = self._Helmholtz(rhov, T) - liquido = self._Helmholtz(rhol, T) - uv = vapor["h"]-vapor["P"]/rhov - ul = liquido["h"]-liquido["P"]/rhol - x = (u-ul)/(uv-ul) - rho = 1/(x/rhov-(1-x)/rhol) - P = Ps/1000 - else: - if u > uv: - rhoo = rhov - else: - rhoo = rhol - rho = fsolve(f, rhoo)[0] - - elif self._mode == "Prho": - delta = rho/rhoc + rhoo = rhol + rho = float(fsolve(rho_func, rhoo)[0]) - def f(T): - tau = Tc/T - - fird = _phird(tau, delta, self._constants) - Po = (1+delta*fird)*self.R*T*rho - return Po-P*1000 - - T = fsolve(f, To)[0] - rhol = self._Liquid_Density(T) - rhov = self._Vapor_Density(T) - if T == To or rhov <= rho <= rhol: - - def f(parr): - T, rhol, rhog = parr - tau = Tc/T - deltaL = rhol/self.rhoc - deltaG = rhog/self.rhoc - - firL = _phir(tau, deltaL, self._constants) - firdL = _phird(tau, deltaL, self._constants) - firG = _phir(tau, deltaG, self._constants) - firdG = _phird(tau, deltaG, self._constants) - - Jl = rhol*(1+deltaL*firdL) - Jv = rhog*(1+deltaG*firdG) - K = firL-firG - Ps = self.R*T*rhol*rhog/(rhol-rhog)*(K+log(rhol/rhog)) - return (Jl-Jv, - Jl*(1/rhog-1/rhol)-log(rhol/rhog)-K, - Ps - P*1000) - - for to in [To, 300, 400, 500, 600]: - rhoLo = self._Liquid_Density(to) - rhoGo = self._Vapor_Density(to) - sol = fsolve(f, [to, rhoLo, rhoGo], full_output=True) - T, rhoL, rhoG = sol[0] - x = (1./rho-1/rhoL)/(1/rhoG-1/rhoL) - if sol[2] == 1 and 0 <= x <= 1 and \ - sum(abs(sol[1]["fvec"])) < 1e-5: - break - - if sum(abs(sol[1]["fvec"])) > 1e-5: - raise(RuntimeError(sol[3])) - - liquido = self._Helmholtz(rhoL, T) - vapor = self._Helmholtz(rhoG, T) - P = self.R*T*rhoL*rhoG/(rhoL-rhoG)*( - liquido["fir"]-vapor["fir"]+log(rhoL/rhoG))/1000 - - elif self._mode == "Ph": - def funcion(parr): - rho, T = parr - delta = rho/rhoc - tau = Tc/T - - ideal = self._phi0(tau, delta) - fiot = ideal["fiot"] - fird = _phird(tau, delta, self._constants) - firt = _phirt(tau, delta, self._constants) - Po = (1+delta*fird)*self.R*T*rho - ho = self.R*T*(1+tau*(fiot+firt)+delta*fird) - return Po-P*1000, ho-h - - rho, T = fsolve(funcion, [rhoo, To]) - rhol = self._Liquid_Density(T) - rhov = self._Vapor_Density(T) - if rho == rhoo or rhov <= rho <= rhol: - - def f(parr): - T, rhol, rhog, x = parr - tau = Tc/T - deltaL = rhol/self.rhoc - deltaG = rhog/self.rhoc - - ideal = self._phi0(tau, deltaL) - fiot = ideal["fiot"] - - firL = _phir(tau, deltaL, self._constants) - firdL = _phird(tau, deltaL, self._constants) - firtL = _phirt(tau, deltaL, self._constants) - hoL = self.R*T*(1+tau*(fiot+firtL)+deltaL*firdL) - firG = _phir(tau, deltaG, self._constants) - firdG = _phird(tau, deltaG, self._constants) - firtG = _phirt(tau, deltaG, self._constants) - hoG = self.R*T*(1+tau*(fiot+firtG)+deltaG*firdG) - - Jl = rhol*(1+deltaL*firdL) - Jv = rhog*(1+deltaG*firdG) - K = firL-firG - Ps = self.R*T*rhol*rhog/(rhol-rhog)*(K+log(rhol/rhog)) - - return (Jl-Jv, - Jl*(1/rhog-1/rhol)-log(rhol/rhog)-K, - hoL*(1-x)+hoG*x - h, - Ps - P*1000) - - for to in [To, 300, 400, 500, 600]: - rLo = self._Liquid_Density(to) - rGo = self._Vapor_Density(to) - sol = fsolve(f, [to, rLo, rGo, 0.5], full_output=True) - T, rhoL, rhoG, x = sol[0] - if sol[2] == 1 and 0 <= x <= 1 and \ - sum(abs(sol[1]["fvec"])) < 1e-5: - break - - if sum(abs(sol[1]["fvec"])) > 1e-5: - raise(RuntimeError(sol[3])) - - liquido = self._Helmholtz(rhoL, T) - vapor = self._Helmholtz(rhoG, T) - P = self.R*T*rhoL*rhoG/(rhoL-rhoG)*( - liquido["fir"]-vapor["fir"]+log(rhoL/rhoG))/1000 - - elif self._mode == "Ps": - try: - x0 = st0.x - except NameError: - x0 = None - - if x0 is None or x0 == 0 or x0 == 1: - def f(parr): - rho, T = parr - delta = rho/rhoc - tau = Tc/T - - ideal = self._phi0(tau, delta) - fio = ideal["fio"] - fiot = ideal["fiot"] - fird = _phird(tau, delta, self._constants) - fir = _phir(tau, delta, self._constants) - firt = _phirt(tau, delta, self._constants) - Po = (1+delta*fird)*self.R*T*rho - so = self.R*(tau*(fiot+firt)-fio-fir) - return Po-P*1000, so-s - - rho, T = fsolve(f, [rhoo, To]) + self.solve_case1(T, P, x, rho, liquido, vapor) - else: - def funcion(parr): - rho, T = parr - rhol, rhov, Ps = self._saturation(T) - vapor = self._Helmholtz(rhov, T) - liquido = self._Helmholtz(rhol, T) - x = (1./rho-1/rhol)/(1/rhov-1/rhol) - return Ps-P*1000, vapor["s"]*x+liquido["s"]*(1-x)-s - rho, T = fsolve(funcion, [2., 500.]) - rhol, rhov, Ps = self._saturation(T) - vapor = self._Helmholtz(rhov, T) - liquido = self._Helmholtz(rhol, T) - sv = vapor["s"] - sl = liquido["s"] - x = (s-sl)/(sv-sl) - - elif self._mode == "Pu": - def f(parr): - rho, T = parr - delta = rho/rhoc - tau = Tc/T - - ideal = self._phi0(tau, delta) - fiot = ideal["fiot"] - fird = _phird(tau, delta, self._constants) - firt = _phirt(tau, delta, self._constants) - Po = (1+delta*fird)*self.R*T*rho - ho = self.R*T*(1+tau*(fiot+firt)+delta*fird) - return ho-Po/rho-u, Po-P*1000 - - sol = fsolve(f, [rhoo, To], full_output=True) - rho, T = sol[0] - rhol = self._Liquid_Density(T) - rhov = self._Vapor_Density(T) - if rho == rhoo or sol[2] != 1: - - def f(parr): - T, rhol, rhog, x = parr - tau = Tc/T - deltaL = rhol/self.rhoc - deltaG = rhog/self.rhoc - - ideal = self._phi0(tau, deltaL) - fiot = ideal["fiot"] - - firL = _phir(tau, deltaL, self._constants) - firdL = _phird(tau, deltaL, self._constants) - firtL = _phirt(tau, deltaL, self._constants) - hoL = self.R*T*(1+tau*(fiot+firtL)+deltaL*firdL) - firG = _phir(tau, deltaG, self._constants) - firdG = _phird(tau, deltaG, self._constants) - firtG = _phirt(tau, deltaG, self._constants) - hoG = self.R*T*(1+tau*(fiot+firtG)+deltaG*firdG) - - Jl = rhol*(1+deltaL*firdL) - Jv = rhog*(1+deltaG*firdG) - K = firL-firG - Ps = self.R*T*rhol*rhog/(rhol-rhog)*(K+log(rhol/rhog)) - vu = hoG-Ps/rhog - lu = hoL-Ps/rhol - return (Jl-Jv, - Jl*(1/rhog-1/rhol)-log(rhol/rhog)-K, - lu*(1-x)+vu*x - u, - Ps - P*1000) - - for to in [To, 300, 400, 500, 600]: - rLo = self._Liquid_Density(to) - rGo = self._Vapor_Density(to) - sol = fsolve(f, [to, rLo, rGo, 0.5], full_output=True) - T, rhoL, rhoG, x = sol[0] - if sol[2] == 1 and 0 <= x <= 1 and \ - sum(abs(sol[1]["fvec"])) < 1e-5: - break - - if sum(abs(sol[1]["fvec"])) > 1e-5: - raise(RuntimeError(sol[3])) - - liquido = self._Helmholtz(rhoL, T) - vapor = self._Helmholtz(rhoG, T) - P = self.R*T*rhoL*rhoG/(rhoL-rhoG)*( - liquido["fir"]-vapor["fir"]+log(rhoL/rhoG))/1000 - - elif self._mode == "rhoh": - delta = rho/rhoc + def solve_T_u(self, T: float, u: float) -> None: + """Solve for properties using T & u.""" + tau = self._constant_Tref/T + ideal = self._phi0(tau, 1) + x = None + P = 0.0 - def f(T): - tau = Tc/T - - ideal = self._phi0(tau, delta) - fiot = ideal["fiot"] - fird = _phird(tau, delta, self._constants) - firt = _phirt(tau, delta, self._constants) - ho = self.R*T*(1+tau*(fiot+firt)+delta*fird) - return ho-h - - T = fsolve(f, To)[0] - rhol = self._Liquid_Density(T) - rhov = self._Vapor_Density(T) - if T == To or rhov <= rho <= rhol: - def f(parr): - T, rhol, rhog = parr - tau = Tc/T - deltaL = rhol/self.rhoc - deltaG = rhog/self.rhoc - - ideal = self._phi0(tau, deltaL) - fiot = ideal["fiot"] - firL = _phir(tau, deltaL, self._constants) - firdL = _phird(tau, deltaL, self._constants) - firtL = _phirt(tau, deltaL, self._constants) - hoL = self.R*T*(1+tau*(fiot+firtL)+deltaL*firdL) - firG = _phir(tau, deltaG, self._constants) - firdG = _phird(tau, deltaG, self._constants) - firtG = _phirt(tau, deltaG, self._constants) - hoG = self.R*T*(1+tau*(fiot+firtG)+deltaG*firdG) - - Jl = rhol*(1+deltaL*firdL) - Jv = rhog*(1+deltaG*firdG) - K = firL-firG - x = (1./rho-1/rhol)/(1/rhog-1/rhol) - return (Jl-Jv, - Jl*(1/rhog-1/rhol)-log(rhol/rhog)-K, - hoL*(1-x)+hoG*x - h) - - for to in [To, 300, 400, 500, 600]: - rhoLo = self._Liquid_Density(to) - rhoGo = self._Vapor_Density(to) - sol = fsolve(f, [to, rhoLo, rhoGo], full_output=True) - T, rhoL, rhoG = sol[0] - x = (1./rho-1/rhoL)/(1/rhoG-1/rhoL) - if sol[2] == 1 and 0 <= x <= 1 and \ - sum(abs(sol[1]["fvec"])) < 1e-5: - break - - if sum(abs(sol[1]["fvec"])) > 1e-5: - raise(RuntimeError(sol[3])) - - liquido = self._Helmholtz(rhoL, T) - vapor = self._Helmholtz(rhoG, T) - P = self.R*T*rhoL*rhoG/(rhoL-rhoG)*( - liquido["fir"]-vapor["fir"]+log(rhoL/rhoG))/1000 - - elif self._mode == "rhos": - delta = rho/rhoc + def rho_func(rho: float) -> float: + delta = rho/self._constant_rhoc - def f(T): - tau = Tc/T - ideal = self._phi0(tau, delta) - fio = ideal["fio"] - fiot = ideal["fiot"] - fir = _phir(tau, delta, self._constants) - firt = _phirt(tau, delta, self._constants) - so = self.R*(tau*(fiot+firt)-fio-fir) - return so-s - - T = fsolve(f, To)[0] - rhol = self._Liquid_Density(T) - rhov = self._Vapor_Density(T) - if T == To or rhov <= rho <= rhol: - - def f(parr): - T, rhol, rhog = parr - tau = Tc/T - deltaL = rhol/self.rhoc - deltaG = rhog/self.rhoc - - idealL = self._phi0(tau, deltaL) - fioL = idealL["fio"] - fiot = idealL["fiot"] - idealG = self._phi0(tau, deltaG) - fioG = idealG["fio"] - - firL = _phir(tau, deltaL, self._constants) - firdL = _phird(tau, deltaL, self._constants) - firtL = _phirt(tau, deltaL, self._constants) - soL = self.R*(tau*(fiot+firtL)-fioL-firL) - firG = _phir(tau, deltaG, self._constants) - firdG = _phird(tau, deltaG, self._constants) - firtG = _phirt(tau, deltaG, self._constants) - soG = self.R*(tau*(fiot+firtG)-fioG-firG) - - Jl = rhol*(1+deltaL*firdL) - Jv = rhog*(1+deltaG*firdG) - K = firL-firG - x = (1./rho-1/rhol)/(1/rhog-1/rhol) - return (Jl-Jv, - Jl*(1/rhog-1/rhol)-log(rhol/rhog)-K, - soL*(1-x)+soG*x - s) - - for to in [To, 300, 400, 500, 600]: - rhoLo = self._Liquid_Density(to) - rhoGo = self._Vapor_Density(to) - sol = fsolve(f, [to, rhoLo, rhoGo], full_output=True) - T, rhoL, rhoG = sol[0] - x = (1./rho-1/rhoL)/(1/rhoG-1/rhoL) - if sol[2] == 1 and 0 <= x <= 1 and \ - sum(abs(sol[1]["fvec"])) < 1e-5: - break - - if sum(abs(sol[1]["fvec"])) > 1e-5: - raise(RuntimeError(sol[3])) - - liquido = self._Helmholtz(rhoL, T) - vapor = self._Helmholtz(rhoG, T) - P = self.R*T*rhoL*rhoG/(rhoL-rhoG)*( - liquido["fir"]-vapor["fir"]+log(rhoL/rhoG))/1000 - - elif self._mode == "rhou": - delta = rho/rhoc + fird = self.residual.phird(tau, delta) + firt = self.residual.phirt(tau, delta) + Po = (1+delta*fird)*self.R*T*rho + ho = self.R*T*(1+tau*(ideal.fit+firt)+delta*fird) - def f(T): - tau = Tc/T - - ideal = self._phi0(tau, delta) - fiot = ideal["fiot"] - fird = _phird(tau, delta, self._constants) - firt = _phirt(tau, delta, self._constants) - Po = (1+delta*fird)*self.R*T*rho - ho = self.R*T*(1+tau*(fiot+firt)+delta*fird) - return ho-Po/rho-u - - T = fsolve(f, To)[0] - rhol = self._Liquid_Density(T) - rhov = self._Vapor_Density(T) - if T == To or rhov <= rho <= rhol: - def f(parr): - T, rhol, rhog = parr - tau = Tc/T - deltaL = rhol/self.rhoc - deltaG = rhog/self.rhoc - - ideal = self._phi0(tau, deltaL) - fiot = ideal["fiot"] - firL = _phir(tau, deltaL, self._constants) - firdL = _phird(tau, deltaL, self._constants) - firtL = _phirt(tau, deltaL, self._constants) - hoL = self.R*T*(1+tau*(fiot+firtL)+deltaL*firdL) - firG = _phir(tau, deltaG, self._constants) - firdG = _phird(tau, deltaG, self._constants) - firtG = _phirt(tau, deltaG, self._constants) - hoG = self.R*T*(1+tau*(fiot+firtG)+deltaG*firdG) - - Jl = rhol*(1+deltaL*firdL) - Jv = rhog*(1+deltaG*firdG) - K = firL-firG - x = (1./rho-1/rhol)/(1/rhog-1/rhol) - Ps = self.R*T*rhol*rhog/(rhol-rhog)*(K+log(rhol/rhog)) - vu = hoG-Ps/rhog - lu = hoL-Ps/rhol - return (Jl-Jv, - Jl*(1/rhog-1/rhol)-log(rhol/rhog)-K, - lu*(1-x)+vu*x - u) - - for to in [To, 300, 400, 500, 600]: - rhoLo = self._Liquid_Density(to) - rhoGo = self._Vapor_Density(to) - sol = fsolve(f, [to, rhoLo, rhoGo], full_output=True) - T, rhoL, rhoG = sol[0] - x = (1./rho-1/rhoL)/(1/rhoG-1/rhoL) - if sol[2] == 1 and 0 <= x <= 1 and \ - sum(abs(sol[1]["fvec"])) < 1e-5: - break - - if sum(abs(sol[1]["fvec"])) > 1e-5: - raise(RuntimeError(sol[3])) - - liquido = self._Helmholtz(rhoL, T) - vapor = self._Helmholtz(rhoG, T) - P = self.R*T*rhoL*rhoG/(rhoL-rhoG)*( - liquido["fir"]-vapor["fir"]+log(rhoL/rhoG))/1000 - - elif self._mode == "hs": - def f(parr): - rho, T = parr - delta = rho/rhoc - tau = Tc/T - - ideal = self._phi0(tau, delta) - fio = ideal["fio"] - fiot = ideal["fiot"] - fird = _phird(tau, delta, self._constants) - fir = _phir(tau, delta, self._constants) - firt = _phirt(tau, delta, self._constants) - ho = self.R*T*(1+tau*(fiot+firt)+delta*fird) - so = self.R*(tau*(fiot+firt)-fio-fir) - return ho-h, so-s - - rho, T = fsolve(f, [rhoo, To]) - rhol = self._Liquid_Density(T) - rhov = self._Vapor_Density(T) - if rhov <= rho <= rhol: - - def f(parr): - T, rhol, rhog, x = parr - tau = Tc/T - deltaL = rhol/self.rhoc - deltaG = rhog/self.rhoc - - idealL = self._phi0(tau, deltaL) - fiot = idealL["fiot"] - fioL = idealL["fio"] - idealG = self._phi0(tau, deltaG) - fioG = idealG["fio"] - - firL = _phir(tau, deltaL, self._constants) - firdL = _phird(tau, deltaL, self._constants) - firtL = _phirt(tau, deltaL, self._constants) - hoL = self.R*T*(1+tau*(fiot+firtL)+deltaL*firdL) - soL = self.R*(tau*(fiot+firtL)-fioL-firL) - firG = _phir(tau, deltaG, self._constants) - firdG = _phird(tau, deltaG, self._constants) - firtG = _phirt(tau, deltaG, self._constants) - hoG = self.R*T*(1+tau*(fiot+firtG)+deltaG*firdG) - soG = self.R*(tau*(fiot+firtG)-fioG-firG) - - Jl = rhol*(1+deltaL*firdL) - Jv = rhog*(1+deltaG*firdG) - K = firL-firG - return (Jl-Jv, - Jl*(1/rhog-1/rhol)-log(rhol/rhog)-K, - hoL*(1-x)+hoG*x - h, - soL*(1-x)+soG*x - s) - - for to in [To, 300, 400, 500, 600]: - rLo = self._Liquid_Density(to) - rGo = self._Vapor_Density(to) - sol = fsolve(f, [to, rLo, rGo, 0.5], full_output=True) - T, rhoL, rhoG, x = sol[0] - if sol[2] == 1 and 0 <= x <= 1 and \ - sum(abs(sol[1]["fvec"])) < 1e-5: - break - - if sum(abs(sol[1]["fvec"])) > 1e-5: - raise(RuntimeError(sol[3])) - - liquido = self._Helmholtz(rhoL, T) - vapor = self._Helmholtz(rhoG, T) - P = self.R*T*rhoL*rhoG/(rhoL-rhoG)*( - liquido["fir"]-vapor["fir"]+log(rhoL/rhoG))/1000 - - elif self._mode == "hu": - def f(parr): - rho, T = parr - delta = rho/rhoc - tau = Tc/T - - ideal = self._phi0(tau, delta) - fiot = ideal["fiot"] - fird = _phird(tau, delta, self._constants) - firt = _phirt(tau, delta, self._constants) - Po = (1+delta*fird)*self.R*T*rho - ho = self.R*T*(1+tau*(fiot+firt)+delta*fird) - - return ho-Po/rho-u, ho-h - - sol = fsolve(f, [rhoo, To], full_output=True) - rho, T = sol[0] - rhol = self._Liquid_Density(T) - rhov = self._Vapor_Density(T) - if sol[2] != 1 or rhov <= rho <= rhol: - - def f(parr): - T, rhol, rhog, x = parr - tau = Tc/T - deltaL = rhol/self.rhoc - deltaG = rhog/self.rhoc - - ideal = self._phi0(tau, deltaL) - fiot = ideal["fiot"] - - firL = _phir(tau, deltaL, self._constants) - firdL = _phird(tau, deltaL, self._constants) - firtL = _phirt(tau, deltaL, self._constants) - hoL = self.R*T*(1+tau*(fiot+firtL)+deltaL*firdL) - firG = _phir(tau, deltaG, self._constants) - firdG = _phird(tau, deltaG, self._constants) - firtG = _phirt(tau, deltaG, self._constants) - hoG = self.R*T*(1+tau*(fiot+firtG)+deltaG*firdG) - - Jl = rhol*(1+deltaL*firdL) - Jv = rhog*(1+deltaG*firdG) - K = firL-firG - - Ps = self.R*T*rhol*rhog/(rhol-rhog)*(K+log(rhol/rhog)) - vu = hoG-Ps/rhog - lu = hoL-Ps/rhol - - return (Jl-Jv, - Jl*(1/rhog-1/rhol)-log(rhol/rhog)-K, - hoL*(1-x)+hoG*x - h, - lu*(1-x)+vu*x - u) - - for to in [To, 300, 400, 500, 600]: - rLo = self._Liquid_Density(to) - rGo = self._Vapor_Density(to) - sol = fsolve(f, [to, rLo, rGo, 0.5], full_output=True) - T, rhoL, rhoG, x = sol[0] - if sol[2] == 1 and 0 <= x <= 1 and \ - sum(abs(sol[1]["fvec"])) < 1e-5: - break - - if sum(abs(sol[1]["fvec"])) > 1e-5: - raise(RuntimeError(sol[3])) - - liquido = self._Helmholtz(rhoL, T) - vapor = self._Helmholtz(rhoG, T) - P = self.R*T*rhoL*rhoG/(rhoL-rhoG)*( - liquido["fir"]-vapor["fir"]+log(rhoL/rhoG))/1000 - - elif self._mode == "su": - def f(parr): - rho, T = parr - delta = rho/rhoc - tau = Tc/T - - ideal = self._phi0(tau, delta) - fio = ideal["fio"] - fiot = ideal["fiot"] - fird = _phird(tau, delta, self._constants) - fir = _phir(tau, delta, self._constants) - firt = _phirt(tau, delta, self._constants) - ho = self.R*T*(1+tau*(fiot+firt)+delta*fird) - so = self.R*(tau*(fiot+firt)-fio-fir) - Po = (1+delta*fird)*self.R*T*rho - return ho-Po/rho-u, so-s - - sol = fsolve(f, [rhoo, To], full_output=True) - rho, T = sol[0] - rhol = self._Liquid_Density(T) - rhov = self._Vapor_Density(T) - if sol[2] != 1 or rhov <= rho <= rhol: - - def f(parr): - T, rhol, rhog, x = parr - tau = Tc/T - deltaL = rhol/self.rhoc - deltaG = rhog/self.rhoc - - idealL = self._phi0(tau, deltaL) - fiot = idealL["fiot"] - fioL = idealL["fio"] - idealG = self._phi0(tau, deltaG) - fioG = idealG["fio"] - - firL = _phir(tau, deltaL, self._constants) - firdL = _phird(tau, deltaL, self._constants) - firtL = _phirt(tau, deltaL, self._constants) - hoL = self.R*T*(1+tau*(fiot+firtL)+deltaL*firdL) - soL = self.R*(tau*(fiot+firtL)-fioL-firL) - firG = _phir(tau, deltaG, self._constants) - firdG = _phird(tau, deltaG, self._constants) - firtG = _phirt(tau, deltaG, self._constants) - hoG = self.R*T*(1+tau*(fiot+firtG)+deltaG*firdG) - soG = self.R*(tau*(fiot+firtG)-fioG-firG) - - Jl = rhol*(1+deltaL*firdL) - Jv = rhog*(1+deltaG*firdG) - K = firL-firG - - Ps = self.R*T*rhol*rhog/(rhol-rhog)*(K+log(rhol/rhog)) - vu = hoG-Ps/rhog - lu = hoL-Ps/rhol - - return (Jl-Jv, - Jl*(1/rhog-1/rhol)-log(rhol/rhog)-K, - soL*(1-x)+soG*x - s, - lu*(1-x)+vu*x - u) - - for to in [To, 300, 400, 500, 600]: - rLo = self._Liquid_Density(to) - rGo = self._Vapor_Density(to) - sol = fsolve(f, [to, rLo, rGo, 0.5], full_output=True) - T, rhoL, rhoG, x = sol[0] - if sol[2] == 1 and 0 <= x <= 1 and \ - sum(abs(sol[1]["fvec"])) < 1e-5: - break - - if sum(abs(sol[1]["fvec"])) > 1e-5: - raise(RuntimeError(sol[3])) - - liquido = self._Helmholtz(rhoL, T) - vapor = self._Helmholtz(rhoG, T) - P = self.R*T*rhoL*rhoG/(rhoL-rhoG)*( - liquido["fir"]-vapor["fir"]+log(rhoL/rhoG))/1000 - - elif self._mode == "Trho": - if T < self.Tc: - rhov = self._Vapor_Density(T) - rhol = self._Liquid_Density(T) - if rhol > rho > rhov: - rhol, rhov, Ps = self._saturation(T) - if rhol > rho > rhov: - vapor = self._Helmholtz(rhov, T) - liquido = self._Helmholtz(rhol, T) - x = (1/rho-1/rhol)/(1/rhov-1/rhol) - P = Ps/1000 - - rho = float(rho) - T = float(T) - propiedades = self._Helmholtz(rho, T) - - if T > self.Tc: - x = 1 - elif x is None: - x = 0 - - if not P: - P = propiedades["P"]/1000. - - elif self._mode == "Tx": - # Check input T in saturation range - if self.Tt > T or self.Tc < T or x > 1 or x < 0: - raise NotImplementedError("Incoming out of bound") + return ho-Po/rho-u - rhol, rhov, Ps = self._saturation(T) - vapor = self._Helmholtz(rhov, T) - liquido = self._Helmholtz(rhol, T) - if x == 0: - propiedades = liquido - elif x == 1: - propiedades = vapor - P = Ps/1000. - - elif self._mode == "Px": - # Check input P in saturation range - if self.Pc < P or x > 1 or x < 0: - raise NotImplementedError("Incoming out of bound") - - # Iterate over saturation routine to get T - def f(T): - rhol = self._Liquid_Density(T) - rhog = self._Vapor_Density(T) - - deltaL = rhol/self.rhoc - deltaG = rhog/self.rhoc - - tau = Tc/T - - firL = _phir(tau, deltaL, self._constants) - firG = _phir(tau, deltaG, self._constants) - Ps = self.R*T*rhol*rhog/(rhol-rhog)*( - firL-firG+log(deltaL/deltaG)) - return Ps/1000-P - - if T0: - To = T0 - elif self.name == "water": - To = _TSat_P(P) + liquido = None + vapor = None + if T >= self._constant_Tref: + rhoo = self._constant_rhoc + rho = float(fsolve(rho_func, rhoo)[0]) + else: + rhov = self._Vapor_Density(T) + rhol = self._Liquid_Density(T) + deltaL = rhol/self._constant_rhoc + deltaG = rhov/self._constant_rhoc + + firdL = self.residual.phird(tau, deltaL) + firtL = self.residual.phirt(tau, deltaL) + firdG = self.residual.phird(tau, deltaG) + firtG = self.residual.phirt(tau, deltaG) + PoL = (1+deltaL*firdL)*self.R*T*rhol + PoG = (1+deltaG*firdG)*self.R*T*rhov + hoL = self.R*T*(1+tau*(ideal.fit+firtL)+deltaL*firdL) + hoG = self.R*T*(1+tau*(ideal.fit+firtG)+deltaG*firdG) + + uv = hoG-PoG/rhov + ul = hoL-PoL/rhol + if ul <= u <= uv: + rhol, rhov, Ps = self._saturation(T) + vapor = self._Helmholtz(rhov, T) + liquido = self._Helmholtz(rhol, T) + uv = vapor.h-vapor.P/rhov + ul = liquido.h-liquido.P/rhol + x = (u-ul)/(uv-ul) + rho = 1/(x/rhov-(1-x)/rhol) + P = Ps/1000 else: - To = (self.Tc+self.Tt)/2 - T = fsolve(f, To)[0] + if u > uv: + rhoo = rhov + else: + rhoo = rhol + rho = float(fsolve(rho_func, rhoo)[0]) + + self.solve_case1(T, P, x, rho, liquido, vapor) + + def solve_P_rho(self, P: float, rho: float, To: float) -> None: + """Solve for properties using P & rho.""" + delta = rho/self._constant_rhoc + x = None + + def t_func(T: float) -> float: + tau = self._constant_Tref/T + fird = self.residual.phird(tau, delta) + Po = (1+delta*fird)*self.R*T*rho + return Po-P*1000 + + T = float(fsolve(t_func, To)[0]) + rhol = self._Liquid_Density(T) + rhov = self._Vapor_Density(T) + liquido = None + vapor = None + if T == To or rhov <= rho <= rhol: + + def f3(parr: Tuple[float, float, float]) -> Tuple[float, float, float]: + T, rhol, rhog = parr + tau = self._constant_Tref/T + deltaL = rhol/self._constant_rhoc + deltaG = rhog/self._constant_rhoc + + firL = self.residual.phir(tau, deltaL) + firdL = self.residual.phird(tau, deltaL) + firG = self.residual.phir(tau, deltaG) + firdG = self.residual.phird(tau, deltaG) + + Jl = rhol*(1+deltaL*firdL) + Jv = rhog*(1+deltaG*firdG) + K = firL-firG + try: + logrho = log(rhol/rhog) + except ValueError: + logrho = float('nan') + Ps = self.R*T*rhol*rhog/(rhol-rhog)*(K+logrho) + return (Jl-Jv, Jl*(1/rhog-1/rhol)-logrho-K, Ps - P*1000) + + for to in [To, 300, 400, 500, 600]: + rhoLo = self._Liquid_Density(to) + rhoGo = self._Vapor_Density(to) + sol = fsolve(f3, [to, rhoLo, rhoGo], full_output=True) + T, rhoL, rhoG = tuple(map(float, sol[0])) + x = (1./rho-1/rhoL)/(1/rhoG-1/rhoL) + if sol[2] == 1 and 0 <= x <= 1 and \ + sum(abs(sol[1]["fvec"])) < 1e-5: + break + + if sum(abs(sol[1]["fvec"])) > 1e-5: + raise(RuntimeError(sol[3])) + + liquido = self._Helmholtz(rhoL, T) + vapor = self._Helmholtz(rhoG, T) + P = self.R*T*rhoL*rhoG/(rhoL-rhoG)*( + liquido.fir-vapor.fir+log(rhoL/rhoG))/1000 + + self.solve_case1(T, P, x, rho, liquido, vapor) + + def solve_P_h(self, P: float, h: float, rhoo: float, To: float) -> None: + """Solve for properties using P & h.""" + x = None + + def f2(parr: Tuple[float, float]) -> Tuple[float, float]: + rho, T = parr + delta = rho/rhoc + tau = self._constant_Tref/T + + ideal = self._phi0(tau, delta) + fird = self.residual.phird(tau, delta) + firt = self.residual.phirt(tau, delta) + Po = (1+delta*fird)*self.R*T*rho + ho = self.R*T*(1+tau*(ideal.fit+firt)+delta*fird) + return Po-P*1000, ho-h + + rho, T = tuple(map(float, fsolve(f2, [rhoo, To]))) + rhol = self._Liquid_Density(T) + rhov = self._Vapor_Density(T) + liquido = None + vapor = None + if rho == rhoo or rhov <= rho <= rhol: + + def f4(parr: Tuple[float, float, float, float]) -> Tuple[ + float, float, float, float]: + T, rhol, rhog, x = parr + tau = self._constant_Tref/T + deltaL = rhol/self._constant_rhoc + deltaG = rhog/self._constant_rhoc + + ideal = self._phi0(tau, deltaL) + + firL = self.residual.phir(tau, deltaL) + firdL = self.residual.phird(tau, deltaL) + firtL = self.residual.phirt(tau, deltaL) + hoL = self.R*T*(1+tau*(ideal.fit+firtL)+deltaL*firdL) + firG = self.residual.phir(tau, deltaG) + firdG = self.residual.phird(tau, deltaG) + firtG = self.residual.phirt(tau, deltaG) + hoG = self.R*T*(1+tau*(ideal.fit+firtG)+deltaG*firdG) + + Jl = rhol*(1+deltaL*firdL) + Jv = rhog*(1+deltaG*firdG) + K = firL-firG + Ps = self.R*T*rhol*rhog/(rhol-rhog)*(K+log(rhol/rhog)) + + return (Jl-Jv, + Jl*(1/rhog-1/rhol)-log(rhol/rhog)-K, + hoL*(1-x)+hoG*x - h, + Ps - P*1000) + + for to in [To, 300, 400, 500, 600]: + rLo = self._Liquid_Density(to) + rGo = self._Vapor_Density(to) + sol = fsolve(f4, [to, rLo, rGo, 0.5], full_output=True) + T, rhoL, rhoG, x = tuple(map(float, sol[0])) + if sol[2] == 1 and 0 <= x <= 1 and \ + sum(abs(sol[1]["fvec"])) < 1e-5: + break + + if sum(abs(sol[1]["fvec"])) > 1e-5: + raise(RuntimeError(sol[3])) + + liquido = self._Helmholtz(rhoL, T) + vapor = self._Helmholtz(rhoG, T) + P = self.R*T*rhoL*rhoG/(rhoL-rhoG)*( + liquido.fir-vapor.fir+log(rhoL/rhoG))/1000 + + self.solve_case1(T, P, x, rho, liquido, vapor) + + def solve_P_s(self, P: float, s: float, rhoo: float, + To: float, x0: Optional[float]) -> None: + """Solve for properties using P & s.""" + if x0 is None or x0 == 0 or x0 == 1: + + def f2(parr: Tuple[float, float]) -> Tuple[float, float]: + rho, T = parr + delta = rho/rhoc + tau = self._constant_Tref/T + + ideal = self._phi0(tau, delta) + fird = self.residual.phird(tau, delta) + fir = self.residual.phir(tau, delta) + firt = self.residual.phirt(tau, delta) + Po = (1+delta*fird)*self.R*T*rho + so = self.R*(tau*(ideal.fit+firt)-ideal.fi-fir) + return Po-P*1000, so-s + + rho, T = tuple(map(float, fsolve(f2, [rhoo, To]))) + liquido = None + vapor = None + x = None + else: + + def f2(parr: Tuple[float, float]) -> Tuple[float, float]: + rho, T = parr + rhol, rhov, Ps = self._saturation(T) + vapor = self._Helmholtz(rhov, T) + liquido = self._Helmholtz(rhol, T) + x = (1./rho-1/rhol)/(1/rhov-1/rhol) + return Ps-P*1000, vapor.s*x+liquido.s*(1-x)-s + rho, T = tuple(map(float, fsolve(f2, [2., 500.]))) rhol, rhov, Ps = self._saturation(T) vapor = self._Helmholtz(rhov, T) liquido = self._Helmholtz(rhol, T) - if x == 0: - propiedades = liquido - elif x == 1: - propiedades = vapor + x = (s-liquido.s)/(vapor.s-liquido.s) + + self.solve_case1(T, P, x, rho, liquido, vapor) + + def solve_P_u(self, P: float, u: float, rhoo: float, To: float) -> None: + """Solve for properties using P & u.""" + x = None + + def f2(parr: Tuple[float, float]) -> Tuple[float, float]: + rho, T = parr + delta = rho/rhoc + tau = self._constant_Tref/T + + ideal = self._phi0(tau, delta) + fird = self.residual.phird(tau, delta) + firt = self.residual.phirt(tau, delta) + Po = (1+delta*fird)*self.R*T*rho + ho = self.R*T*(1+tau*(ideal.fit+firt)+delta*fird) + return ho-Po/rho-u, Po-P*1000 + + sol = fsolve(f2, [rhoo, To], full_output=True) + rho, T = tuple(map(float, sol[0])) + liquido = None + vapor = None + if rho == rhoo or sol[2] != 1: + + def f4(parr: Tuple[float, float, float, float]) -> Tuple[ + float, float, float, float]: + T, rhol, rhog, x = parr + tau = self._constant_Tref/T + deltaL = rhol/self._constant_rhoc + deltaG = rhog/self._constant_rhoc + + ideal = self._phi0(tau, deltaL) + + firL = self.residual.phir(tau, deltaL) + firdL = self.residual.phird(tau, deltaL) + firtL = self.residual.phirt(tau, deltaL) + hoL = self.R*T*(1+tau*(ideal.fit+firtL)+deltaL*firdL) + firG = self.residual.phir(tau, deltaG) + firdG = self.residual.phird(tau, deltaG) + firtG = self.residual.phirt(tau, deltaG) + hoG = self.R*T*(1+tau*(ideal.fit+firtG)+deltaG*firdG) + + Jl = rhol*(1+deltaL*firdL) + Jv = rhog*(1+deltaG*firdG) + K = firL-firG + try: + logrho = log(rhol/rhog) + except ValueError: + logrho = float('nan') + Ps = self.R*T*rhol*rhog/(rhol-rhog)*(K+logrho) + vu = hoG-Ps/rhog + lu = hoL-Ps/rhol + return (Jl-Jv, Jl*(1/rhog-1/rhol)-logrho-K, + lu*(1-x)+vu*x - u, Ps - P*1000) + + for to in [To, 300, 400, 500, 600]: + rLo = self._Liquid_Density(to) + rGo = self._Vapor_Density(to) + sol = fsolve(f4, [to, rLo, rGo, 0.5], full_output=True) + T, rhoL, rhoG, x = tuple(map(float, sol[0])) + if sol[2] == 1 and 0 <= x <= 1 and \ + sum(abs(sol[1]["fvec"])) < 1e-5: + break + + if sum(abs(sol[1]["fvec"])) > 1e-5: + raise(RuntimeError(sol[3])) + + liquido = self._Helmholtz(rhoL, T) + vapor = self._Helmholtz(rhoG, T) + P = self.R*T*rhoL*rhoG/(rhoL-rhoG)*( + liquido.fir-vapor.fir+log(rhoL/rhoG))/1000 + + self.solve_case1(T, P, x, rho, liquido, vapor) + + def solve_rho_h(self, rho: float, h: float, To: float) -> None: + """Solve for properties using rho & h.""" + delta = rho/rhoc + P = 0.0 + x = None + + def t_func(T: float) -> float: + tau = self._constant_Tref/T + + ideal = self._phi0(tau, delta) + fird = self.residual.phird(tau, delta) + firt = self.residual.phirt(tau, delta) + ho = self.R*T*(1+tau*(ideal.fit+firt)+delta*fird) + return ho-h + + T = float(fsolve(t_func, To)[0]) + rhol = self._Liquid_Density(T) + rhov = self._Vapor_Density(T) + liquido = None + vapor = None + if T == To or rhov <= rho <= rhol: + def f3(parr: Tuple[float, float, float]) -> Tuple[float, float, float]: + T, rhol, rhog = parr + tau = self._constant_Tref/T + deltaL = rhol/self._constant_rhoc + deltaG = rhog/self._constant_rhoc + + ideal = self._phi0(tau, deltaL) + firL = self.residual.phir(tau, deltaL) + firdL = self.residual.phird(tau, deltaL) + firtL = self.residual.phirt(tau, deltaL) + hoL = self.R*T*(1+tau*(ideal.fit+firtL)+deltaL*firdL) + firG = self.residual.phir(tau, deltaG) + firdG = self.residual.phird(tau, deltaG) + firtG = self.residual.phirt(tau, deltaG) + hoG = self.R*T*(1+tau*(ideal.fit+firtG)+deltaG*firdG) + + Jl = rhol*(1+deltaL*firdL) + Jv = rhog*(1+deltaG*firdG) + K = firL-firG + x = (1./rho-1/rhol)/(1/rhog-1/rhol) + try: + logrho = log(rhol/rhog) + except ValueError: + logrho = float('nan') + return (Jl-Jv, Jl*(1/rhog-1/rhol)-logrho-K, + hoL*(1-x)+hoG*x - h) + + for to in [To, 300, 400, 500, 600]: + rhoLo = self._Liquid_Density(to) + rhoGo = self._Vapor_Density(to) + sol = fsolve(f3, [to, rhoLo, rhoGo], full_output=True) + T, rhoL, rhoG = tuple(map(float, sol[0])) + x = (1./rho-1/rhoL)/(1/rhoG-1/rhoL) + if sol[2] == 1 and 0 <= x <= 1 and \ + sum(abs(sol[1]["fvec"])) < 1e-5: + break + + if sum(abs(sol[1]["fvec"])) > 1e-5: + raise(RuntimeError(sol[3])) + + liquido = self._Helmholtz(rhoL, T) + vapor = self._Helmholtz(rhoG, T) + P = self.R*T*rhoL*rhoG/(rhoL-rhoG)*( + liquido.fir-vapor.fir+log(rhoL/rhoG))/1000 + + self.solve_case1(T, P, x, rho, liquido, vapor) + + def solve_rho_s(self, rho: float, s: float, To: float) -> None: + """Solve for properties using rho & s.""" + delta = rho/rhoc + P = 0.0 + x = None + + def t_func(T: float) -> float: + tau = self._constant_Tref/T + ideal = self._phi0(tau, delta) + fir = self.residual.phir(tau, delta) + firt = self.residual.phirt(tau, delta) + so = self.R*(tau*(ideal.fit+firt)-ideal.fi-fir) + return so-s + + T = float(fsolve(t_func, To)[0]) + rhol = self._Liquid_Density(T) + rhov = self._Vapor_Density(T) + liquido = None + vapor = None + if T == To or rhov <= rho <= rhol: + + def f3(parr: Tuple[float, float, float]) -> Tuple[float, float, float]: + T, rhol, rhog = parr + tau = self._constant_Tref/T + deltaL = rhol/self._constant_rhoc + deltaG = rhog/self._constant_rhoc + + idealL = self._phi0(tau, deltaL) + idealG = self._phi0(tau, deltaG) + + firL = self.residual.phir(tau, deltaL) + firdL = self.residual.phird(tau, deltaL) + firtL = self.residual.phirt(tau, deltaL) + soL = self.R*(tau*(idealL.fit+firtL)-idealL.fi-firL) + firG = self.residual.phir(tau, deltaG) + firdG = self.residual.phird(tau, deltaG) + firtG = self.residual.phirt(tau, deltaG) + soG = self.R*(tau*(idealL.fit+firtG)-idealG.fi-firG) + + Jl = rhol*(1+deltaL*firdL) + Jv = rhog*(1+deltaG*firdG) + K = firL-firG + x = (1./rho-1/rhol)/(1/rhog-1/rhol) + try: + logrho = log(rhol/rhog) + except ValueError: + logrho = float('nan') + return (Jl-Jv, Jl*(1/rhog-1/rhol)-logrho-K, + soL*(1-x)+soG*x - s) + + for to in [To, 300, 400, 500, 600]: + rhoLo = self._Liquid_Density(to) + rhoGo = self._Vapor_Density(to) + sol = fsolve(f3, [to, rhoLo, rhoGo], full_output=True) + T, rhoL, rhoG = tuple(map(float, sol[0])) + x = (1./rho-1/rhoL)/(1/rhoG-1/rhoL) + if sol[2] == 1 and 0 <= x <= 1 and \ + sum(abs(sol[1]["fvec"])) < 1e-5: + break + + if sum(abs(sol[1]["fvec"])) > 1e-5: + raise(RuntimeError(sol[3])) + + liquido = self._Helmholtz(rhoL, T) + vapor = self._Helmholtz(rhoG, T) + P = self.R*T*rhoL*rhoG/(rhoL-rhoG)*( + liquido.fir-vapor.fir+log(rhoL/rhoG))/1000 + + self.solve_case1(T, P, x, rho, liquido, vapor) + + def solve_rho_u(self, rho: float, u: float, rhoo: float, To: float) -> None: + """Solve for properties using rho & u.""" + delta = rho/rhoc + P = 0.0 + x = None + + def t_func(T: float) -> float: + tau = self._constant_Tref/T + + ideal = self._phi0(tau, delta) + fird = self.residual.phird(tau, delta) + firt = self.residual.phirt(tau, delta) + Po = (1+delta*fird)*self.R*T*rho + ho = self.R*T*(1+tau*(ideal.fit+firt)+delta*fird) + return ho-Po/rho-u + + T = float(fsolve(t_func, To)[0]) + rhol = self._Liquid_Density(T) + rhov = self._Vapor_Density(T) + liquido = None + vapor = None + if T == To or rhov <= rho <= rhol: + def f3(parr: Tuple[float, float, float]) -> Tuple[float, float, float]: + T, rhol, rhog = parr + tau = self._constant_Tref/T + deltaL = rhol/self._constant_rhoc + deltaG = rhog/self._constant_rhoc + + ideal = self._phi0(tau, deltaL) + firL = self.residual.phir(tau, deltaL) + firdL = self.residual.phird(tau, deltaL) + firtL = self.residual.phirt(tau, deltaL) + hoL = self.R*T*(1+tau*(ideal.fit+firtL)+deltaL*firdL) + firG = self.residual.phir(tau, deltaG) + firdG = self.residual.phird(tau, deltaG) + firtG = self.residual.phirt(tau, deltaG) + hoG = self.R*T*(1+tau*(ideal.fit+firtG)+deltaG*firdG) + + Jl = rhol*(1+deltaL*firdL) + Jv = rhog*(1+deltaG*firdG) + K = firL-firG + x = (1./rho-1/rhol)/(1/rhog-1/rhol) + try: + logrho = log(rhol/rhog) + except ValueError: + logrho = float('nan') + Ps = self.R*T*rhol*rhog/(rhol-rhog)*(K+logrho) + vu = hoG-Ps/rhog + lu = hoL-Ps/rhol + return (Jl-Jv, Jl*(1/rhog-1/rhol)-logrho-K, + lu*(1-x)+vu*x - u) + + for to in [To, 300, 400, 500, 600]: + rhoLo = self._Liquid_Density(to) + rhoGo = self._Vapor_Density(to) + sol = fsolve(f3, [to, rhoLo, rhoGo], full_output=True) + T, rhoL, rhoG = tuple(map(float, sol[0])) + x = (1./rho-1/rhoL)/(1/rhoG-1/rhoL) + if sol[2] == 1 and 0 <= x <= 1 and \ + sum(abs(sol[1]["fvec"])) < 1e-5: + break + + if sum(abs(sol[1]["fvec"])) > 1e-5: + raise(RuntimeError(sol[3])) + + liquido = self._Helmholtz(rhoL, T) + vapor = self._Helmholtz(rhoG, T) + P = self.R*T*rhoL*rhoG/(rhoL-rhoG)*( + liquido.fir-vapor.fir+log(rhoL/rhoG))/1000 + + self.solve_case1(T, P, x, rho, liquido, vapor) + + def solve_h_s(self, h: float, s: float, rhoo: float, To: float) -> None: + """Solve for properties using h & s.""" + P = 0.0 + x = None + + def f2(parr: Tuple[float, float]) -> Tuple[float, float]: + rho, T = parr + delta = rho/rhoc + tau = self._constant_Tref/T + + ideal = self._phi0(tau, delta) + fird = self.residual.phird(tau, delta) + fir = self.residual.phir(tau, delta) + firt = self.residual.phirt(tau, delta) + ho = self.R*T*(1+tau*(ideal.fit+firt)+delta*fird) + so = self.R*(tau*(ideal.fit+firt)-ideal.fi-fir) + return ho-h, so-s + + rho, T = tuple(map(float, fsolve(f2, [rhoo, To]))) + rhol = self._Liquid_Density(T) + rhov = self._Vapor_Density(T) + liquido = None + vapor = None + if rhov <= rho <= rhol: + + def f4(parr: Tuple[float, float, float, float]) -> Tuple[ + float, float, float, float]: + T, rhol, rhog, x = parr + tau = self._constant_Tref/T + deltaL = rhol/self._constant_rhoc + deltaG = rhog/self._constant_rhoc + + idealL = self._phi0(tau, deltaL) + idealG = self._phi0(tau, deltaG) + + firL = self.residual.phir(tau, deltaL) + firdL = self.residual.phird(tau, deltaL) + firtL = self.residual.phirt(tau, deltaL) + hoL = self.R*T*(1+tau*(idealL.fit+firtL)+deltaL*firdL) + soL = self.R*(tau*(idealL.fit+firtL)-idealL.fi-firL) + firG = self.residual.phir(tau, deltaG) + firdG = self.residual.phird(tau, deltaG) + firtG = self.residual.phirt(tau, deltaG) + hoG = self.R*T*(1+tau*(idealL.fit+firtG)+deltaG*firdG) + soG = self.R*(tau*(idealL.fit+firtG)-idealG.fi-firG) + + Jl = rhol*(1+deltaL*firdL) + Jv = rhog*(1+deltaG*firdG) + K = firL-firG + return (Jl-Jv, + Jl*(1/rhog-1/rhol)-log(rhol/rhog)-K, + hoL*(1-x)+hoG*x - h, + soL*(1-x)+soG*x - s) + + for to in [To, 300, 400, 500, 600]: + rLo = self._Liquid_Density(to) + rGo = self._Vapor_Density(to) + sol = fsolve(f4, [to, rLo, rGo, 0.5], full_output=True) + T, rhoL, rhoG, x = tuple(map(float, sol[0])) + if sol[2] == 1 and 0 <= x <= 1 and \ + sum(abs(sol[1]["fvec"])) < 1e-5: + break + + if sum(abs(sol[1]["fvec"])) > 1e-5: + raise(RuntimeError(sol[3])) + + liquido = self._Helmholtz(rhoL, T) + vapor = self._Helmholtz(rhoG, T) + P = self.R*T*rhoL*rhoG/(rhoL-rhoG)*( + liquido.fir-vapor.fir+log(rhoL/rhoG))/1000 + + self.solve_case1(T, P, x, rho, liquido, vapor) + + def solve_h_u(self, h: float, u: float, rhoo: float, To: float) -> None: + """Solve for properties using h & u.""" + P = 0.0 + x = None + + def f2(parr: Tuple[float, float]) -> Tuple[float, float]: + rho, T = parr + delta = rho/rhoc + tau = self._constant_Tref/T + + ideal = self._phi0(tau, delta) + fird = self.residual.phird(tau, delta) + firt = self.residual.phirt(tau, delta) + Po = (1+delta*fird)*self.R*T*rho + ho = self.R*T*(1+tau*(ideal.fit+firt)+delta*fird) + + return ho-Po/rho-u, ho-h + + sol = fsolve(f2, [rhoo, To], full_output=True) + rho, T = tuple(map(float, sol[0])) + rhol = self._Liquid_Density(T) + rhov = self._Vapor_Density(T) + liquido = None + vapor = None + if sol[2] != 1 or rhov <= rho <= rhol: + + def f4(parr: Tuple[float, float, float, float]) -> Tuple[ + float, float, float, float]: + T, rhol, rhog, x = parr + tau = self._constant_Tref/T + deltaL = rhol/self._constant_rhoc + deltaG = rhog/self._constant_rhoc + + ideal = self._phi0(tau, deltaL) + + firL = self.residual.phir(tau, deltaL) + firdL = self.residual.phird(tau, deltaL) + firtL = self.residual.phirt(tau, deltaL) + hoL = self.R*T*(1+tau*(ideal.fit+firtL)+deltaL*firdL) + firG = self.residual.phir(tau, deltaG) + firdG = self.residual.phird(tau, deltaG) + firtG = self.residual.phirt(tau, deltaG) + hoG = self.R*T*(1+tau*(ideal.fit+firtG)+deltaG*firdG) + + Jl = rhol*(1+deltaL*firdL) + Jv = rhog*(1+deltaG*firdG) + K = firL-firG + + Ps = self.R*T*rhol*rhog/(rhol-rhog)*(K+log(rhol/rhog)) + vu = hoG-Ps/rhog + lu = hoL-Ps/rhol + + return (Jl-Jv, + Jl*(1/rhog-1/rhol)-log(rhol/rhog)-K, + hoL*(1-x)+hoG*x - h, + lu*(1-x)+vu*x - u) + + for to in [To, 300, 400, 500, 600]: + rLo = self._Liquid_Density(to) + rGo = self._Vapor_Density(to) + sol = fsolve(f4, [to, rLo, rGo, 0.5], full_output=True) + T, rhoL, rhoG, x = tuple(map(float, sol[0])) + if sol[2] == 1 and 0 <= x <= 1 and \ + sum(abs(sol[1]["fvec"])) < 1e-5: + break + + if sum(abs(sol[1]["fvec"])) > 1e-5: + raise(RuntimeError(sol[3])) + + liquido = self._Helmholtz(rhoL, T) + vapor = self._Helmholtz(rhoG, T) + P = self.R*T*rhoL*rhoG/(rhoL-rhoG)*( + liquido.fir-vapor.fir+log(rhoL/rhoG))/1000 + + self.solve_case1(T, P, x, rho, liquido, vapor) + + def solve_s_u(self, s: float, u: float, rhoo: float, To: float) -> None: + """Solve for properties using s & u.""" + P = 0.0 + x = None + + def f2(parr: Tuple[float, float]) -> Tuple[float, float]: + rho, T = parr + delta = rho/rhoc + tau = self._constant_Tref/T + + ideal = self._phi0(tau, delta) + fird = self.residual.phird(tau, delta) + fir = self.residual.phir(tau, delta) + firt = self.residual.phirt(tau, delta) + ho = self.R*T*(1+tau*(ideal.fit+firt)+delta*fird) + so = self.R*(tau*(ideal.fit+firt)-ideal.fi-fir) + Po = (1+delta*fird)*self.R*T*rho + return ho-Po/rho-u, so-s + + sol = fsolve(f2, [rhoo, To], full_output=True) + rho, T = tuple(map(float, sol[0])) + rhol = self._Liquid_Density(T) + rhov = self._Vapor_Density(T) + liquido = None + vapor = None + if sol[2] != 1 or rhov <= rho <= rhol: + + def f4(parr: Tuple[float, float, float, float]) -> Tuple[ + float, float, float, float]: + T, rhol, rhog, x = parr + tau = self._constant_Tref/T + deltaL = rhol/self._constant_rhoc + deltaG = rhog/self._constant_rhoc + + idealL = self._phi0(tau, deltaL) + idealG = self._phi0(tau, deltaG) + + firL = self.residual.phir(tau, deltaL) + firdL = self.residual.phird(tau, deltaL) + firtL = self.residual.phirt(tau, deltaL) + hoL = self.R*T*(1+tau*(idealL.fit+firtL)+deltaL*firdL) + soL = self.R*(tau*(idealL.fit+firtL)-idealL.fi-firL) + firG = self.residual.phir(tau, deltaG) + firdG = self.residual.phird(tau, deltaG) + firtG = self.residual.phirt(tau, deltaG) + hoG = self.R*T*(1+tau*(idealL.fit+firtG)+deltaG*firdG) + soG = self.R*(tau*(idealL.fit+firtG)-idealG.fi-firG) + + Jl = rhol*(1+deltaL*firdL) + Jv = rhog*(1+deltaG*firdG) + K = firL-firG + try: + logrho = log(rhol/rhog) + except ValueError: + logrho = float('nan') + Ps = self.R*T*rhol*rhog/(rhol-rhog)*(K+logrho) + vu = hoG-Ps/rhog + lu = hoL-Ps/rhol + + return (Jl-Jv, Jl*(1/rhog-1/rhol)-logrho-K, + soL*(1-x)+soG*x - s, lu*(1-x)+vu*x - u) + + for to in [To, 300, 400, 500, 600]: + rLo = self._Liquid_Density(to) + rGo = self._Vapor_Density(to) + sol = fsolve(f4, [to, rLo, rGo, 0.5], full_output=True) + T, rhoL, rhoG, x = tuple(map(float, sol[0])) + if sol[2] == 1 and 0 <= x <= 1 and sum(abs(sol[1]["fvec"])) < 1e-5: + break + + if sum(abs(sol[1]["fvec"])) > 1e-5: + raise(RuntimeError(sol[3])) + + liquido = self._Helmholtz(rhoL, T) + vapor = self._Helmholtz(rhoG, T) + P = self.R*T*rhoL*rhoG/(rhoL-rhoG)*( + liquido.fir-vapor.fir+log(rhoL/rhoG))/1000 + + self.solve_case1(T, P, x, rho, liquido, vapor) + + def solve_T_rho(self, T: float, rho: float) -> None: + """Solve for properties using T & rho.""" + P = 0.0 + x = None + liquido = None + vapor = None + if T < self._constant_Tref: + rhov = self._Vapor_Density(T) + rhol = self._Liquid_Density(T) + if rhol > rho > rhov: + rhol, rhov, Ps = self._saturation(T) + if rhol > rho > rhov: + vapor = self._Helmholtz(rhov, T) + liquido = self._Helmholtz(rhol, T) + x = (1/rho-1/rhol)/(1/rhov-1/rhol) + P = Ps/1000 + self.solve_case1(T, P, x, rho, liquido, vapor) + + def solve_case1(self, T: float, P: float, x: Optional[float], rho: float, + liquido: Optional[MEoSProperties], + vapor: Optional[MEoSProperties]) -> None: + """Post processing for most solve cases except Tx and Px.""" + # T, P, rho and x are really sometimes float and sometimes int. + T = float(T) + P = float(P) + rho = float(rho) + if x is not None: + x = float(x) + propiedades = self._Helmholtz(rho, T) + + if T > self._constant_Tref: + x = 1.0 + elif x is None: + x = 0.0 + + if not P: + P = propiedades.P/1000. + self.dofill(T, P, x, propiedades, liquido, vapor) + + def solve_T_x(self, T: float, x: float) -> None: + """Solve for properties using T & x.""" + # Check input T in saturation range + if self.Tt > T or self._constant_Tref < T or x > 1 or x < 0: + raise NotImplementedError("Incoming out of bound") + + rhol, rhov, Ps = self._saturation(T) + vapor = self._Helmholtz(rhov, T) + liquido = self._Helmholtz(rhol, T) + propiedades: Optional[MEoSProperties] = None + if x == 0.0: + propiedades = liquido + elif x == 1.0: + propiedades = vapor + P = Ps/1000.0 + self.dofill(T, P, x, propiedades, liquido, vapor) + + def solve_P_x(self, P: float, x: float, T0: Optional[float]) -> None: + """Solve for properties using P & x.""" + # Check input P in saturation range + if self.Pc < P or x > 1 or x < 0: + raise NotImplementedError("Incoming out of bound") + + # Iterate over saturation routine to get T + def t_func(T: float) -> float: + rhol = self._Liquid_Density(T) + rhog = self._Vapor_Density(T) + + deltaL = rhol/self._constant_rhoc + deltaG = rhog/self._constant_rhoc + + tau = self._constant_Tref/T + + firL = self.residual.phir(tau, deltaL) + firG = self.residual.phir(tau, deltaG) + Ps = self.R*T*rhol*rhog/(rhol-rhog)*( + firL-firG+log(deltaL/deltaG)) + return Ps/1000-P + + if T0: + To = T0 + elif self.name == "water": + To = _TSat_P(P) + else: + To = (self._constant_Tref+self.Tt)/2 + T = float(fsolve(t_func, To)[0]) + rhol, rhov, Ps = self._saturation(T) + vapor = self._Helmholtz(rhov, T) + liquido = self._Helmholtz(rhol, T) + propiedades: Optional[MEoSProperties] = None + if x == 0.0: + propiedades = liquido + elif x == 1.0: + propiedades = vapor + self.dofill(T, P, x, propiedades, liquido, vapor) + + def dofill(self, T, P, x, propiedades, liquido, vapor): + """Finish filling the class.""" + self.status = 1 self.T = T - self.Tr = T/self.Tc + self.Tr = T/self._constant_Tref self.P = P self.Pr = self.P/self.Pc self.x = x @@ -1409,15 +1372,15 @@ def f(T): region = 4 else: region = 0 - self.phase = getphase(self.Tc, self.Pc, self.T, self.P, self.x, region) + self.phase = getphase(self._constant_Tref, self.Pc, self.T, self.P, self.x, region) self.Liquid = _fase() self.Gas = _fase() - if x == 0: + if x == 0.0: # liquid phase self.fill(self.Liquid, propiedades) self.fill(self, propiedades) - elif x == 1: + elif x == 1.0: # vapor phase self.fill(self.Gas, propiedades) self.fill(self, propiedades) @@ -1441,18 +1404,18 @@ def f(T): self.IntP = x*self.Gas.IntP+(1-x)*self.Liquid.IntP # Calculate special properties useful only for one phase - if self._mode in ("Px", "Tx") or (x < 1 and self.Tt <= T <= self.Tc): + if self._mode in ("Px", "Tx") or (x < 1 and self.Tt <= T <= self._constant_Tref): self.sigma = self._surface(T) else: self.sigma = None - vir = self._virial(T) - self.virialB = vir["B"]/self.rhoc - self.virialC = vir["C"]/self.rhoc**2 + vir = self.residual._virial(T, self._constant_Tref) + self.virialB = vir["B"]/self._constant_rhoc + self.virialC = vir["C"]/self._constant_rhoc**2 if 0 < x < 1: - self.Hvap = vapor["h"]-liquido["h"] - self.Svap = vapor["s"]-liquido["s"] + self.Hvap = vapor.h-liquido.h + self.Svap = vapor.s-liquido.s else: self.Hvap = None self.Svap = None @@ -1474,22 +1437,33 @@ def f(T): cp0.v = self.v0 self.gamma0 = -self.v0/self.P/1000*self.derivative("P", "v", "s", cp0) - def fill(self, fase, estado): + # Derived classes must implement _visco to call fill() + def _visco(self, rho: float, T: float, fase: Optional[_fase] = None) -> float: + raise NotImplementedError + + # Derived classes must implement _thermo to call fill() + def _thermo(self, rho: float, T: float, fase: Optional[_fase] = None) -> float: + raise NotImplementedError + + def fill(self, fase: _fase, estado: MEoSProperties) -> None: """Fill phase properties""" - fase.rho = estado["rho"] + fase.rho = estado.rho fase.v = 1/fase.rho - fase.h = estado["h"] - fase.s = estado["s"] + assert(isinstance(self.M, float)) + assert(isinstance(self.R, float)) + + fase.h = estado.h + fase.s = estado.s fase.u = fase.h-self.P*1000*fase.v fase.a = fase.u-self.T*fase.s fase.g = fase.h-self.T*fase.s fase.Z = self.P*fase.v/self.T/self.R*1e3 - fase.fi = exp(estado["fir"]+estado["delta"]*estado["fird"] - - log(1+estado["delta"]*estado["fird"])) + fase.fi = exp(estado.fir+estado.delta*estado.fird + - log(1+estado.delta*estado.fird)) fase.f = fase.fi*self.P - fase.cv = estado["cv"] + fase.cv = estado.cv fase.rhoM = fase.rho/self.M fase.hM = fase.h*self.M @@ -1498,8 +1472,8 @@ def fill(self, fase, estado): fase.aM = fase.a*self.M fase.gM = fase.g*self.M - fase.alfap = estado["alfap"] - fase.betap = estado["betap"] + fase.alfap = estado.alfap + fase.betap = estado.betap fase.cp = self.derivative("h", "T", "P", fase) fase.cp_cv = fase.cp/fase.cv @@ -1538,64 +1512,65 @@ def fill(self, fase, estado): fase.nu = fase.mu/fase.rho fase.alfa = fase.k/1000/fase.rho/fase.cp fase.Prandt = fase.mu*fase.cp*1000/fase.k + fase.epsilon = None + fase.n = None if self.name == "water": try: fase.epsilon = _Dielectric(fase.rho, self.T) except NotImplementedError: fase.epsilon = None - try: - fase.n = _Refractive(fase.rho, self.T, self.kwargs["l"]) - except NotImplementedError: - fase.n = None - else: - fase.epsilon = None - fase.n = None + if self.kwargs["l"] is not None: + try: + fase.n = _Refractive(fase.rho, self.T, self.kwargs["l"]) + except NotImplementedError: + fase.n = None - def derivative(self, z, x, y, fase): - """Wrapper derivative for custom derived properties - where x, y, z can be: P, T, v, rho, u, h, s, g, a""" + def derivative(self, z: str, x: str, y: str, fase: _fase) -> float: + """ + Wrapper derivative for custom derived properties + where x, y, z can be: P, T, v, rho, u, h, s, g, a + """ return deriv_H(self, z, x, y, fase) - def _saturation(self, T): + def _saturation(self, T: float) -> Tuple[float, float, float]: """Saturation calculation for two phase search""" - rhoc = self._constants.get("rhoref", self.rhoc) - Tc = self._constants.get("Tref", self.Tc) + rhoc = self._constant_rhoc - if T > Tc: - T = Tc - tau = Tc/T + if T > self._constant_Tref: + T = self._constant_Tref + tau = self._constant_Tref/T rhoLo = self._Liquid_Density(T) rhoGo = self._Vapor_Density(T) - def f(parr): + def f2(parr: Tuple[float, float]) -> Tuple[float, float]: rhol, rhog = parr deltaL = rhol/rhoc deltaG = rhog/rhoc - phirL = _phir(tau, deltaL, self._constants) - phirG = _phir(tau, deltaG, self._constants) - phirdL = _phird(tau, deltaL, self._constants) - phirdG = _phird(tau, deltaG, self._constants) + phirL = self.residual.phir(tau, deltaL) + phirG = self.residual.phir(tau, deltaG) + phirdL = self.residual.phird(tau, deltaL) + phirdG = self.residual.phird(tau, deltaG) Jl = deltaL*(1+deltaL*phirdL) Jv = deltaG*(1+deltaG*phirdG) Kl = deltaL*phirdL+phirL+log(deltaL) Kv = deltaG*phirdG+phirG+log(deltaG) return Kv-Kl, Jv-Jl - rhoL, rhoG = fsolve(f, [rhoLo, rhoGo]) + rhoL, rhoG = tuple(map(float, fsolve(f2, [rhoLo, rhoGo]))) if rhoL == rhoG: Ps = self.Pc else: - deltaL = rhoL/self.rhoc - deltaG = rhoG/self.rhoc - firL = _phir(tau, deltaL, self._constants) - firG = _phir(tau, deltaG, self._constants) + deltaL = rhoL/self._constant_rhoc + deltaG = rhoG/self._constant_rhoc + firL = self.residual.phir(tau, deltaL) + firG = self.residual.phir(tau, deltaG) Ps = self.R*T*rhoL*rhoG/(rhoL-rhoG)*(firL-firG+log(deltaL/deltaG)) return rhoL, rhoG, Ps - def _Helmholtz(self, rho, T): + def _Helmholtz(self, rho: float, T: float) -> MEoSProperties: """Calculated properties from helmholtz free energy and derivatives Parameters @@ -1627,68 +1602,41 @@ def _Helmholtz(self, rho, T): Scientific Use, September 2016, Table 3 http://www.iapws.org/relguide/IAPWS-95.html """ - if isinstance(rho, ndarray): - rho = rho[0] - if isinstance(T, ndarray): - T = T[0] if rho < 0: rho = 1e-20 if T < 50: T = 50 - rhoc = self._constants.get("rhoref", self.rhoc) - Tc = self._constants.get("Tref", self.Tc) - delta = rho/rhoc - tau = Tc/T + delta = rho/self._constant_rhoc + tau = self._constant_Tref/T ideal = self._phi0(tau, delta) - fio = ideal["fio"] - fiot = ideal["fiot"] - fiott = ideal["fiott"] - - res = self._phir(tau, delta) - fir = res["fir"] - firt = res["firt"] - firtt = res["firtt"] - fird = res["fird"] - firdd = res["firdd"] - firdt = res["firdt"] - - propiedades = {} - propiedades["fir"] = fir - propiedades["fird"] = fird - propiedades["firdd"] = firdd - propiedades["delta"] = delta - - propiedades["rho"] = rho - propiedades["P"] = (1+delta*fird)*self.R*T*rho - propiedades["h"] = self.R*T*(1+tau*(fiot+firt)+delta*fird) - propiedades["s"] = self.R*(tau*(fiot+firt)-fio-fir) - propiedades["cv"] = -self.R*tau**2*(fiott+firtt) - propiedades["alfap"] = (1-delta*tau*firdt/(1+delta*fird))/T - propiedades["betap"] = rho*( - 1+(delta*fird+delta**2*firdd)/(1+delta*fird)) - return propiedades - def _prop0(self, rho, T): + res = self.residual.helmholtz(tau, delta) + + P = (1+delta*res.fid)*self.R*T*rho + h = self.R*T*(1+tau*(ideal.fit+res.fit)+delta*res.fid) + s = self.R*(tau*(ideal.fit+res.fit)-ideal.fi-res.fi) + cv = -self.R*tau**2*(ideal.fitt+res.fitt) + alfap = (1-delta*tau*res.fidt/(1+delta*res.fid))/T + betap = rho*(1+(delta*res.fid+delta**2*res.fidd)/(1+delta*res.fid)) + return MEoSProperties(rho, P, h, s, cv, alfap, betap, + res.fi, res.fid, res.fidd, delta) + + def _prop0(self, rho: float, T: float) -> _fase: """Ideal gas properties""" - rhoc = self._constants.get("rhoref", self.rhoc) - Tc = self._constants.get("Tref", self.Tc) - delta = rho/rhoc - tau = Tc/T + delta = rho/self._constant_rhoc + tau = self._constant_Tref/T ideal = self._phi0(tau, delta) - fio = ideal["fio"] - fiot = ideal["fiot"] - fiott = ideal["fiott"] propiedades = _fase() - propiedades.h = self.R*T*(1+tau*fiot) - propiedades.s = self.R*(tau*fiot-fio) - propiedades.cv = -self.R*tau**2*fiott - propiedades.cp = self.R*(-tau**2*fiott+1) - propiedades.alfap = 1/T + propiedades.h = self.R*T*(1+tau*ideal.fit) + propiedades.s = self.R*(tau*ideal.fit-ideal.fi) + propiedades.cv = -self.R*tau**2*ideal.fitt + propiedades.cp = self.R*(-tau**2*ideal.fitt+1) + propiedades.alfap = 1.0/T propiedades.betap = rho return propiedades - def _phi0(self, tau, delta): + def _phi0(self, tau: float, delta: float) -> HelmholtzDerivatives: """Ideal gas Helmholtz free energy and derivatives Parameters @@ -1717,7 +1665,17 @@ def _phi0(self, tau, delta): """ Fi0 = self.Fi0 - fio = Fi0["ao_log"][0]*log(delta)+Fi0["ao_log"][1]*log(tau) + try: + dlog = log(delta) + except ValueError: + dlog = float('nan') + + try: + tlog = log(tau) + except ValueError: + tlog = float('nan') + + fio = Fi0["ao_log"][0]*dlog+Fi0["ao_log"][1]*tlog fiot = +Fi0["ao_log"][1]/tau fiott = -Fi0["ao_log"][1]/tau**2 @@ -1733,9 +1691,17 @@ def _phi0(self, tau, delta): fiott += n*t*(t-1)*tau**(t-2) for n, t in zip(Fi0["ao_exp"], Fi0["titao"]): - fio += n*log(1-exp(-tau*t)) - fiot += n*t*((1-exp(-t*tau))**-1-1) - fiott -= n*t**2*exp(-t*tau)*(1-exp(-t*tau))**-2 + try: + expt = exp(-t*tau) + except OverflowError: + expt = float('inf') + try: + logterm = log(1-expt) + except ValueError: + logterm = float('nan') + fio += n*logterm + fiot += n*t*((1-expt)**-1-1) + fiott -= n*t**2*expt*(1-expt)**-2 # Extension to especial terms of air if "ao_exp2" in Fi0: @@ -1744,238 +1710,9 @@ def _phi0(self, tau, delta): fiot += n*g/(C*exp(-g*tau)+1) fiott += C*n*g**2*exp(-g*tau)/(C*exp(-g*tau)+1)**2 - prop = {} - prop["fio"] = fio - prop["fiot"] = fiot - prop["fiott"] = fiott - prop["fiod"] = fiod - prop["fiodd"] = fiodd - prop["fiodt"] = fiodt - return prop - - def _phir(self, tau, delta): - """Residual contribution to the free Helmholtz energy + return HelmholtzDerivatives(fio, fiot, fiod, fiott, fiodd, fiodt) - Parameters - ---------- - tau : float - Inverse reduced temperature Tc/T, [-] - delta : float - Reduced density rho/rhoc, [-] - - Returns - ------- - prop : dict - Dictionary with residual adimensional helmholtz energy and deriv: - * fir - * firt: ∂fir/∂τ|δ,x - * fird: ∂fir/∂δ|τ,x - * firtt: ∂²fir/∂τ²|δ,x - * firdt: ∂²fir/∂τ∂δ|x - * firdd: ∂²fir/∂δ²|τ,x - - References - ---------- - IAPWS, Revised Release on the IAPWS Formulation 1995 for the - Thermodynamic Properties of Ordinary Water Substance for General and - Scientific Use, September 2016, Table 5 - http://www.iapws.org/relguide/IAPWS-95.html - """ - fir = fird = firdd = firt = firtt = firdt = 0 - - # Polinomial terms - nr1 = self._constants.get("nr1", []) - d1 = self._constants.get("d1", []) - t1 = self._constants.get("t1", []) - for n, d, t in zip(nr1, d1, t1): - fir += n*delta**d*tau**t - fird += n*d*delta**(d-1)*tau**t - firdd += n*d*(d-1)*delta**(d-2)*tau**t - firt += n*t*delta**d*tau**(t-1) - firtt += n*t*(t-1)*delta**d*tau**(t-2) - firdt += n*t*d*delta**(d-1)*tau**(t-1) - - # Exponential terms - nr2 = self._constants.get("nr2", []) - d2 = self._constants.get("d2", []) - g2 = self._constants.get("gamma2", []) - t2 = self._constants.get("t2", []) - c2 = self._constants.get("c2", []) - for n, d, g, t, c in zip(nr2, d2, g2, t2, c2): - fir += n*delta**d*tau**t*exp(-g*delta**c) - fird += n*exp(-g*delta**c)*delta**(d-1)*tau**t*(d-g*c*delta**c) - firdd += n*exp(-g*delta**c)*delta**(d-2)*tau**t * \ - ((d-g*c*delta**c)*(d-1-g*c*delta**c)-g**2*c**2*delta**c) - firt += n*t*delta**d*tau**(t-1)*exp(-g*delta**c) - firtt += n*t*(t-1)*delta**d*tau**(t-2)*exp(-g*delta**c) - firdt += n*t*delta**(d-1)*tau**(t-1)*(d-g*c*delta**c)*exp( - -g*delta**c) - - # Gaussian terms - nr3 = self._constants.get("nr3", []) - d3 = self._constants.get("d3", []) - t3 = self._constants.get("t3", []) - a3 = self._constants.get("alfa3", []) - e3 = self._constants.get("epsilon3", []) - b3 = self._constants.get("beta3", []) - g3 = self._constants.get("gamma3", []) - for n, d, t, a, e, b, g in zip(nr3, d3, t3, a3, e3, b3, g3): - fir += n*delta**d*tau**t*exp(-a*(delta-e)**2-b*(tau-g)**2) - fird += n*delta**d*tau**t*exp(-a*(delta-e)**2-b*(tau-g)**2)*( - d/delta-2*a*(delta-e)) - firdd += n*tau**t*exp(-a*(delta-e)**2-b*(tau-g)**2)*( - -2*a*delta**d + 4*a**2*delta**d*(delta-e)**2 - - 4*d*a*delta**(d-1)*(delta-e) + d*(d-1)*delta**(d-2)) - firt += n*delta**d*tau**t*exp(-a*(delta-e)**2-b*(tau-g)**2)*( - t/tau-2*b*(tau-g)) - firtt += n*delta**d*tau**t*exp(-a*(delta-e)**2-b*(tau-g)**2)*( - (t/tau-2*b*(tau-g))**2-t/tau**2-2*b) - firdt += n*delta**d*tau**t*exp(-a*(delta-e)**2-b*(tau-g)**2)*( - t/tau-2*b*(tau-g))*(d/delta-2*a*(delta-e)) - - # Non analitic terms - nr4 = self._constants.get("nr4", []) - a4 = self._constants.get("a4", []) - b4 = self._constants.get("b4", []) - Ai = self._constants.get("A", []) - Bi = self._constants.get("B", []) - Ci = self._constants.get("C", []) - Di = self._constants.get("D", []) - bt4 = self._constants.get("beta4", []) - for n, a, b, A, B, C, D, bt in zip(nr4, a4, b4, Ai, Bi, Ci, Di, bt4): - Tita = (1-tau)+A*((delta-1)**2)**(0.5/bt) - F = exp(-C*(delta-1)**2-D*(tau-1)**2) - Fd = -2*C*F*(delta-1) - Fdd = 2*C*F*(2*C*(delta-1)**2-1) - Ft = -2*D*F*(tau-1) - Ftt = 2*D*F*(2*D*(tau-1)**2-1) - Fdt = 4*C*D*F*(delta-1)*(tau-1) - - Delta = Tita**2+B*((delta-1)**2)**a - Deltad = (delta-1)*(A*Tita*2/bt*((delta-1)**2)**(0.5/bt-1) + - 2*B*a*((delta-1)**2)**(a-1)) - if delta == 1: - Deltadd = 0 - else: - Deltadd = Deltad/(delta-1)+(delta-1)**2*( - 4*B*a*(a-1)*((delta-1)**2)**(a-2) + - 2*A**2/bt**2*(((delta-1)**2)**(0.5/bt-1))**2 + - A*Tita*4/bt*(0.5/bt-1)*((delta-1)**2)**(0.5/bt-2)) - - DeltaBd = b*Delta**(b-1)*Deltad - DeltaBdd = b*(Delta**(b-1)*Deltadd+(b-1)*Delta**(b-2)*Deltad**2) - DeltaBt = -2*Tita*b*Delta**(b-1) - DeltaBtt = 2*b*Delta**(b-1)+4*Tita**2*b*(b-1)*Delta**(b-2) - DeltaBdt = -A*b*2/bt*Delta**(b-1)*(delta-1)*((delta-1)**2)**( - 0.5/bt-1)-2*Tita*b*(b-1)*Delta**(b-2)*Deltad - - fir += n*Delta**b*delta*F - fird += n*(Delta**b*(F+delta*Fd)+DeltaBd*delta*F) - firdd += n*(Delta**b*(2*Fd+delta*Fdd) + 2*DeltaBd*(F+delta*Fd) + - DeltaBdd*delta*F) - firt += n*delta*(DeltaBt*F+Delta**b*Ft) - firtt += n*delta*(DeltaBtt*F+2*DeltaBt*Ft+Delta**b*Ftt) - firdt += n*(Delta**b*(Ft+delta*Fdt)+delta*DeltaBd*Ft + - DeltaBt*(F+delta*Fd)+DeltaBdt*delta*F) - - prop = {} - prop["fir"] = fir - prop["firt"] = firt - prop["firtt"] = firtt - prop["fird"] = fird - prop["firdd"] = firdd - prop["firdt"] = firdt - return prop - - def _virial(self, T): - """Virial coefficient - - Parameters - ---------- - T : float - Temperature [K] - - Returns - ------- - prop : dict - Dictionary with residual adimensional helmholtz energy: - * B: ∂fir/∂δ|δ->0 - * C: ∂²fir/∂δ²|δ->0 - """ - Tc = self._constants.get("Tref", self.Tc) - tau = Tc/T - B = C = 0 - delta = 1e-200 - - # Polinomial terms - nr1 = self._constants.get("nr1", []) - d1 = self._constants.get("d1", []) - t1 = self._constants.get("t1", []) - for n, d, t in zip(nr1, d1, t1): - B += n*d*delta**(d-1)*tau**t - C += n*d*(d-1)*delta**(d-2)*tau**t - - # Exponential terms - nr2 = self._constants.get("nr2", []) - d2 = self._constants.get("d2", []) - g2 = self._constants.get("gamma2", []) - t2 = self._constants.get("t2", []) - c2 = self._constants.get("c2", []) - for n, d, g, t, c in zip(nr2, d2, g2, t2, c2): - B += n*exp(-g*delta**c)*delta**(d-1)*tau**t*(d-g*c*delta**c) - C += n*exp(-g*delta**c)*(delta**(d-2)*tau**t*( - (d-g*c*delta**c)*(d-1-g*c*delta**c)-g**2*c**2*delta**c)) - - # Gaussian terms - nr3 = self._constants.get("nr3", []) - d3 = self._constants.get("d3", []) - t3 = self._constants.get("t3", []) - a3 = self._constants.get("alfa3", []) - e3 = self._constants.get("epsilon3", []) - b3 = self._constants.get("beta3", []) - g3 = self._constants.get("gamma3", []) - for n, d, t, a, e, b, g in zip(nr3, d3, t3, a3, e3, b3, g3): - B += n*delta**d*tau**t*exp(-a*(delta-e)**2-b*(tau-g)**2)*( - d/delta-2*a*(delta-e)) - C += n*tau**t*exp(-a*(delta-e)**2-b*(tau-g)**2)*( - -2*a*delta**d+4*a**2*delta**d*( - delta-e)**2-4*d*a*delta**2*( - delta-e)+d*2*delta) - - # Non analitic terms - nr4 = self._constants.get("nr4", []) - a4 = self._constants.get("a4", []) - b4 = self._constants.get("b4", []) - Ai = self._constants.get("A", []) - Bi = self._constants.get("B", []) - Ci = self._constants.get("C", []) - Di = self._constants.get("D", []) - bt4 = self._constants.get("beta4", []) - for n, a, b, A, B_, C_, D, bt in zip(nr4, a4, b4, Ai, Bi, Ci, Di, bt4): - Tita = (1-tau)+A*((delta-1)**2)**(0.5/bt) - Delta = Tita**2+B_*((delta-1)**2)**a - Deltad = (delta-1)*(A*Tita*2/bt*((delta-1)**2)**( - 0.5/bt-1)+2*B_*a*((delta-1)**2)**(a-1)) - Deltadd = Deltad/(delta-1) + (delta-1)**2*( - 4*B_*a*(a-1)*((delta-1)**2)**(a-2) + - 2*A**2/bt**2*(((delta-1)**2)**(0.5/bt-1))**2 + - A*Tita*4/bt*(0.5/bt-1)*((delta-1)**2)**(0.5/bt-2)) - DeltaBd = b*Delta**(b-1)*Deltad - DeltaBdd = b*(Delta**(b-1)*Deltadd+(b-1)*Delta**(b-2)*Deltad**2) - F = exp(-C_*(delta-1)**2-D*(tau-1)**2) - Fd = -2*C_*F*(delta-1) - Fdd = 2*C_*F*(2*C_*(delta-1)**2-1) - - B += n*(Delta**b*(F+delta*Fd)+DeltaBd*delta*F) - C += n*(Delta**b*(2*Fd+delta*Fdd)+2*DeltaBd*(F+delta*Fd) + - DeltaBdd*delta*F) - - prop = {} - prop["B"] = B - prop["C"] = C - return prop - - def _derivDimensional(self, rho, T): + def _derivDimensional(self, rho: float, T: float) -> HelmholtzDerivatives: """Calcule the dimensional form or Helmholtz free energy derivatives Parameters @@ -2005,46 +1742,28 @@ def _derivDimensional(self, rho, T): http://www.iapws.org/relguide/SeaAir.html """ if not rho: - prop = {} - prop["fir"] = 0 - prop["firt"] = 0 - prop["fird"] = 0 - prop["firtt"] = 0 - prop["firdt"] = 0 - prop["firdd"] = 0 - return prop - - R = self._constants.get("R")/self._constants.get("M", self.M) - rhoc = self._constants.get("rhoref", self.rhoc) - Tc = self._constants.get("Tref", self.Tc) + return HelmholtzDerivatives(0.0, 0.0, 0.0, 0.0, 0.0, 0.0) + + rhoc = self._constant_rhoc + Tc = self._constant_Tref delta = rho/rhoc tau = Tc/T ideal = self._phi0(tau, delta) - fio = ideal["fio"] - fiot = ideal["fiot"] - fiott = ideal["fiott"] - fiod = ideal["fiod"] - fiodd = ideal["fiodd"] - - res = self._phir(tau, delta) - fir = res["fir"] - firt = res["firt"] - firtt = res["firtt"] - fird = res["fird"] - firdd = res["firdd"] - firdt = res["firdt"] - - prop = {} - prop["fir"] = R*T*(fio+fir) - prop["firt"] = R*(fio+fir-(fiot+firt)*tau) - prop["fird"] = R*T/rhoc*(fiod+fird) - prop["firtt"] = R*tau**2/T*(fiott+firtt) - prop["firdt"] = R/rhoc*(fiod+fird-firdt*tau) - prop["firdd"] = R*T/rhoc**2*(fiodd+firdd) - return prop - def _surface(self, T): + res = self.residual.helmholtz(tau, delta) + + R = self.R + + fir = R*T*(ideal.fi+res.fi) + firt = R*(ideal.fi+res.fi-(ideal.fit+res.fit)*tau) + fird = R*T/rhoc*(ideal.fid+res.fid) + firtt = R*tau**2/T*(ideal.fitt+res.fitt) + firdt = R/rhoc*(ideal.fid+res.fid-res.fidt*tau) + firdd = R*T/rhoc**2*(ideal.fidd+res.fidd) + return HelmholtzDerivatives(fir, firt, fird, firtt, firdd, firdt) + + def _surface(self, T: float) -> float: """Generic equation for the surface tension Parameters @@ -2063,14 +1782,14 @@ def _surface(self, T): sigma: coefficient exp: exponent """ - tau = 1-T/self.Tc - sigma = 0 + tau = 1-T/self._constant_Tref + sigma = 0.0 for n, t in zip(self._surf["sigma"], self._surf["exp"]): sigma += n*tau**t return sigma @classmethod - def _Vapor_Pressure(cls, T): + def _Vapor_Pressure(cls, T: float) -> float: """Auxiliary equation for the vapour pressure Parameters @@ -2090,15 +1809,15 @@ def _Vapor_Pressure(cls, T): http://www.iapws.org/relguide/Supp-sat.html, Eq.1 """ Tita = 1-T/cls.Tc - suma = 0 - for n, x in zip(cls._Pv["ao"], cls._Pv["exp"]): + suma = 0.0 + for n, x in zip(cls._Pv_ao, cls._Pv_exp): suma += n*Tita**x Pr = exp(cls.Tc/T*suma) Pv = Pr*cls.Pc return Pv @classmethod - def _Liquid_Density(cls, T): + def _Liquid_Density(cls, T: float) -> float: """Auxiliary equation for the density of saturated liquid Parameters @@ -2117,19 +1836,20 @@ def _Liquid_Density(cls, T): Ordinary Water Substance September 1992, http://www.iapws.org/relguide/Supp-sat.html, Eq.2 """ - eq = cls._rhoL["eq"] + # Convert numpy.Float64 back into Python floats. + T = float(T) Tita = 1-T/cls.Tc - if eq == 2: + if cls._rhoL_eq == 2: Tita = Tita**(1./3) - suma = 0 - for n, x in zip(cls._rhoL["ao"], cls._rhoL["exp"]): + suma = 0.0 + for n, x in zip(cls._rhoL_ao, cls._rhoL_exp): suma += n*Tita**x Pr = suma+1 rho = Pr*cls.rhoc return rho @classmethod - def _Vapor_Density(cls, T): + def _Vapor_Density(cls, T: float) -> float: """Auxiliary equation for the density of saturated vapor Parameters @@ -2148,19 +1868,23 @@ def _Vapor_Density(cls, T): Ordinary Water Substance September 1992, http://www.iapws.org/relguide/Supp-sat.html, Eq.3 """ - eq = cls._rhoG["eq"] + # Convert numpy.Float64 back into Python floats. + T = float(T) Tita = 1-T/cls.Tc - if eq == 4: + if cls._rhoG_eq == 4: Tita = Tita**(1./3) - suma = 0 - for n, x in zip(cls._rhoG["ao"], cls._rhoG["exp"]): + suma = 0.0 + for n, x in zip(cls._rhoG_ao, cls._rhoG_exp): suma += n*Tita**x + if type(suma) == complex: + suma = float('nan') + Pr = exp(suma) rho = Pr*cls.rhoc return rho @classmethod - def _dPdT_sat(cls, T): + def _dPdT_sat(cls, T: float) -> float: """Auxiliary equation for the dP/dT along saturation line Parameters @@ -2180,9 +1904,9 @@ def _dPdT_sat(cls, T): http://www.iapws.org/relguide/Supp-sat.html, derived from Eq.1 """ Tita = 1-T/cls.Tc - suma1 = 0 - suma2 = 0 - for n, x in zip(cls._Pv["ao"], cls._Pv["exp"]): + suma1 = 0.0 + suma2 = 0.0 + for n, x in zip(cls._Pv_ao, cls._Pv_exp): suma1 -= n*x*Tita**(x-1)/cls.Tc suma2 += n*Tita**x Pr = (cls.Tc*suma1/T-cls.Tc/T**2*suma2)*exp(cls.Tc/T*suma2) @@ -2190,15 +1914,18 @@ def _dPdT_sat(cls, T): return dPdT -def mainClassDoc(): - """Function decorator used to automatic adiction of base class MEoS in - subclass __doc__""" - def decorator(f): +def mainClassDoc() -> Callable[[Callable[..., Any]], Callable[..., Any]]: + """ + Function decorator used to automatic adiction of base class MEoS in + subclass __doc__ + """ + def decorator(f: Callable[..., Any]) -> Callable[..., Any]: # __doc__ is only writable in python3. - # The doc build must be done with python3 so this snnippet do the work + # The doc build must be done with python3 so this snippet do the work py_version = platform.python_version() if py_version[0] == "3": - doc = f.__doc__.split(os.linesep) + if isinstance(f.__doc__, str): + doc = f.__doc__.split(os.linesep) try: ind = doc.index("") except ValueError: @@ -2206,7 +1933,8 @@ def decorator(f): doc1 = os.linesep.join(doc[:ind]) doc3 = os.linesep.join(doc[ind:]) - doc2 = os.linesep.join(MEoS.__doc__.split(os.linesep)[3:]) + if isinstance(MEoS.__doc__, str): + doc2 = os.linesep.join(MEoS.__doc__.split(os.linesep)[3:]) f.__doc__ = doc1 + os.linesep + os.linesep + \ doc2 + os.linesep + os.linesep + doc3 @@ -2325,6 +2053,7 @@ class IAPWS95(MEoS): IAPWS, Revised Advisory Note No. 3: Thermodynamic Derivatives from IAPWS Formulations, http://www.iapws.org/relguide/Advise3.pdf """ + name = "water" CASNumber = "7732-18-5" formula = "H2O" @@ -2332,111 +2061,112 @@ class IAPWS95(MEoS): Tc = Tc rhoc = rhoc Pc = Pc - M = M + M = _global_M Tt = 273.16 Tb = 373.1243 f_acent = 0.3443 momentoDipolar = 1.855 - Fi0 = {"ao_log": [1, 3.00632], - "pow": [0, 1], + Fi0 = {"ao_log": [1.0, 3.00632], + "pow": [0.0, 1.0], "ao_pow": [-8.3204464837497, 6.6832105275932], "ao_exp": [0.012436, 0.97315, 1.2795, 0.96956, 0.24873], "titao": [1.28728967, 3.53734222, 7.74073708, 9.24437796, 27.5075105]} - _constants = { - "R": 8.314371357587, - - "nr1": [0.12533547935523e-1, 0.78957634722828e1, -0.87803203303561e1, - 0.31802509345418, -0.26145533859358, -0.78199751687981e-2, - 0.88089493102134e-2], - "d1": [1, 1, 1, 2, 2, 3, 4], - "t1": [-0.5, 0.875, 1, 0.5, 0.75, 0.375, 1], - - "nr2": [-0.66856572307965, 0.20433810950965, -0.66212605039687e-4, - -0.19232721156002, -0.25709043003438, 0.16074868486251, - -0.4009282892587e-1, 0.39343422603254e-6, -0.75941377088144e-5, - 0.56250979351888e-3, -0.15608652257135e-4, 0.11537996422951e-8, - .36582165144204e-6, -.13251180074668e-11, -.62639586912454e-9, - -0.10793600908932, 0.17611491008752e-1, 0.22132295167546, - -0.40247669763528, 0.58083399985759, 0.49969146990806e-2, - -0.31358700712549e-1, -0.74315929710341, 0.47807329915480, - 0.20527940895948e-1, -0.13636435110343, 0.14180634400617e-1, - 0.83326504880713e-2, -0.29052336009585e-1, 0.38615085574206e-1, - -0.20393486513704e-1, -0.16554050063734e-2, .19955571979541e-2, - 0.15870308324157e-3, -0.16388568342530e-4, 0.43613615723811e-1, - 0.34994005463765e-1, -0.76788197844621e-1, 0.22446277332006e-1, - -0.62689710414685e-4, -0.55711118565645e-9, -0.19905718354408, - 0.31777497330738, -0.11841182425981], - "c2": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 6, 6, - 6, 6], - "d2": [1, 1, 1, 2, 2, 3, 4, 4, 5, 7, 9, 10, 11, 13, 15, 1, 2, 2, 2, 3, - 4, 4, 4, 5, 6, 6, 7, 9, 9, 9, 9, 9, 10, 10, 12, 3, 4, 4, 5, 14, - 3, 6, 6, 6], - "t2": [4, 6, 12, 1, 5, 4, 2, 13, 9, 3, 4, 11, 4, 13, 1, 7, 1, 9, 10, - 10, 3, 7, 10, 10, 6, 10, 10, 1, 2, 3, 4, 8, 6, 9, 8, 16, 22, 23, - 23, 10, 50, 44, 46, 50], - "gamma2": [1]*44, - - "nr3": [-0.31306260323435e2, 0.31546140237781e2, -0.25213154341695e4], - "d3": [3]*3, - "t3": [0, 1, 4], - "alfa3": [20]*3, - "beta3": [150, 150, 250], - "gamma3": [1.21, 1.21, 1.25], - "epsilon3": [1.]*3, - - "nr4": [-0.14874640856724, 0.31806110878444], - "a4": [3.5, 3.5], - "b4": [0.85, 0.95], - "B": [0.2, 0.2], - "C": [28, 32], - "D": [700, 800], - "A": [0.32, .32], - "beta4": [0.3, 0.3]} - - _Pv = { - "ao": [-7.85951783, 1.84408259, -11.7866497, 22.6807411, -15.9618719, - 1.80122502], - "exp": [1, 1.5, 3, 3.5, 4, 7.5]} - _rhoL = { - "eq": 2, - "ao": [1.99274064, 1.09965342, -0.510839303, -1.75493479, -45.5170352, - -6.74694450e5], - "exp": [1, 2, 5, 16, 43, 110]} - _rhoG = { - "eq": 4, - "ao": [-2.0315024, -2.6830294, -5.38626492, -17.2991605, -44.7586581, - -63.9201063], - "exp": [1, 2, 4, 9, 18.5, 35.5]} - - def _phi0(self, tau, delta): + _constant_R = 8.314371357587 + _constant_rhoc = rhoc + _constant_Tref = Tc + residual = ResidualContribution( + nr1=[0.12533547935523e-1, 0.78957634722828e1, -0.87803203303561e1, + 0.31802509345418, -0.26145533859358, -0.78199751687981e-2, + 0.88089493102134e-2], + d1=[1, 1, 1, 2, 2, 3, 4], + t1=[-0.5, 0.875, 1, 0.5, 0.75, 0.375, 1], + + nr2=[-0.66856572307965, 0.20433810950965, -0.66212605039687e-4, + -0.19232721156002, -0.25709043003438, 0.16074868486251, + -0.4009282892587e-1, 0.39343422603254e-6, -0.75941377088144e-5, + 0.56250979351888e-3, -0.15608652257135e-4, 0.11537996422951e-8, + .36582165144204e-6, -.13251180074668e-11, -.62639586912454e-9, + -0.10793600908932, 0.17611491008752e-1, 0.22132295167546, + -0.40247669763528, 0.58083399985759, 0.49969146990806e-2, + -0.31358700712549e-1, -0.74315929710341, 0.47807329915480, + 0.20527940895948e-1, -0.13636435110343, 0.14180634400617e-1, + 0.83326504880713e-2, -0.29052336009585e-1, 0.38615085574206e-1, + -0.20393486513704e-1, -0.16554050063734e-2, .19955571979541e-2, + 0.15870308324157e-3, -0.16388568342530e-4, 0.43613615723811e-1, + 0.34994005463765e-1, -0.76788197844621e-1, 0.22446277332006e-1, + -0.62689710414685e-4, -0.55711118565645e-9, -0.19905718354408, + 0.31777497330738, -0.11841182425981], + c2=[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 6, 6, + 6, 6], + d2=[1, 1, 1, 2, 2, 3, 4, 4, 5, 7, 9, 10, 11, 13, 15, 1, 2, 2, 2, 3, + 4, 4, 4, 5, 6, 6, 7, 9, 9, 9, 9, 9, 10, 10, 12, 3, 4, 4, 5, 14, + 3, 6, 6, 6], + t2=[4, 6, 12, 1, 5, 4, 2, 13, 9, 3, 4, 11, 4, 13, 1, 7, 1, 9, 10, + 10, 3, 7, 10, 10, 6, 10, 10, 1, 2, 3, 4, 8, 6, 9, 8, 16, 22, 23, + 23, 10, 50, 44, 46, 50], + + nr3=[-0.31306260323435e2, 0.31546140237781e2, -0.25213154341695e4], + d3=[3]*3, + t3=[0, 1, 4], + alfa3=[20]*3, + beta3=[150, 150, 250], + gamma3=[1.21, 1.21, 1.25], + epsilon3=[1.]*3, + + nr4=[-0.14874640856724, 0.31806110878444], + a4=[3.5, 3.5], + b4=[0.85, 0.95], + B=[0.2, 0.2], + C=[28, 32], + D=[700, 800], + A=[0.32, .32], + beta4=[0.3, 0.3], + ) + + _Pv_ao = [-7.85951783, 1.84408259, -11.7866497, 22.6807411, -15.9618719, + 1.80122502] + _Pv_exp = [1.0, 1.5, 3.0, 3.5, 4.0, 7.5] + _rhoL_eq = 2 + _rhoL_ao = [1.99274064, 1.09965342, -0.510839303, -1.75493479, -45.5170352, + -6.74694450e5] + _rhoL_exp = [1.0, 2.0, 5.0, 16.0, 43.0, 110] + _rhoG_eq = 4 + _rhoG_ao = [-2.0315024, -2.6830294, -5.38626492, -17.2991605, -44.7586581, + -63.9201063] + _rhoG_exp = [1.0, 2.0, 4.0, 9.0, 18.5, 35.5] + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + def _phi0(self, tau: float, delta: float) -> HelmholtzDerivatives: """Low temperature extension of the IAPWS-95""" prop = MEoS._phi0(self, tau, delta) - T = self.Tc/tau + T = self._constant_Tref/tau if 50 <= T < 130: fex, fext, fextt = self._phiex(T) - prop["fio"] += fex - prop["fiot"] += fext - prop["fiott"] += fextt + prop.fi += fex + prop.fit += fext + prop.fitt += fextt return prop - def _phiex(self, T): + def _phiex(self, T: float) -> Tuple[float, float, float]: """Low temperature extension""" - tau = self.Tc/T + tau = self._constant_Tref/T E = 0.278296458178592 - ep = self.Tc/130 - fex = E*(-1/2/tau-3/ep**2*(tau+ep)*log(tau/ep)-9/2/ep+9*tau/2/ep**2 + - tau**2/2/ep**3) + ep = self._constant_Tref/130 + fex = E*(-1/2/tau-3/ep**2*(tau+ep)*log(tau/ep)-9/2/ep+9*tau/2/ep**2 + + tau**2/2/ep**3) fext = E*(1/2/tau**2-3/tau/ep-3/ep**2*log(tau/ep)+3/2/ep**2+tau/ep**3) fextt = E*(-1/tau+1/ep)**3 return fex, fext, fextt @classmethod - def _alfa_sat(cls, T): + def _alfa_sat(cls, T: float) -> float: """Auxiliary equation for the alfa coefficient for calculate the enthalpy along the saturation line @@ -2460,13 +2190,13 @@ def _alfa_sat(cls, T): -135.003439, 0.981825814] expi = [0, -19, 1, 4.5, 5, 54.5] Tita = T/cls.Tc - alfa = 0 + alfa = 0.0 for d, x in zip(di, expi): alfa += d*Tita**x return alfa @classmethod - def _phi_sat(cls, T): + def _phi_sat(cls, T: float) -> float: """Auxiliary equation for the phi coefficient for calculate the entropy along the saturation line @@ -2490,7 +2220,7 @@ def _phi_sat(cls, T): -135.003439*5/4, 0.981825814*109/107] expi = [0, -20, None, 3.5, 4, 53.5] Tita = T/cls.Tc - suma = 0 + suma = 0.0 for d, x in zip(di, expi): if x is None: suma += d*log(Tita) @@ -2500,7 +2230,7 @@ def _phi_sat(cls, T): return phi @classmethod - def _Liquid_Enthalpy(cls, T): + def _Liquid_Enthalpy(cls, T: float) -> float: """Auxiliary equation for the specific enthalpy for saturated liquid Parameters @@ -2526,7 +2256,7 @@ def _Liquid_Enthalpy(cls, T): return h @classmethod - def _Vapor_Enthalpy(cls, T): + def _Vapor_Enthalpy(cls, T: float) -> float: """Auxiliary equation for the specific enthalpy for saturated vapor Parameters @@ -2552,7 +2282,7 @@ def _Vapor_Enthalpy(cls, T): return h @classmethod - def _Liquid_Entropy(cls, T): + def _Liquid_Entropy(cls, T: float) -> float: """Auxiliary equation for the specific entropy for saturated liquid Parameters @@ -2578,7 +2308,7 @@ def _Liquid_Entropy(cls, T): return s @classmethod - def _Vapor_Entropy(cls, T): + def _Vapor_Entropy(cls, T: float) -> float: """Auxiliary equation for the specific entropy for saturated vapor Parameters @@ -2603,52 +2333,57 @@ def _Vapor_Entropy(cls, T): s = phi+dpdT/rho*1000 return s - def _visco(self, rho, T, fase): + def _visco(self, rho: float, T: float, fase: Optional[_fase] = None) -> float: ref = IAPWS95() st = ref._Helmholtz(rho, 1.5*Tc) delta = rho/rhoc - drho = 1e3/self.R/1.5/Tc/(1+2*delta*st["fird"]+delta**2*st["firdd"]) + drho = 1e3/self.R/1.5/Tc/(1+2*delta*st.fird+delta**2*st.firdd) return _Viscosity(rho, T, fase, drho) - def _thermo(self, rho, T, fase): + def _thermo(self, rho: float, T: float, fase: Optional[_fase] = None) -> float: ref = IAPWS95() st = ref._Helmholtz(rho, 1.5*Tc) delta = rho/rhoc - drho = 1e3/self.R/1.5/Tc/(1+2*delta*st["fird"]+delta**2*st["firdd"]) + drho = 1e3/self.R/1.5/Tc/(1+2*delta*st.fird+delta**2*st.firdd) return _ThCond(rho, T, fase, drho) - def _surface(self, T): + def _surface(self, T: float) -> float: s = _Tension(T) return s class IAPWS95_PT(IAPWS95): """Derivated class for direct P and T input""" - def __init__(self, P, T): + + def __init__(self, P: float, T: float): IAPWS95.__init__(self, T=T, P=P) class IAPWS95_Ph(IAPWS95): """Derivated class for direct P and h input""" - def __init__(self, P, h): + + def __init__(self, P: float, h: float): IAPWS95.__init__(self, P=P, h=h) class IAPWS95_Ps(IAPWS95): """Derivated class for direct P and s input""" - def __init__(self, P, s): + + def __init__(self, P: float, s: float): IAPWS95.__init__(self, P=P, s=s) class IAPWS95_Px(IAPWS95): """Derivated class for direct P and v input""" - def __init__(self, P, x): + + def __init__(self, P: float, x): IAPWS95.__init__(self, P=P, x=x) class IAPWS95_Tx(IAPWS95): """Derivated class for direct T and x input""" - def __init__(self, T, x): + + def __init__(self, T: float, x): IAPWS95.__init__(self, T=T, x=x) @@ -2670,6 +2405,7 @@ class D2O(MEoS): IAPWS, Revised Advisory Note No. 3: Thermodynamic Derivatives from IAPWS Formulations, http://www.iapws.org/relguide/Advise3.pdf """ + name = "heavy water" CASNumber = "7789-20-0" formula = "D2O" @@ -2683,62 +2419,62 @@ class D2O(MEoS): f_acent = 0.364 momentoDipolar = 1.9 - Fi0 = {"ao_log": [1, 3], - "pow": [0, 1], + Fi0 = {"ao_log": [1.0, 3.0], + "pow": [0.0, 1.0], "ao_pow": [-8.670994022646, 6.96033578458778], "ao_exp": [0.010633, 0.99787, 2.1483, 0.3549], - "titao": [308/Tc, 1695/Tc, 3949/Tc, 10317/Tc], + "titao": [308.0/Tc, 1695.0/Tc, 3949.0/Tc, 10317.0/Tc], "ao_hyp": [], "hyp": []} - _constants = { - "R": 8.3144598, - - "nr1": [0.122082060e-1, 0.296956870e1, -0.379004540e1, 0.941089600, - -0.922466250, -0.139604190e-1], - "d1": [4, 1, 1, 2, 2, 3], - "t1": [1.0000, 0.6555, 0.9369, 0.5610, 0.7017, 1.0672], - - "nr2": [-0.125203570, -0.555391500e1, -0.493009740e1, -0.359470240e-1, - -0.936172870e1, -0.691835150], - "c2": [1, 2, 2, 1, 2, 2], - "d2": [1, 1, 3, 2, 2, 1], - "t2": [3.9515, 4.6000, 5.1590, 0.2000, 5.4644, 2.3660], - "gamma2": [1]*6, - - "nr3": [-0.456110600e-1, -0.224513300e1, 0.860006070e1, -0.248410420e1, - 0.164476900e2, 0.270393360e1, 0.375637470e2, -0.177607760e1, - 0.220924640e1, 0.519652000e1, 0.421097400, -0.391921100], - "t3": [3.4553, 1.4150, 1.5745, 3.4540, 3.8106, 4.8950, 1.4300, 1.5870, - 3.7900, 2.6200, 1.9000, 4.3200], - "d3": [1, 3, 1, 3, 1, 1, 2, 2, 2, 1, 1, 1], - "alfa3": [0.6014, 1.4723, 1.5305, 2.4297, 1.3086, 1.3528, 3.4456, - 1.2645, 2.5547, 1.2148, 18.738, 18.677], - "beta3": [0.4200, 2.4318, 1.2888, 8.2710, 0.3673, 0.9504, 7.8318, - 3.3281, 7.1753, 0.9465, 1177.0, 1167.0], - "epsilon3": [1.8663, 0.2895, 0.5803, 0.2236, 0.6815, 0.9495, 1.1158, - 0.1607, 0.4144, 0.9683, 0.9488, 0.9487], - "gamma3": [1.5414, 1.3794, 1.7385, 1.3045, 2.7242, 3.5321, 2.4552, - 0.8319, 1.3500, 2.5617, 1.0491, 1.0486]} - - _Pv = { - "ao": [-0.80236e1, 0.23957e1, -0.42639e2, 0.99569e2, -0.62135e2], - "exp": [1.0, 1.5, 2.75, 3.0, 3.2]} - _rhoL = { - "eq": 1, - "ao": [0.26406e1, 0.97090e1, -0.18058e2, 0.87202e1, -0.74487e1], - "exp": [0.3678, 1.9, 2.2, 2.63, 7.3]} - _rhoG = { - "eq": 3, - "ao": [-0.37651e1, -0.38673e2, 0.73024e2, -0.13251e3, 0.75235e2, - -0.70412e2], - "exp": [0.409, 1.766, 2.24, 3.04, 3.42, 6.9]} - - def _visco(self, rho, T, fase): + _constant_R = 8.3144598 + _constant_rhoc = rhoc_D2O + _constant_Tref = Tc_D2O + residual = ResidualContribution( + nr1=[0.122082060e-1, 0.296956870e1, -0.379004540e1, 0.941089600, + -0.922466250, -0.139604190e-1], + d1=[4, 1, 1, 2, 2, 3], + t1=[1.0000, 0.6555, 0.9369, 0.5610, 0.7017, 1.0672], + nr2=[-0.125203570, -0.555391500e1, -0.493009740e1, -0.359470240e-1, + -0.936172870e1, -0.691835150], + c2=[1, 2, 2, 1, 2, 2], + d2=[1, 1, 3, 2, 2, 1], + t2=[3.9515, 4.6000, 5.1590, 0.2000, 5.4644, 2.3660], + + nr3=[-0.456110600e-1, -0.224513300e1, 0.860006070e1, -0.248410420e1, + 0.164476900e2, 0.270393360e1, 0.375637470e2, -0.177607760e1, + 0.220924640e1, 0.519652000e1, 0.421097400, -0.391921100], + t3=[3.4553, 1.4150, 1.5745, 3.4540, 3.8106, 4.8950, 1.4300, 1.5870, + 3.7900, 2.6200, 1.9000, 4.3200], + d3=[1, 3, 1, 3, 1, 1, 2, 2, 2, 1, 1, 1], + alfa3=[0.6014, 1.4723, 1.5305, 2.4297, 1.3086, 1.3528, 3.4456, + 1.2645, 2.5547, 1.2148, 18.738, 18.677], + beta3=[0.4200, 2.4318, 1.2888, 8.2710, 0.3673, 0.9504, 7.8318, + 3.3281, 7.1753, 0.9465, 1177.0, 1167.0], + epsilon3=[1.8663, 0.2895, 0.5803, 0.2236, 0.6815, 0.9495, 1.1158, + 0.1607, 0.4144, 0.9683, 0.9488, 0.9487], + gamma3=[1.5414, 1.3794, 1.7385, 1.3045, 2.7242, 3.5321, 2.4552, + 0.8319, 1.3500, 2.5617, 1.0491, 1.0486], + nr4=[], a4=[], b4=[], A=[], B=[], C=[], D=[], beta4=[], + ) + + _Pv_ao = [-0.80236e1, 0.23957e1, -0.42639e2, 0.99569e2, -0.62135e2] + _Pv_exp = [1.0, 1.5, 2.75, 3.0, 3.2] + _rhoL_eq = 1 + _rhoL_ao = [0.26406e1, 0.97090e1, -0.18058e2, 0.87202e1, -0.74487e1] + _rhoL_exp = [0.3678, 1.9, 2.2, 2.63, 7.3] + _rhoG_eq = 3 + _rhoG_ao = [-0.37651e1, -0.38673e2, 0.73024e2, -0.13251e3, 0.75235e2, + -0.70412e2] + _rhoG_exp = [0.409, 1.766, 2.24, 3.04, 3.42, 6.9] + + # fase is unused + def _visco(self, rho: float, T: float, fase: Optional[_fase] = None) -> float: return _D2O_Viscosity(rho, T) - def _thermo(self, rho, T, fase): + # fase is unused + def _thermo(self, rho: float, T: float, fase: Optional[_fase] = None) -> float: return _D2O_ThCond(rho, T) - def _surface(self, T): + def _surface(self, T: float) -> float: s = _D2O_Tension(T) return s diff --git a/iapws/iapws97.py b/iapws/iapws97.py index 6ac7cff..b087da0 100644 --- a/iapws/iapws97.py +++ b/iapws/iapws97.py @@ -84,10 +84,11 @@ from __future__ import division from math import sqrt, log, exp +from typing import Tuple, Dict, List, Optional from scipy.optimize import fsolve, newton -from ._iapws import R, Tc, Pc, rhoc, Tt, Pt, Tb, Dipole, f_acent +from ._iapws import _global_R, Tc, Pc, rhoc, Tt, Pt, Tb, Dipole, f_acent from ._iapws import _Viscosity, _ThCond, _Tension, _Dielectric, _Refractive from ._utils import getphase, deriv_G, _fase @@ -103,7 +104,7 @@ # Boundary Region1-Region3 -def _h13_s(s): +def _h13_s(s: float) -> float: """Define the boundary between Region 1 and 3, h=f(s) Parameters @@ -144,17 +145,17 @@ def _h13_s(s): sigma = s/3.8 I = [0, 1, 1, 3, 5, 6] J = [0, -2, 2, -12, -4, -3] - n = [0.913965547600543, -0.430944856041991e-4, 0.603235694765419e2, + N = [0.913965547600543, -0.430944856041991e-4, 0.603235694765419e2, 0.117518273082168e-17, 0.220000904781292, -0.690815545851641e2] - suma = 0 - for i, j, ni in zip(I, J, n): - suma += ni * (sigma-0.884)**i * (sigma-0.864)**j + suma = 0.0 + for i, j, n in zip(I, J, N): + suma += n * (sigma-0.884)**i * (sigma-0.864)**j return 1700 * suma # Boundary Region2-Region3 -def _P23_T(T): +def _P23_T(T: float) -> float: """Define the boundary between Region 2 and 3, P=f(T) Parameters @@ -182,7 +183,7 @@ def _P23_T(T): return n[0]+n[1]*T+n[2]*T**2 -def _t_P(P): +def _t_P(P: float) -> float: """Define the boundary between Region 2 and 3, T=f(P) Parameters @@ -210,7 +211,7 @@ def _t_P(P): return n[1]+((P-n[2])/n[0])**0.5 -def _t_hs(h, s): +def _t_hs(h: float, s: float) -> float: """Define the boundary between Region 2 and 3, T=f(h,s) Parameters @@ -258,7 +259,7 @@ def _t_hs(h, s): 8, 12, 12, 14, 14] J = [10, 8, 3, 4, 3, -6, 2, 3, 4, 0, -3, -2, 10, -2, -1, -5, -6, -3, -8, -2, -1, -12, -1, -12, 1] - n = [0.629096260829810e-3, -0.823453502583165e-3, 0.515446951519474e-7, + N = [0.629096260829810e-3, -0.823453502583165e-3, 0.515446951519474e-7, -0.117565945784945e1, 0.348519684726192e1, -0.507837382408313e-11, -0.284637670005479e1, -0.236092263939673e1, 0.601492324973779e1, 0.148039650824546e1, 0.360075182221907e-3, -0.126700045009952e-1, @@ -268,14 +269,14 @@ def _t_hs(h, s): 0.261907376402688e-5, -0.291626417025961e5, 0.140660774926165e-4, 0.783237062349385e7] - suma = 0 - for i, j, ni in zip(I, J, n): - suma += ni * (nu-0.727)**i * (sigma-0.864)**j + suma = 0.0 + for i, j, n in zip(I, J, N): + suma += n * (nu-0.727)**i * (sigma-0.864)**j return 900*suma # Saturated line -def _PSat_T(T): +def _PSat_T(T: float) -> float: """Define the saturated line, P=f(T) Parameters @@ -320,7 +321,7 @@ def _PSat_T(T): return (2*C/(-B+(B**2-4*A*C)**0.5))**4 -def _TSat_P(P): +def _TSat_P(P: float) -> float: """Define the saturated line, T=f(P) Parameters @@ -366,7 +367,7 @@ def _TSat_P(P): return (n[10]+D-((n[10]+D)**2-4*(n[9]+n[10]*D))**0.5)/2 -def _PSat_h(h): +def _PSat_h(h: float) -> float: """Define the saturated line, P=f(h) for region 3 Parameters @@ -400,27 +401,27 @@ def _PSat_h(h): 20.18090839 """ # Check input parameters - hmin_Ps3 = _Region1(623.15, Ps_623)["h"] - hmax_Ps3 = _Region2(623.15, Ps_623)["h"] + hmin_Ps3 = _Region1(623.15, Ps_623).h + hmax_Ps3 = _Region2(623.15, Ps_623).h if h < hmin_Ps3 or h > hmax_Ps3: raise NotImplementedError("Incoming out of bound") nu = h/2600 I = [0, 1, 1, 1, 1, 5, 7, 8, 14, 20, 22, 24, 28, 36] J = [0, 1, 3, 4, 36, 3, 0, 24, 16, 16, 3, 18, 8, 24] - n = [0.600073641753024, -0.936203654849857e1, 0.246590798594147e2, + N = [0.600073641753024, -0.936203654849857e1, 0.246590798594147e2, -0.107014222858224e3, -0.915821315805768e14, -0.862332011700662e4, -0.235837344740032e2, 0.252304969384128e18, -0.389718771997719e19, -0.333775713645296e23, 0.356499469636328e11, -0.148547544720641e27, 0.330611514838798e19, 0.813641294467829e38] - suma = 0 - for i, j, ni in zip(I, J, n): - suma += ni * (nu-1.02)**i * (nu-0.608)**j + suma = 0.0 + for i, j, n in zip(I, J, N): + suma += n * (nu-1.02)**i * (nu-0.608)**j return 22*suma -def _PSat_s(s): +def _PSat_s(s: float) -> float: """Define the saturated line, P=f(s) for region 3 Parameters @@ -454,26 +455,26 @@ def _PSat_s(s): 16.68968482 """ # Check input parameters - smin_Ps3 = _Region1(623.15, Ps_623)["s"] - smax_Ps3 = _Region2(623.15, Ps_623)["s"] + smin_Ps3 = _Region1(623.15, Ps_623).s + smax_Ps3 = _Region2(623.15, Ps_623).s if s < smin_Ps3 or s > smax_Ps3: raise NotImplementedError("Incoming out of bound") sigma = s/5.2 I = [0, 1, 1, 4, 12, 12, 16, 24, 28, 32] J = [0, 1, 32, 7, 4, 14, 36, 10, 0, 18] - n = [0.639767553612785, -0.129727445396014e2, -0.224595125848403e16, + N = [0.639767553612785, -0.129727445396014e2, -0.224595125848403e16, 0.177466741801846e7, 0.717079349571538e10, -0.378829107169011e18, -0.955586736431328e35, 0.187269814676188e24, 0.119254746466473e12, 0.110649277244882e37] - suma = 0 - for i, j, ni in zip(I, J, n): - suma += ni * (sigma-1.03)**i * (sigma-0.699)**j + suma = 0.0 + for i, j, n in zip(I, J, N): + suma += n * (sigma-1.03)**i * (sigma-0.699)**j return 22*suma -def _h1_s(s): +def _h1_s(s: float) -> float: """Define the saturated line boundary between Region 1 and 4, h=f(s) Parameters @@ -516,7 +517,7 @@ def _h1_s(s): 20, 22, 24, 28, 32, 32] J = [14, 36, 3, 16, 0, 5, 4, 36, 4, 16, 24, 18, 24, 1, 4, 2, 4, 1, 22, 10, 12, 28, 8, 3, 0, 6, 8] - n = [0.332171191705237, 0.611217706323496e-3, -0.882092478906822e1, + N = [0.332171191705237, 0.611217706323496e-3, -0.882092478906822e1, -0.455628192543250, -0.263483840850452e-4, -0.223949661148062e2, -0.428398660164013e1, -0.616679338856916, -0.146823031104040e2, 0.284523138727299e3, -0.113398503195444e3, 0.115671380760859e4, @@ -526,13 +527,13 @@ def _h1_s(s): 0.573953875852936e7, 0.173226193407919e3, -0.363968822121321e-1, 0.834596332878346e-6, 0.503611916682674e1, 0.655444787064505e2] - suma = 0 - for i, j, ni in zip(I, J, n): - suma += ni * (sigma-1.09)**i * (sigma+0.366e-4)**j + suma = 0.0 + for i, j, n in zip(I, J, N): + suma += n * (sigma-1.09)**i * (sigma+0.366e-4)**j return 1700*suma -def _h3a_s(s): +def _h3a_s(s: float) -> float: """Define the saturated line boundary between Region 4 and 3a, h=f(s) Parameters @@ -573,7 +574,7 @@ def _h3a_s(s): sigma = s/3.8 I = [0, 0, 0, 0, 2, 3, 4, 4, 5, 5, 6, 7, 7, 7, 10, 10, 10, 32, 32] J = [1, 4, 10, 16, 1, 36, 3, 16, 20, 36, 4, 2, 28, 32, 14, 32, 36, 0, 6] - n = [0.822673364673336, 0.181977213534479, -0.112000260313624e-1, + N = [0.822673364673336, 0.181977213534479, -0.112000260313624e-1, -0.746778287048033e-3, -0.179046263257381, 0.424220110836657e-1, -0.341355823438768, -0.209881740853565e1, -0.822477343323596e1, -0.499684082076008e1, 0.191413958471069, 0.581062241093136e-1, @@ -581,13 +582,13 @@ def _h3a_s(s): -0.317714386511207e5, -0.945890406632871e5, -0.139273847088690e-5, 0.631052532240980] - suma = 0 - for i, j, ni in zip(I, J, n): - suma += ni * (sigma-1.09)**i * (sigma+0.366e-4)**j + suma = 0.0 + for i, j, n in zip(I, J, N): + suma += n * (sigma-1.09)**i * (sigma+0.366e-4)**j return 1700*suma -def _h2ab_s(s): +def _h2ab_s(s: float) -> float: """Define the saturated line boundary between Region 4 and 2a-2b, h=f(s) Parameters @@ -631,7 +632,7 @@ def _h2ab_s(s): 32, 32, 32, 32, 32, 36, 36, 36, 36, 36] J = [8, 24, 4, 32, 1, 2, 7, 5, 12, 1, 0, 7, 10, 12, 32, 8, 12, 20, 22, 24, 2, 7, 12, 14, 24, 10, 12, 20, 22, 28] - n = [-0.524581170928788e3, -0.926947218142218e7, -0.237385107491666e3, + N = [-0.524581170928788e3, -0.926947218142218e7, -0.237385107491666e3, 0.210770155812776e11, -0.239494562010986e2, 0.221802480294197e3, -0.510472533393438e7, 0.124981396109147e7, 0.200008436996201e10, -0.815158509791035e3, -0.157612685637523e3, -0.114200422332791e11, @@ -642,13 +643,13 @@ def _h2ab_s(s): 0.297478906557467e35, -0.953588761745473e20, 0.166957699620939e25, -0.175407764869978e33, 0.347581490626396e35, -0.710971318427851e39] - suma = 0 - for i, j, ni in zip(I, J, n): - suma += ni * (1/sigma1-0.513)**i * (sigma2-0.524)**j + suma = 0.0 + for i, j, n in zip(I, J, N): + suma += n * (1/sigma1-0.513)**i * (sigma2-0.524)**j return 2800*exp(suma) -def _h2c3b_s(s): +def _h2c3b_s(s: float) -> float: """Define the saturated line boundary between Region 4 and 2c-3b, h=f(s) Parameters @@ -689,21 +690,41 @@ def _h2c3b_s(s): sigma = s/5.9 I = [0, 0, 0, 1, 1, 5, 6, 7, 8, 8, 12, 16, 22, 22, 24, 36] J = [0, 3, 4, 0, 12, 36, 12, 16, 2, 20, 32, 36, 2, 32, 7, 20] - n = [0.104351280732769e1, -0.227807912708513e1, 0.180535256723202e1, + N = [0.104351280732769e1, -0.227807912708513e1, 0.180535256723202e1, 0.420440834792042, -0.105721244834660e6, 0.436911607493884e25, -0.328032702839753e12, -0.678686760804270e16, 0.743957464645363e4, -0.356896445355761e20, 0.167590585186801e32, -0.355028625419105e38, 0.396611982166538e12, -0.414716268484468e41, 0.359080103867382e19, -0.116994334851995e41] - suma = 0 - for i, j, ni in zip(I, J, n): - suma += ni * (sigma-1.02)**i * (sigma-0.726)**j + suma = 0.0 + for i, j, n in zip(I, J, N): + suma += n * (sigma-1.02)**i * (sigma-0.726)**j return 2800*suma**4 +class IAPWS97Properties(object): + """The properties required to fill the IAPWS97 phase.""" + + def __init__(self, T: float, P: float, v: float, h: float, s: float, + cp: float, cv: float, w: float, alfav: float, kt: float, + region: int, x: float) -> None: + self.T = T + self.P = P + self.v = v + self.h = h + self.s = s + self.cp = cp + self.cv = cv + self.w = w + self.alfav = alfav + self.kt = kt + self.region = region + self.x = x + + # Region 1 -def _Region1(T, P): +def _Region1(T: float, P: float) -> IAPWS97Properties: """Basic equation for region 1 Parameters @@ -735,23 +756,23 @@ def _Region1(T, P): Examples -------- - >>> _Region1(300,3)["v"] + >>> _Region1(300,3).v 0.00100215168 - >>> _Region1(300,3)["h"] + >>> _Region1(300,3).h 115.331273 - >>> _Region1(300,3)["h"]-3000*_Region1(300,3)["v"] + >>> _Region1(300,3).h-3000*_Region1(300,3).v 112.324818 - >>> _Region1(300,80)["s"] + >>> _Region1(300,80).s 0.368563852 - >>> _Region1(300,80)["cp"] + >>> _Region1(300,80).cp 4.01008987 - >>> _Region1(300,80)["cv"] + >>> _Region1(300,80).cv 3.91736606 - >>> _Region1(500,3)["w"] + >>> _Region1(500,3).w 1240.71337 - >>> _Region1(500,3)["alfav"] + >>> _Region1(500,3).alfav 0.00164118128 - >>> _Region1(500,3)["kt"] + >>> _Region1(500,3).kt 0.00112892188 """ if P < 0: @@ -761,7 +782,7 @@ def _Region1(T, P): 4, 4, 5, 8, 8, 21, 23, 29, 30, 31, 32] J = [-2, -1, 0, 1, 2, 3, 4, 5, -9, -7, -1, 0, 1, 3, -3, 0, 1, 3, 17, -4, 0, 6, -5, -2, 10, -8, -11, -6, -29, -31, -38, -39, -40, -41] - n = [0.14632971213167, -0.84548187169114, -0.37563603672040e1, + N = [0.14632971213167, -0.84548187169114, -0.37563603672040e1, 0.33855169168385e1, -0.95791963387872, 0.15772038513228, -0.16616417199501e-1, 0.81214629983568e-3, 0.28319080123804e-3, -0.60706301565874e-3, -0.18990068218419e-1, -0.32529748770505e-1, @@ -775,32 +796,29 @@ def _Region1(T, P): -0.93537087292458e-25] Tr = 1386/T Pr = P/16.53 - g = gp = gpp = gt = gtt = gpt = 0 - for i, j, ni in zip(I, J, n): - g += ni * (7.1-Pr)**i * (Tr-1.222)**j - gp -= ni*i * (7.1-Pr)**(i-1) * (Tr-1.222)**j - gpp += ni*i*(i-1) * (7.1-Pr)**(i-2) * (Tr-1.222)**j - gt += ni*j * (7.1-Pr)**i * (Tr-1.222)**(j-1) - gtt += ni*j*(j-1) * (7.1-Pr)**i * (Tr-1.222)**(j-2) - gpt -= ni*i*j * (7.1-Pr)**(i-1) * (Tr-1.222)**(j-1) - - propiedades = {} - propiedades["T"] = T - propiedades["P"] = P - propiedades["v"] = Pr*gp*R*T/P/1000 - propiedades["h"] = Tr*gt*R*T - propiedades["s"] = R*(Tr*gt-g) - propiedades["cp"] = -R*Tr**2*gtt - propiedades["cv"] = R*(-Tr**2*gtt+(gp-Tr*gpt)**2/gpp) - propiedades["w"] = sqrt(R*T*1000*gp**2/((gp-Tr*gpt)**2/(Tr**2*gtt)-gpp)) - propiedades["alfav"] = (1-Tr*gpt/gp)/T - propiedades["kt"] = -Pr*gpp/gp/P - propiedades["region"] = 1 - propiedades["x"] = 0 - return propiedades - - -def _Backward1_T_Ph(P, h): + g = gp = gpp = gt = gtt = gpt = 0.0 + for i, j, n in zip(I, J, N): + g += n * (7.1-Pr)**i * (Tr-1.222)**j + gp -= n*i * (7.1-Pr)**(i-1) * (Tr-1.222)**j + gpp += n*i*(i-1) * (7.1-Pr)**(i-2) * (Tr-1.222)**j + gt += n*j * (7.1-Pr)**i * (Tr-1.222)**(j-1) + gtt += n*j*(j-1) * (7.1-Pr)**i * (Tr-1.222)**(j-2) + gpt -= n*i*j * (7.1-Pr)**(i-1) * (Tr-1.222)**(j-1) + + R = _global_R + + v = Pr*gp*R*T/P/1000 + h = Tr*gt*R*T + s = R*(Tr*gt-g) + cp = -R*Tr**2*gtt + cv = R*(-Tr**2*gtt+(gp-Tr*gpt)**2/gpp) + w = sqrt(R*T*1000*gp**2/((gp-Tr*gpt)**2/(Tr**2*gtt)-gpp)) + alfav = (1-Tr*gpt/gp)/T + kt = -Pr*gpp/gp/P + return IAPWS97Properties(T, P, v, h, s, cp, cv, w, alfav, kt, 1, 0.0) + + +def _Backward1_T_Ph(P: float, h: float) -> float: """ Backward equation for region 1, T=f(P,h) @@ -831,7 +849,7 @@ def _Backward1_T_Ph(P, h): """ I = [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 3, 3, 4, 5, 6] J = [0, 1, 2, 6, 22, 32, 0, 1, 2, 3, 4, 10, 32, 10, 32, 10, 32, 32, 32, 32] - n = [-0.23872489924521e3, 0.40421188637945e3, 0.11349746881718e3, + N = [-0.23872489924521e3, 0.40421188637945e3, 0.11349746881718e3, -0.58457616048039e1, -0.15285482413140e-3, -0.10866707695377e-5, -0.13391744872602e2, 0.43211039183559e2, -0.54010067170506e2, 0.30535892203916e2, -0.65964749423638e1, 0.93965400878363e-2, @@ -841,13 +859,13 @@ def _Backward1_T_Ph(P, h): Pr = P/1 nu = h/2500 - T = 0 - for i, j, ni in zip(I, J, n): - T += ni * Pr**i * (nu+1)**j + T = 0.0 + for i, j, n in zip(I, J, N): + T += n * Pr**i * (nu+1)**j return T -def _Backward1_T_Ps(P, s): +def _Backward1_T_Ps(P: float, s: float) -> float: """Backward equation for region 1, T=f(P,s) Parameters @@ -877,7 +895,7 @@ def _Backward1_T_Ps(P, s): """ I = [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 4] J = [0, 1, 2, 3, 11, 31, 0, 1, 2, 3, 12, 31, 0, 1, 2, 9, 31, 10, 32, 32] - n = [0.17478268058307e3, 0.34806930892873e2, 0.65292584978455e1, + N = [0.17478268058307e3, 0.34806930892873e2, 0.65292584978455e1, 0.33039981775489, -0.19281382923196e-6, -0.24909197244573e-22, -0.26107636489332, 0.22592965981586, -0.64256463395226e-1, 0.78876289270526e-2, 0.35672110607366e-9, 0.17332496994895e-23, @@ -887,13 +905,13 @@ def _Backward1_T_Ps(P, s): Pr = P/1 sigma = s/1 - T = 0 - for i, j, ni in zip(I, J, n): - T += ni * Pr**i * (sigma+2)**j + T = 0.0 + for i, j, n in zip(I, J, N): + T += n * Pr**i * (sigma+2)**j return T -def _Backward1_P_hs(h, s): +def _Backward1_P_hs(h: float, s: float) -> float: """Backward equation for region 1, P=f(h,s) Parameters @@ -926,7 +944,7 @@ def _Backward1_P_hs(h, s): """ I = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 4, 4, 5] J = [0, 1, 2, 4, 5, 6, 8, 14, 0, 1, 4, 6, 0, 1, 10, 4, 1, 4, 0] - n = [-0.691997014660582, -0.183612548787560e2, -0.928332409297335e1, + N = [-0.691997014660582, -0.183612548787560e2, -0.928332409297335e1, 0.659639569909906e2, -0.162060388912024e2, 0.450620017338667e3, 0.854680678224170e3, 0.607523214001162e4, 0.326487682621856e2, -0.269408844582931e2, -0.319947848334300e3, -0.928354307043320e3, @@ -936,14 +954,14 @@ def _Backward1_P_hs(h, s): nu = h/3400 sigma = s/7.6 - P = 0 - for i, j, ni in zip(I, J, n): - P += ni * (nu+0.05)**i * (sigma+0.05)**j + P = 0.0 + for i, j, n in zip(I, J, N): + P += n * (nu+0.05)**i * (sigma+0.05)**j return 100*P # Region 2 -def _Region2(T, P): +def _Region2(T: float, P: float) -> IAPWS97Properties: """Basic equation for region 2 Parameters @@ -975,23 +993,23 @@ def _Region2(T, P): Examples -------- - >>> _Region2(700,30)["v"] + >>> _Region2(700,30).v 0.00542946619 - >>> _Region2(700,30)["h"] + >>> _Region2(700,30).h 2631.49474 - >>> _Region2(700,30)["h"]-30000*_Region2(700,30)["v"] + >>> _Region2(700,30).h-30000*_Region2(700,30).v 2468.61076 - >>> _Region2(700,0.0035)["s"] + >>> _Region2(700,0.0035).s 10.1749996 - >>> _Region2(700,0.0035)["cp"] + >>> _Region2(700,0.0035).cp 2.08141274 - >>> _Region2(700,0.0035)["cv"] + >>> _Region2(700,0.0035).cv 1.61978333 - >>> _Region2(300,0.0035)["w"] + >>> _Region2(300,0.0035).w 427.920172 - >>> _Region2(300,0.0035)["alfav"] + >>> _Region2(300,0.0035).alfav 0.00337578289 - >>> _Region2(300,0.0035)["kt"] + >>> _Region2(300,0.0035).kt 286.239651 """ if P < 0: @@ -1026,7 +1044,7 @@ def _Region2(T, P): -1.2768608934681e-15, 7.3087610595061e-29, 5.5414715350778001e-17, -9.4369707241209998e-07] - gr = grp = grpp = grt = grtt = grpt = 0 + gr = grp = grpp = grt = grtt = grpt = 0.0 for i, j, ni in zip(Ir, Jr, nr): gr += ni * Pr**i * (Tr-0.5)**j grp += ni*i * Pr**(i-1) * (Tr-0.5)**j @@ -1035,25 +1053,21 @@ def _Region2(T, P): grtt += ni*j*(j-1) * Pr**i * (Tr-0.5)**(j-2) grpt += ni*i*j * Pr**(i-1) * (Tr-0.5)**(j-1) - propiedades = {} - propiedades["T"] = T - propiedades["P"] = P - propiedades["v"] = Pr*(gop+grp)*R*T/P/1000 - propiedades["h"] = Tr*(got+grt)*R*T - propiedades["s"] = R*(Tr*(got+grt)-(go+gr)) - propiedades["cp"] = -R*Tr**2*(gott+grtt) - propiedades["cv"] = R*(-Tr**2*(gott+grtt)-(1+Pr*grp-Tr*Pr*grpt)**2 / - (1-Pr**2*grpp)) - propiedades["w"] = (R*T*1000*(1+2*Pr*grp+Pr**2*grp**2)/(1-Pr**2*grpp+( + R = _global_R + + v = Pr*(gop+grp)*R*T/P/1000 + h = Tr*(got+grt)*R*T + s = R*(Tr*(got+grt)-(go+gr)) + cp = -R*Tr**2*(gott+grtt) + cv = R*(-Tr**2*(gott+grtt)-(1+Pr*grp-Tr*Pr*grpt)**2 / (1-Pr**2*grpp)) + w = (R*T*1000*(1+2*Pr*grp+Pr**2*grp**2)/(1-Pr**2*grpp+( 1+Pr*grp-Tr*Pr*grpt)**2/Tr**2/(gott+grtt)))**0.5 - propiedades["alfav"] = (1+Pr*grp-Tr*Pr*grpt)/(1+Pr*grp)/T - propiedades["kt"] = (1-Pr**2*grpp)/(1+Pr*grp)/P - propiedades["region"] = 2 - propiedades["x"] = 1 - return propiedades + alfav = (1+Pr*grp-Tr*Pr*grpt)/(1+Pr*grp)/T + kt = (1-Pr**2*grpp)/(1+Pr*grp)/P + return IAPWS97Properties(T, P, v, h, s, cp, cv, w, alfav, kt, 2, 1.0) -def Region2_cp0(Tr, Pr): +def Region2_cp0(Tr: float, Pr: float) -> Tuple[float, float, float, float, float, float]: """Ideal properties for Region 2 Parameters @@ -1089,7 +1103,7 @@ def Region2_cp0(Tr, Pr): go = log(Pr) gop = Pr**-1 gopp = -Pr**-2 - got = gott = gopt = 0 + got = gott = gopt = 0.0 for j, ni in zip(Jo, no): go += ni * Tr**j got += ni*j * Tr**(j-1) @@ -1097,7 +1111,7 @@ def Region2_cp0(Tr, Pr): return go, gop, gopp, got, gott, gopt -def _P_2bc(h): +def _P_2bc(h: float) -> float: """Define the boundary between Region 2b and 2c, P=f(h) Parameters @@ -1124,7 +1138,7 @@ def _P_2bc(h): return 905.84278514723-0.67955786399241*h+1.2809002730136e-4*h**2 -def _hbc_P(P): +def _hbc_P(P: float) -> float: """Define the boundary between Region 2b and 2c, h=f(P) Parameters @@ -1151,7 +1165,7 @@ def _hbc_P(P): return 0.26526571908428e4+((P-0.45257578905948e1)/1.2809002730136e-4)**0.5 -def _hab_s(s): +def _hab_s(s: float) -> float: """Define the boundary between Region 2a and 2b, h=f(s) Parameters @@ -1176,19 +1190,19 @@ def _hab_s(s): >>> _hab_s(7) 3376.437884 """ - smin = _Region2(_TSat_P(4), 4)["s"] - smax = _Region2(1073.15, 4)["s"] + smin = _Region2(_TSat_P(4), 4).s + smax = _Region2(1073.15, 4).s if s < smin: - h = 0 + h = 0.0 elif s > smax: - h = 5000 + h = 5000.0 else: h = -0.349898083432139e4 + 0.257560716905876e4*s - \ 0.421073558227969e3*s**2+0.276349063799944e2*s**3 return h -def _Backward2a_T_Ph(P, h): +def _Backward2a_T_Ph(P: float, h: float) -> float: """Backward equation for region 2a, T=f(P,h) Parameters @@ -1220,7 +1234,7 @@ def _Backward2a_T_Ph(P, h): 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 7] J = [0, 1, 2, 3, 7, 20, 0, 1, 2, 3, 7, 9, 11, 18, 44, 0, 2, 7, 36, 38, 40, 42, 44, 24, 44, 12, 32, 44, 32, 36, 42, 34, 44, 28] - n = [0.10898952318288e4, 0.84951654495535e3, -0.10781748091826e3, + N = [0.10898952318288e4, 0.84951654495535e3, -0.10781748091826e3, 0.33153654801263e2, -0.74232016790248e1, 0.11765048724356e2, 0.18445749355790e1, -0.41792700549624e1, 0.62478196935812e1, -0.17344563108114e2, -0.20058176862096e3, 0.27196065473796e3, @@ -1235,13 +1249,13 @@ def _Backward2a_T_Ph(P, h): Pr = P/1 nu = h/2000 - T = 0 - for i, j, ni in zip(I, J, n): - T += ni * Pr**i * (nu-2.1)**j + T = 0.0 + for i, j, n in zip(I, J, N): + T += n * Pr**i * (nu-2.1)**j return T -def _Backward2b_T_Ph(P, h): +def _Backward2b_T_Ph(P: float, h: float) -> float: """Backward equation for region 2b, T=f(P,h) Parameters @@ -1273,7 +1287,7 @@ def _Backward2b_T_Ph(P, h): 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 6, 7, 7, 9, 9] J = [0, 1, 2, 12, 18, 24, 28, 40, 0, 2, 6, 12, 18, 24, 28, 40, 2, 8, 18, 40, 1, 2, 12, 24, 2, 12, 18, 24, 28, 40, 18, 24, 40, 28, 2, 28, 1, 40] - n = [0.14895041079516e4, 0.74307798314034e3, -0.97708318797837e2, + N = [0.14895041079516e4, 0.74307798314034e3, -0.97708318797837e2, 0.24742464705674e1, -0.63281320016026, 0.11385952129658e1, -0.47811863648625, 0.85208123431544e-2, 0.93747147377932, 0.33593118604916e1, 0.33809355601454e1, 0.16844539671904, @@ -1289,13 +1303,13 @@ def _Backward2b_T_Ph(P, h): Pr = P/1 nu = h/2000 - T = 0 - for i, j, ni in zip(I, J, n): - T += ni * (Pr-2)**i * (nu-2.6)**j + T = 0.0 + for i, j, n in zip(I, J, N): + T += n * (Pr-2)**i * (nu-2.6)**j return T -def _Backward2c_T_Ph(P, h): +def _Backward2c_T_Ph(P: float, h: float) -> float: """Backward equation for region 2c, T=f(P,h) Parameters @@ -1327,7 +1341,7 @@ def _Backward2c_T_Ph(P, h): 6, 6, 6] J = [0, 4, 0, 2, 0, 2, 0, 1, 0, 2, 0, 1, 4, 8, 4, 0, 1, 4, 10, 12, 16, 20, 22] - n = [-0.32368398555242e13, 0.73263350902181e13, 0.35825089945447e12, + N = [-0.32368398555242e13, 0.73263350902181e13, 0.35825089945447e12, -0.58340131851590e12, -0.10783068217470e11, 0.20825544563171e11, 0.61074783564516e6, 0.85977722535580e6, -0.25745723604170e5, 0.31081088422714e5, 0.12082315865936e4, 0.48219755109255e3, @@ -1338,13 +1352,13 @@ def _Backward2c_T_Ph(P, h): Pr = P/1 nu = h/2000 - T = 0 - for i, j, ni in zip(I, J, n): - T += ni * (Pr+25)**i * (nu-1.8)**j + T = 0.0 + for i, j, n in zip(I, J, N): + T += n * (Pr+25)**i * (nu-1.8)**j return T -def _Backward2_T_Ph(P, h): +def _Backward2_T_Ph(P: float, h: float) -> float: """Backward equation for region 2, T=f(P,h) Parameters @@ -1376,7 +1390,7 @@ def _Backward2_T_Ph(P, h): return T -def _Backward2a_T_Ps(P, s): +def _Backward2a_T_Ps(P: float, s: float) -> float: """Backward equation for region 2a, T=f(P,s) Parameters @@ -1411,7 +1425,7 @@ def _Backward2a_T_Ps(P, s): J = [-24, -23, -19, -13, -11, -10, -19, -15, -6, -26, -21, -17, -16, -9, -8, -15, -14, -26, -13, -9, -7, -27, -25, -11, -6, 1, 4, 8, 11, 0, 1, 5, 6, 10, 14, 16, 0, 4, 9, 17, 7, 18, 3, 15, 5, 18] - n = [-0.39235983861984e6, 0.51526573827270e6, 0.40482443161048e5, + N = [-0.39235983861984e6, 0.51526573827270e6, 0.40482443161048e5, -0.32193790923902e3, 0.96961424218694e2, -0.22867846371773e2, -0.44942914124357e6, -0.50118336020166e4, 0.35684463560015, 0.44235335848190e5, -0.13673388811708e5, 0.42163260207864e6, @@ -1430,13 +1444,13 @@ def _Backward2a_T_Ps(P, s): Pr = P/1 sigma = s/2 - T = 0 - for i, j, ni in zip(I, J, n): - T += ni * Pr**i * (sigma-2)**j + T = 0.0 + for i, j, n in zip(I, J, N): + T += n * Pr**i * (sigma-2)**j return T -def _Backward2b_T_Ps(P, s): +def _Backward2b_T_Ps(P: float, s: float) -> float: """Backward equation for region 2b, T=f(P,s) Parameters @@ -1469,7 +1483,7 @@ def _Backward2b_T_Ps(P, s): 4, 4, 5, 5, 5] J = [0, 11, 0, 11, 0, 1, 11, 0, 1, 11, 12, 0, 1, 6, 10, 0, 1, 5, 8, 9, 0, 1, 2, 4, 5, 6, 9, 0, 1, 2, 3, 7, 8, 0, 1, 5, 0, 1, 3, 0, 1, 0, 1, 2] - n = [0.31687665083497e6, 0.20864175881858e2, -0.39859399803599e6, + N = [0.31687665083497e6, 0.20864175881858e2, -0.39859399803599e6, -0.21816058518877e2, 0.22369785194242e6, -0.27841703445817e4, 0.99207436071480e1, -0.75197512299157e5, 0.29708605951158e4, -0.34406878548526e1, 0.38815564249115, 0.17511295085750e5, @@ -1487,13 +1501,13 @@ def _Backward2b_T_Ps(P, s): Pr = P/1 sigma = s/0.7853 - T = 0 - for i, j, ni in zip(I, J, n): - T += ni * Pr**i * (10-sigma)**j + T = 0.0 + for i, j, n in zip(I, J, N): + T += n * Pr**i * (10-sigma)**j return T -def _Backward2c_T_Ps(P, s): +def _Backward2c_T_Ps(P: float, s: float) -> float: """Backward equation for region 2c, T=f(P,s) Parameters @@ -1525,7 +1539,7 @@ def _Backward2c_T_Ps(P, s): 5, 6, 6, 7, 7, 7, 7, 7] J = [0, 1, 0, 0, 1, 2, 3, 0, 1, 3, 4, 0, 1, 2, 0, 1, 5, 0, 1, 4, 0, 1, 2, 0, 1, 0, 1, 3, 4, 5] - n = [0.90968501005365e3, 0.24045667088420e4, -0.59162326387130e3, + N = [0.90968501005365e3, 0.24045667088420e4, -0.59162326387130e3, 0.54145404128074e3, -0.27098308411192e3, 0.97976525097926e3, -0.46966772959435e3, 0.14399274604723e2, -0.19104204230429e2, 0.53299167111971e1, -0.21252975375934e2, -0.31147334413760, @@ -1538,13 +1552,13 @@ def _Backward2c_T_Ps(P, s): Pr = P/1 sigma = s/2.9251 - T = 0 - for i, j, ni in zip(I, J, n): - T += ni * Pr**i * (2-sigma)**j + T = 0.0 + for i, j, n in zip(I, J, N): + T += n * Pr**i * (2-sigma)**j return T -def _Backward2_T_Ps(P, s): +def _Backward2_T_Ps(P: float, s: float) -> float: """Backward equation for region 2, T=f(P,s) Parameters @@ -1572,7 +1586,7 @@ def _Backward2_T_Ps(P, s): return T -def _Backward2a_P_hs(h, s): +def _Backward2a_P_hs(h: float, s: float) -> float: """Backward equation for region 2a, P=f(h,s) Parameters @@ -1607,7 +1621,7 @@ def _Backward2a_P_hs(h, s): 3, 4, 5, 5, 6, 7] J = [1, 3, 6, 16, 20, 22, 0, 1, 2, 3, 5, 6, 10, 16, 20, 22, 3, 16, 20, 0, 2, 3, 6, 16, 16, 3, 16, 3, 1] - n = [-0.182575361923032e-1, -0.125229548799536, 0.592290437320145, + N = [-0.182575361923032e-1, -0.125229548799536, 0.592290437320145, 0.604769706185122e1, 0.238624965444474e3, -0.298639090222922e3, 0.512250813040750e-1, -0.437266515606486, 0.413336902999504, -0.516468254574773e1, -0.557014838445711e1, 0.128555037824478e2, @@ -1620,13 +1634,13 @@ def _Backward2a_P_hs(h, s): nu = h/4200 sigma = s/12 - suma = 0 - for i, j, ni in zip(I, J, n): - suma += ni * (nu-0.5)**i * (sigma-1.2)**j + suma = 0.0 + for i, j, n in zip(I, J, N): + suma += n * (nu-0.5)**i * (sigma-1.2)**j return 4*suma**4 -def _Backward2b_P_hs(h, s): +def _Backward2b_P_hs(h: float, s: float) -> float: """Backward equation for region 2b, P=f(h,s) Parameters @@ -1661,7 +1675,7 @@ def _Backward2b_P_hs(h, s): 6, 6, 7, 7, 8, 8, 8, 8, 12, 14] J = [0, 1, 2, 4, 8, 0, 1, 2, 3, 5, 12, 1, 6, 18, 0, 1, 7, 12, 1, 16, 1, 12, 1, 8, 18, 1, 16, 1, 3, 14, 18, 10, 16] - n = [0.801496989929495e-1, -0.543862807146111, 0.337455597421283, + N = [0.801496989929495e-1, -0.543862807146111, 0.337455597421283, 0.890555451157450e1, 0.313840736431485e3, 0.797367065977789, -0.121616973556240e1, 0.872803386937477e1, -0.169769781757602e2, -0.186552827328416e3, 0.951159274344237e5, -0.189168510120494e2, @@ -1675,13 +1689,13 @@ def _Backward2b_P_hs(h, s): nu = h/4100 sigma = s/7.9 - suma = 0 - for i, j, ni in zip(I, J, n): - suma += ni * (nu-0.6)**i * (sigma-1.01)**j + suma = 0.0 + for i, j, n in zip(I, J, N): + suma += n * (nu-0.6)**i * (sigma-1.01)**j return 100*suma**4 -def _Backward2c_P_hs(h, s): +def _Backward2c_P_hs(h: float, s: float) -> float: """Backward equation for region 2c, P=f(h,s) Parameters @@ -1716,7 +1730,7 @@ def _Backward2c_P_hs(h, s): 5, 5, 5, 6, 6, 10, 12, 16] J = [0, 1, 2, 3, 4, 8, 0, 2, 5, 8, 14, 2, 3, 7, 10, 18, 0, 5, 8, 16, 18, 18, 1, 4, 6, 14, 8, 18, 7, 7, 10] - n = [0.112225607199012, -0.339005953606712e1, -0.320503911730094e2, + N = [0.112225607199012, -0.339005953606712e1, -0.320503911730094e2, -0.197597305104900e3, -0.407693861553446e3, 0.132943775222331e5, 0.170846839774007e1, 0.373694198142245e2, 0.358144365815434e4, 0.423014446424664e6, -0.751071025760063e9, 0.523446127607898e2, @@ -1730,13 +1744,13 @@ def _Backward2c_P_hs(h, s): nu = h/3500 sigma = s/5.9 - suma = 0 - for i, j, ni in zip(I, J, n): - suma += ni * (nu-0.7)**i * (sigma-1.1)**j + suma = 0.0 + for i, j, n in zip(I, J, N): + suma += n * (nu-0.7)**i * (sigma-1.1)**j return 100*suma**4 -def _Backward2_P_hs(h, s): +def _Backward2_P_hs(h: float, s: float) -> float: """Backward equation for region 2, P=f(h,s) Parameters @@ -1763,7 +1777,7 @@ def _Backward2_P_hs(h, s): # Region 3 -def _Region3(rho, T): +def _Region3(rho: float, T: float) -> IAPWS97Properties: """Basic equation for region 3 Parameters @@ -1795,32 +1809,31 @@ def _Region3(rho, T): Examples -------- - >>> _Region3(500,650)["P"] + >>> _Region3(500,650).P 25.5837018 - >>> _Region3(500,650)["h"] + >>> _Region3(500,650).h 1863.43019 >>> p = _Region3(500, 650) - >>> p["h"]-p["P"]*1000*p["v"] + >>> p.h-p.P*1000*p.v 1812.26279 - >>> _Region3(200,650)["s"] + >>> _Region3(200,650).s 4.85438792 - >>> _Region3(200,650)["cp"] + >>> _Region3(200,650).cp 44.6579342 - >>> _Region3(200,650)["cv"] + >>> _Region3(200,650).cv 4.04118076 - >>> _Region3(200,650)["w"] + >>> _Region3(200,650).w 383.444594 - >>> _Region3(500,750)["alfav"] + >>> _Region3(500,750).alfav 0.00441515098 - >>> _Region3(500,750)["kt"] + >>> _Region3(500,750).kt 0.00806710817 """ - I = [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 8, 9, 9, 10, 10, 11] J = [0, 1, 2, 7, 10, 12, 23, 2, 6, 15, 17, 0, 2, 6, 7, 22, 26, 0, 2, 4, 16, 26, 0, 2, 4, 26, 1, 3, 26, 0, 2, 26, 2, 26, 2, 26, 0, 1, 26] - n = [-0.15732845290239e2, 0.20944396974307e2, -0.76867707878716e1, + N = [-0.15732845290239e2, 0.20944396974307e2, -0.76867707878716e1, 0.26185947787954e1, -0.28080781148620e1, 0.12053369696517e1, -0.84566812812502e-2, -0.12654315477714e1, -0.11524407806681e1, 0.88521043984318, -0.64207765181607, 0.38493460186671, @@ -1839,33 +1852,30 @@ def _Region3(rho, T): g = 1.0658070028513*log(d) gd = 1.0658070028513/d gdd = -1.0658070028513/d**2 - gt = gtt = gdt = 0 - for i, j, ni in zip(I, J, n): - g += ni * d**i * Tr**j - gd += ni*i * d**(i-1) * Tr**j - gdd += ni*i*(i-1) * d**(i-2) * Tr**j - gt += ni*j * d**i * Tr**(j-1) - gtt += ni*j*(j-1) * d**i * Tr**(j-2) - gdt += ni*i*j * d**(i-1) * Tr**(j-1) - - propiedades = {} - propiedades["T"] = T - propiedades["P"] = d*gd*R*T*rho/1000 - propiedades["v"] = 1/rho - propiedades["h"] = R*T*(Tr*gt+d*gd) - propiedades["s"] = R*(Tr*gt-g) - propiedades["cp"] = R*(-Tr**2*gtt+(d*gd-d*Tr*gdt)**2/(2*d*gd+d**2*gdd)) - propiedades["cv"] = -R*Tr**2*gtt - propiedades["w"] = sqrt(R*T*1000*(2*d*gd+d**2*gdd-(d*gd-d*Tr*gdt)**2 / - Tr**2/gtt)) - propiedades["alfav"] = (gd-Tr*gdt)/(2*gd+d*gdd)/T - propiedades["kt"] = 1/(2*d*gd+d**2*gdd)/rho/R/T*1000 - propiedades["region"] = 3 - propiedades["x"] = 1 - return propiedades - - -def _h_3ab(P): + gt = gtt = gdt = 0.0 + for i, j, n in zip(I, J, N): + g += n * d**i * Tr**j + gd += n*i * d**(i-1) * Tr**j + gdd += n*i*(i-1) * d**(i-2) * Tr**j + gt += n*j * d**i * Tr**(j-1) + gtt += n*j*(j-1) * d**i * Tr**(j-2) + gdt += n*i*j * d**(i-1) * Tr**(j-1) + + R = _global_R + + P = d*gd*R*T*rho/1000 + v = 1/rho + h = R*T*(Tr*gt+d*gd) + s = R*(Tr*gt-g) + cp = R*(-Tr**2*gtt+(d*gd-d*Tr*gdt)**2/(2*d*gd+d**2*gdd)) + cv = -R*Tr**2*gtt + w = sqrt(R*T*1000*(2*d*gd+d**2*gdd-(d*gd-d*Tr*gdt)**2 / Tr**2/gtt)) + alfav = (gd-Tr*gdt)/(2*gd+d*gdd)/T + kt = 1/(2*d*gd+d**2*gdd)/rho/R/T*1000 + return IAPWS97Properties(T, P, v, h, s, cp, cv, w, alfav, kt, 3, 1.0) + + +def _h_3ab(P: float) -> float: """Define the boundary between Region 3a-3b, h=f(P) Parameters @@ -1887,7 +1897,7 @@ def _h_3ab(P): 0.0219921901054187*P**2+0.875131686009950e-4*P**3 -def _tab_P(P): +def _tab_P(P: float) -> float: """Define the boundary between Region 3a-3b, T=f(P) Parameters @@ -1913,17 +1923,17 @@ def _tab_P(P): 693.0341408 """ I = [0, 1, 2, -1, -2] - n = [0.154793642129415e4, -0.187661219490113e3, 0.213144632222113e2, + N = [0.154793642129415e4, -0.187661219490113e3, 0.213144632222113e2, -0.191887498864292e4, 0.918419702359447e3] Pr = P/1 - T = 0 - for i, ni in zip(I, n): - T += ni * log(Pr)**i + T = 0.0 + for i, n in zip(I, N): + T += n * log(Pr)**i return T -def _top_P(P): +def _top_P(P: float) -> float: """Define the boundary between Region 3o-3p, T=f(P) Parameters @@ -1949,17 +1959,17 @@ def _top_P(P): 650.0106943 """ I = [0, 1, 2, -1, -2] - n = [0.969461372400213e3, -0.332500170441278e3, 0.642859598466067e2, + N = [0.969461372400213e3, -0.332500170441278e3, 0.642859598466067e2, 0.773845935768222e3, -0.152313732937084e4] Pr = P/1 - T = 0 - for i, ni in zip(I, n): - T += ni * log(Pr)**i + T = 0.0 + for i, n in zip(I, N): + T += n * log(Pr)**i return T -def _twx_P(P): +def _twx_P(P: float) -> float: """Define the boundary between Region 3w-3x, T=f(P) Parameters @@ -1985,17 +1995,17 @@ def _twx_P(P): 648.2049480 """ I = [0, 1, 2, -1, -2] - n = [0.728052609145380e1, 0.973505869861952e2, 0.147370491183191e2, + N = [0.728052609145380e1, 0.973505869861952e2, 0.147370491183191e2, 0.329196213998375e3, 0.873371668682417e3] Pr = P/1 - T = 0 - for i, ni in zip(I, n): - T += ni * log(Pr)**i + T = 0.0 + for i, n in zip(I, N): + T += n * log(Pr)**i return T -def _tef_P(P): +def _tef_P(P: float) -> float: """Define the boundary between Region 3e-3f, T=f(P) Parameters @@ -2023,7 +2033,7 @@ def _tef_P(P): return 3.727888004*(P-22.064)+647.096 -def _txx_P(P, xy): +def _txx_P(P: float, xy: str) -> float: """Define the boundary between 3x-3y, T=f(P) Parameters @@ -2064,7 +2074,7 @@ def _txx_P(P, xy): >>> _txx_P(22.3,"uv") 647.7996121 """ - ng = { + N = { "cd": [0.585276966696349e3, 0.278233532206915e1, -0.127283549295878e-1, 0.159090746562729e-3], "gh": [-0.249284240900418e5, 0.428143584791546e4, -0.269029173140130e3, @@ -2082,15 +2092,14 @@ def _txx_P(P, xy): "uv": [0.528199646263062e3, 0.890579602135307e1, -0.222814134903755, 0.286791682263697e-2]} - n = ng[xy] Pr = P/1 - T = 0 - for i, ni in enumerate(n): - T += ni * Pr**i + T = 0.0 + for i, n in enumerate(N[xy]): + T += n * Pr**i return T -def _Backward3a_v_Ph(P, h): +def _Backward3a_v_Ph(P: float, h: float) -> float: """Backward equation for region 3a, v=f(P,h) Parameters @@ -2123,7 +2132,7 @@ def _Backward3a_v_Ph(P, h): -2, -1, -1, -1, -1, 0, 0, 1, 1, 1, 2, 2, 3, 4, 5, 8] J = [6, 8, 12, 18, 4, 7, 10, 5, 12, 3, 4, 22, 2, 3, 7, 3, 16, 0, 1, 2, 3, 0, 1, 0, 1, 2, 0, 2, 0, 2, 2, 2] - n = [0.529944062966028e-2, -0.170099690234461, 0.111323814312927e2, + N = [0.529944062966028e-2, -0.170099690234461, 0.111323814312927e2, -0.217898123145125e4, -0.506061827980875e-3, 0.556495239685324, -0.943672726094016e1, -0.297856807561527, 0.939353943717186e2, 0.192944939465981e-1, 0.421740664704763, -0.368914126282330e7, @@ -2137,13 +2146,13 @@ def _Backward3a_v_Ph(P, h): Pr = P/100 nu = h/2100 - suma = 0 - for i, j, ni in zip(I, J, n): - suma += ni * (Pr+0.128)**i * (nu-0.727)**j + suma = 0.0 + for i, j, n in zip(I, J, N): + suma += n * (Pr+0.128)**i * (nu-0.727)**j return 0.0028*suma -def _Backward3b_v_Ph(P, h): +def _Backward3b_v_Ph(P: float, h: float) -> float: """Backward equation for region 3b, v=f(P,h) Parameters @@ -2176,7 +2185,7 @@ def _Backward3b_v_Ph(P, h): -3, -3, -2, -2, -1, -1, -1, -1, 0, 1, 1, 2, 2] J = [0, 1, 0, 1, 3, 6, 7, 8, 0, 1, 2, 5, 6, 10, 3, 6, 10, 0, 2, 1, 2, 0, 1, 4, 5, 0, 0, 1, 2, 6] - n = [-0.225196934336318e-8, 0.140674363313486e-7, 0.233784085280560e-5, + N = [-0.225196934336318e-8, 0.140674363313486e-7, 0.233784085280560e-5, -0.331833715229001e-4, 0.107956778514318e-2, -0.271382067378863, 0.107202262490333e1, -0.853821329075382, -0.215214194340526e-4, 0.769656088222730e-3, -0.431136580433864e-2, 0.453342167309331, @@ -2189,13 +2198,13 @@ def _Backward3b_v_Ph(P, h): Pr = P/100 nu = h/2800 - suma = 0 - for i, j, ni in zip(I, J, n): - suma += ni * (Pr+0.0661)**i * (nu-0.72)**j + suma = 0.0 + for i, j, n in zip(I, J, N): + suma += n * (Pr+0.0661)**i * (nu-0.72)**j return 0.0088*suma -def _Backward3_v_Ph(P, h): +def _Backward3_v_Ph(P: float, h: float) -> float: """Backward equation for region 3, v=f(P,h) Parameters @@ -2217,7 +2226,7 @@ def _Backward3_v_Ph(P, h): return _Backward3b_v_Ph(P, h) -def _Backward3a_T_Ph(P, h): +def _Backward3a_T_Ph(P: float, h: float) -> float: """Backward equation for region 3a, T=f(P,h) Parameters @@ -2250,7 +2259,7 @@ def _Backward3a_T_Ph(P, h): -5, -3, -2, -2, -2, -1, -1, 0, 0, 1, 3, 3, 4, 4, 10, 12] J = [0, 1, 2, 6, 14, 16, 20, 22, 1, 5, 12, 0, 2, 4, 10, 2, 0, 1, 3, 4, 0, 2, 0, 1, 1, 0, 1, 0, 3, 4, 5] - n = [-0.133645667811215e-6, 0.455912656802978e-5, -0.146294640700979e-4, + N = [-0.133645667811215e-6, 0.455912656802978e-5, -0.146294640700979e-4, 0.639341312970080e-2, 0.372783927268847e3, -0.718654377460447e4, 0.573494752103400e6, -0.267569329111439e7, -0.334066283302614e-4, -0.245479214069597e-1, 0.478087847764996e2, 0.764664131818904e-5, @@ -2264,13 +2273,13 @@ def _Backward3a_T_Ph(P, h): Pr = P/100. nu = h/2300. - suma = 0 - for i, j, n in zip(I, J, n): + suma = 0.0 + for i, j, n in zip(I, J, N): suma += n*(Pr+0.240)**i*(nu-0.615)**j return 760*suma -def _Backward3b_T_Ph(P, h): +def _Backward3b_T_Ph(P: float, h: float) -> float: """Backward equation for region 3b, T=f(P,h) Parameters @@ -2303,7 +2312,7 @@ def _Backward3b_T_Ph(P, h): -4, -3, -2, -2, -1, -1, -1, -1, -1, -1, 0, 0, 1, 3, 5, 6, 8] J = [0, 1, 0, 1, 5, 10, 12, 0, 1, 2, 4, 10, 0, 1, 2, 0, 1, 5, 0, 4, 2, 4, 6, 10, 14, 16, 0, 2, 1, 1, 1, 1, 1] - n = [0.323254573644920e-4, -0.127575556587181e-3, -0.475851877356068e-3, + N = [0.323254573644920e-4, -0.127575556587181e-3, -0.475851877356068e-3, 0.156183014181602e-2, 0.105724860113781, -0.858514221132534e2, 0.724140095480911e3, 0.296475810273257e-2, -0.592721983365988e-2, -0.126305422818666e-1, -0.115716196364853, 0.849000969739595e2, @@ -2317,13 +2326,13 @@ def _Backward3b_T_Ph(P, h): Pr = P/100. nu = h/2800. - suma = 0 - for i, j, n in zip(I, J, n): + suma = 0.0 + for i, j, n in zip(I, J, N): suma += n*(Pr+0.298)**i*(nu-0.72)**j return 860*suma -def _Backward3_T_Ph(P, h): +def _Backward3_T_Ph(P: float, h: float) -> float: """Backward equation for region 3, T=f(P,h) Parameters @@ -2346,7 +2355,7 @@ def _Backward3_T_Ph(P, h): return T -def _Backward3a_v_Ps(P, s): +def _Backward3a_v_Ps(P: float, s: float) -> float: """Backward equation for region 3a, v=f(P,s) Parameters @@ -2379,7 +2388,7 @@ def _Backward3a_v_Ps(P, s): -2, -2, -1, -1, 0, 0, 0, 1, 2, 4, 5, 6] J = [10, 12, 14, 4, 8, 10, 20, 5, 6, 14, 16, 28, 1, 5, 2, 4, 3, 8, 1, 2, 0, 1, 3, 0, 0, 2, 2, 0] - n = [0.795544074093975e2, -0.238261242984590e4, 0.176813100617787e5, + N = [0.795544074093975e2, -0.238261242984590e4, 0.176813100617787e5, -0.110524727080379e-2, -0.153213833655326e2, 0.297544599376982e3, -0.350315206871242e8, 0.277513761062119, -0.523964271036888, -0.148011182995403e6, 0.160014899374266e7, 0.170802322663427e13, @@ -2392,13 +2401,13 @@ def _Backward3a_v_Ps(P, s): Pr = P/100 sigma = s/4.4 - suma = 0 - for i, j, ni in zip(I, J, n): - suma += ni * (Pr+0.187)**i * (sigma-0.755)**j + suma = 0.0 + for i, j, n in zip(I, J, N): + suma += n * (Pr+0.187)**i * (sigma-0.755)**j return 0.0028*suma -def _Backward3b_v_Ps(P, s): +def _Backward3b_v_Ps(P: float, s: float) -> float: """Backward equation for region 3b, v=f(P,s) Parameters @@ -2431,7 +2440,7 @@ def _Backward3b_v_Ps(P, s): -4, -4, -4, -3, -2, -2, -2, -2, -2, -2, 0, 0, 0, 1, 1, 2] J = [0, 1, 2, 3, 5, 6, 0, 1, 2, 4, 0, 1, 2, 3, 0, 1, 2, 3, 1, 0, 1, 2, 3, 4, 12, 0, 1, 2, 0, 2, 2] - n = [0.591599780322238e-4, -0.185465997137856e-2, 0.104190510480013e-1, + N = [0.591599780322238e-4, -0.185465997137856e-2, 0.104190510480013e-1, 0.598647302038590e-2, -0.771391189901699, 0.172549765557036e1, -0.467076079846526e-3, 0.134533823384439e-1, -0.808094336805495e-1, 0.508139374365767, 0.128584643361683e-2, -0.163899353915435e1, @@ -2445,13 +2454,13 @@ def _Backward3b_v_Ps(P, s): Pr = P/100 sigma = s/5.3 - suma = 0 - for i, j, ni in zip(I, J, n): - suma += ni * (Pr+0.298)**i * (sigma-0.816)**j + suma = 0.0 + for i, j, n in zip(I, J, N): + suma += n * (Pr+0.298)**i * (sigma-0.816)**j return 0.0088*suma -def _Backward3_v_Ps(P, s): +def _Backward3_v_Ps(P: float, s: float) -> float: """Backward equation for region 3, v=f(P,s) Parameters @@ -2472,7 +2481,7 @@ def _Backward3_v_Ps(P, s): return _Backward3b_v_Ps(P, s) -def _Backward3a_T_Ps(P, s): +def _Backward3a_T_Ps(P: float, s: float) -> float: """Backward equation for region 3a, T=f(P,s) Parameters @@ -2505,7 +2514,7 @@ def _Backward3a_T_Ps(P, s): -4, -4, -4, -2, -2, -1, -1, 0, 0, 0, 1, 2, 2, 3, 8, 8, 10] J = [28, 32, 4, 10, 12, 14, 5, 7, 8, 28, 2, 6, 32, 0, 14, 32, 6, 10, 36, 1, 4, 1, 6, 0, 1, 4, 0, 0, 3, 2, 0, 1, 2] - n = [0.150042008263875e10, -0.159397258480424e12, 0.502181140217975e-3, + N = [0.150042008263875e10, -0.159397258480424e12, 0.502181140217975e-3, -0.672057767855466e2, 0.145058545404456e4, -0.823889534888890e4, -0.154852214233853, 0.112305046746695e2, -0.297000213482822e2, 0.438565132635495e11, 0.137837838635464e-2, -0.297478527157462e1, @@ -2519,13 +2528,13 @@ def _Backward3a_T_Ps(P, s): Pr = P/100 sigma = s/4.4 - suma = 0 - for i, j, ni in zip(I, J, n): - suma += ni * (Pr+0.240)**i * (sigma-0.703)**j + suma = 0.0 + for i, j, n in zip(I, J, N): + suma += n * (Pr+0.240)**i * (sigma-0.703)**j return 760*suma -def _Backward3b_T_Ps(P, s): +def _Backward3b_T_Ps(P: float, s: float) -> float: """Backward equation for region 3b, T=f(P,s) Parameters @@ -2558,7 +2567,7 @@ def _Backward3b_T_Ps(P, s): -3, -3, -2, 0, 2, 3, 4, 5, 6, 8, 12, 14] J = [1, 3, 4, 7, 0, 1, 3, 0, 2, 4, 0, 1, 2, 4, 6, 12, 1, 6, 2, 0, 1, 1, 0, 24, 0, 3, 1, 2] - n = [0.527111701601660, -0.401317830052742e2, 0.153020073134484e3, + N = [0.527111701601660, -0.401317830052742e2, 0.153020073134484e3, -0.224799398218827e4, -0.193993484669048, -0.140467557893768e1, 0.426799878114024e2, 0.752810643416743, 0.226657238616417e2, -0.622873556909932e3, -0.660823667935396, 0.841267087271658, @@ -2571,13 +2580,13 @@ def _Backward3b_T_Ps(P, s): Pr = P/100 sigma = s/5.3 - suma = 0 - for i, j, ni in zip(I, J, n): - suma += ni * (Pr+0.760)**i * (sigma-0.818)**j + suma = 0.0 + for i, j, n in zip(I, J, N): + suma += n * (Pr+0.760)**i * (sigma-0.818)**j return 860*suma -def _Backward3_T_Ps(P, s): +def _Backward3_T_Ps(P: float, s: float) -> float: """Backward equation for region 3, T=f(P,s) Parameters @@ -2600,7 +2609,7 @@ def _Backward3_T_Ps(P, s): return T -def _Backward3a_P_hs(h, s): +def _Backward3a_P_hs(h: float, s: float) -> float: """Backward equation for region 3a, P=f(h,s) Parameters @@ -2636,7 +2645,7 @@ def _Backward3a_P_hs(h, s): 14, 18, 20, 22, 22, 24, 28, 28, 32, 32] J = [0, 1, 5, 0, 3, 4, 8, 14, 6, 16, 0, 2, 3, 0, 1, 4, 5, 28, 28, 24, 1, 32, 36, 22, 28, 36, 16, 28, 36, 16, 36, 10, 28] - n = [0.770889828326934e1, -0.260835009128688e2, 0.267416218930389e3, + N = [0.770889828326934e1, -0.260835009128688e2, 0.267416218930389e3, 0.172221089496844e2, -0.293542332145970e3, 0.614135601882478e3, -0.610562757725674e5, -0.651272251118219e8, 0.735919313521937e5, -0.116646505914191e11, 0.355267086434461e2, -0.596144543825955e3, @@ -2650,13 +2659,13 @@ def _Backward3a_P_hs(h, s): nu = h/2300 sigma = s/4.4 - suma = 0 - for i, j, ni in zip(I, J, n): - suma += ni * (nu-1.01)**i * (sigma-0.75)**j + suma = 0.0 + for i, j, n in zip(I, J, N): + suma += n * (nu-1.01)**i * (sigma-0.75)**j return 99*suma -def _Backward3b_P_hs(h, s): +def _Backward3b_P_hs(h: float, s: float) -> float: """Backward equation for region 3b, P=f(h,s) Parameters @@ -2693,7 +2702,7 @@ def _Backward3b_P_hs(h, s): 14] J = [2, 10, 12, 14, 20, 2, 10, 14, 18, 2, 8, 2, 6, 7, 8, 10, 4, 5, 8, 1, 3, 5, 6, 0, 1, 0, 3, 0, 1, 0, 1, 1, 1, 3, 7] - n = [0.125244360717979e-12, -0.126599322553713e-1, 0.506878030140626e1, + N = [0.125244360717979e-12, -0.126599322553713e-1, 0.506878030140626e1, 0.317847171154202e2, -0.391041161399932e6, -0.975733406392044e-10, -0.186312419488279e2, 0.510973543414101e3, 0.373847005822362e6, 0.299804024666572e-7, 0.200544393820342e2, -0.498030487662829e-5, @@ -2708,13 +2717,13 @@ def _Backward3b_P_hs(h, s): nu = h/2800 sigma = s/5.3 - suma = 0 - for i, j, ni in zip(I, J, n): - suma += ni * (nu-0.681)**i * (sigma-0.792)**j + suma = 0.0 + for i, j, n in zip(I, J, N): + suma += n * (nu-0.681)**i * (sigma-0.792)**j return 16.6/suma -def _Backward3_P_hs(h, s): +def _Backward3_P_hs(h: float, s: float) -> float: """Backward equation for region 3, P=f(h,s) Parameters @@ -2736,7 +2745,7 @@ def _Backward3_P_hs(h, s): return _Backward3b_P_hs(h, s) -def _Backward3_sat_v_P(P, T, x): +def _Backward3_sat_v_P(P: float, T: float, x: int) -> float: """Backward equation for region 3 for saturated state, vs=f(P,x) Parameters @@ -2779,7 +2788,7 @@ def _Backward3_sat_v_P(P, T, x): return _Backward3x_v_PT(T, P, region) -def _Backward3_v_PT(P, T): +def _Backward3_v_PT(P: float, T: float) -> float: """Backward equation for region 3, v=f(P,T) Parameters @@ -2961,7 +2970,7 @@ def _Backward3_v_PT(P, T): return _Backward3x_v_PT(T, P, region) -def _Backward3x_v_PT(T, P, x): +def _Backward3x_v_PT(T: float, P: float, x: str) -> float: """Backward equation for region 3x, v=f(P,T) Parameters @@ -3093,33 +3102,33 @@ def _Backward3x_v_PT(T, P, x): 0.003701940009 """ par = { - "a": [0.0024, 100, 760, 0.085, 0.817, 1, 1, 1], - "b": [0.0041, 100, 860, 0.280, 0.779, 1, 1, 1], - "c": [0.0022, 40, 690, 0.259, 0.903, 1, 1, 1], - "d": [0.0029, 40, 690, 0.559, 0.939, 1, 1, 4], - "e": [0.0032, 40, 710, 0.587, 0.918, 1, 1, 1], - "f": [0.0064, 40, 730, 0.587, 0.891, 0.5, 1, 4], - "g": [0.0027, 25, 660, 0.872, 0.971, 1, 1, 4], - "h": [0.0032, 25, 660, 0.898, 0.983, 1, 1, 4], - "i": [0.0041, 25, 660, 0.910, 0.984, 0.5, 1, 4], - "j": [0.0054, 25, 670, 0.875, 0.964, 0.5, 1, 4], - "k": [0.0077, 25, 680, 0.802, 0.935, 1, 1, 1], - "l": [0.0026, 24, 650, 0.908, 0.989, 1, 1, 4], - "m": [0.0028, 23, 650, 1.000, 0.997, 1, 0.25, 1], - "n": [0.0031, 23, 650, 0.976, 0.997, None, None, None], - "o": [0.0034, 23, 650, 0.974, 0.996, 0.5, 1, 1], - "p": [0.0041, 23, 650, 0.972, 0.997, 0.5, 1, 1], - "q": [0.0022, 23, 650, 0.848, 0.983, 1, 1, 4], - "r": [0.0054, 23, 650, 0.874, 0.982, 1, 1, 1], - "s": [0.0022, 21, 640, 0.886, 0.990, 1, 1, 4], - "t": [0.0088, 20, 650, 0.803, 1.020, 1, 1, 1], - "u": [0.0026, 23, 650, 0.902, 0.988, 1, 1, 1], - "v": [0.0031, 23, 650, 0.960, 0.995, 1, 1, 1], - "w": [0.0039, 23, 650, 0.959, 0.995, 1, 1, 4], - "x": [0.0049, 23, 650, 0.910, 0.988, 1, 1, 1], - "y": [0.0031, 22, 650, 0.996, 0.994, 1, 1, 4], - "z": [0.0038, 22, 650, 0.993, 0.994, 1, 1, 4], - } + "a": [0.0024, 100, 760, 0.085, 0.817, 1.0, 1.0, 1.0], + "b": [0.0041, 100, 860, 0.280, 0.779, 1.0, 1.0, 1.0], + "c": [0.0022, 40, 690, 0.259, 0.903, 1.0, 1.0, 1.0], + "d": [0.0029, 40, 690, 0.559, 0.939, 1.0, 1.0, 4.0], + "e": [0.0032, 40, 710, 0.587, 0.918, 1.0, 1.0, 1.0], + "f": [0.0064, 40, 730, 0.587, 0.891, 0.5, 1.0, 4.0], + "g": [0.0027, 25, 660, 0.872, 0.971, 1.0, 1.0, 4.0], + "h": [0.0032, 25, 660, 0.898, 0.983, 1.0, 1.0, 4.0], + "i": [0.0041, 25, 660, 0.910, 0.984, 0.5, 1.0, 4.0], + "j": [0.0054, 25, 670, 0.875, 0.964, 0.5, 1.0, 4.0], + "k": [0.0077, 25, 680, 0.802, 0.935, 1.0, 1.0, 1.0], + "l": [0.0026, 24, 650, 0.908, 0.989, 1.0, 1.0, 4.0], + "m": [0.0028, 23, 650, 1.000, 0.997, 1.0, 0.25, 1.0], + "n": [0.0031, 23, 650, 0.976, 0.997, 1.0, 1.0, 1.0], + "o": [0.0034, 23, 650, 0.974, 0.996, 0.5, 1.0, 1.0], + "p": [0.0041, 23, 650, 0.972, 0.997, 0.5, 1.0, 1.0], + "q": [0.0022, 23, 650, 0.848, 0.983, 1.0, 1.0, 4.0], + "r": [0.0054, 23, 650, 0.874, 0.982, 1.0, 1.0, 1.0], + "s": [0.0022, 21, 640, 0.886, 0.990, 1.0, 1.0, 4.0], + "t": [0.0088, 20, 650, 0.803, 1.020, 1.0, 1.0, 1.0], + "u": [0.0026, 23, 650, 0.902, 0.988, 1.0, 1.0, 1.0], + "v": [0.0031, 23, 650, 0.960, 0.995, 1.0, 1.0, 1.0], + "w": [0.0039, 23, 650, 0.959, 0.995, 1.0, 1.0, 4.0], + "x": [0.0049, 23, 650, 0.910, 0.988, 1.0, 1.0, 1.0], + "y": [0.0031, 22, 650, 0.996, 0.994, 1.0, 1.0, 4.0], + "z": [0.0038, 22, 650, 0.993, 0.994, 1.0, 1.0, 4.0], + } I = { "a": [-12, -12, -12, -10, -10, -10, -8, -8, -8, -6, -5, -5, -5, -4, -3, @@ -3248,7 +3257,7 @@ def _Backward3x_v_PT(T, P, x): "z": [3, 6, 6, 8, 5, 6, 8, -2, 5, 6, 2, -6, 3, 1, 6, -6, -2, -6, -5, -4, -1, -8, -4]} - n = { + N = { "a": [0.110879558823853e-2, 0.572616740810616e3, -0.767051948380852e5, -0.253321069529674e-1, 0.628008049345689e4, 0.234105654131876e6, 0.216867826045856, -0.156237904341963e3, -0.269893956176613e5, @@ -3551,26 +3560,21 @@ def _Backward3x_v_PT(T, P, x): 0.154355721681459e2, -0.373962862928643e4, -0.682859011374572e11, -0.248488015614543e-3, 0.394536049497068e7]} - I = I[x] - J = J[x] - n = n[x] v_, P_, T_, a, b, c, d, e = par[x] Pr = P/P_ Tr = T/T_ - suma = 0 - if x == "n": - for i, j, ni in zip(I, J, n): - suma += ni * (Pr-a)**i * (Tr-b)**j + suma = 0.0 + for i, j, n in zip(I[x], J[x], N[x]): + suma += n * (Pr-a)**(c*i) * (Tr-b)**(j*d) + if x == 'n': return v_*exp(suma) else: - for i, j, ni in zip(I, J, n): - suma += ni * (Pr-a)**(c*i) * (Tr-b)**(j*d) return v_*suma**e # Region 4 -def _Region4(P, x): +def _Region4(P: float, x: float) -> IAPWS97Properties: """Basic equation for region 4 Parameters @@ -3602,23 +3606,18 @@ def _Region4(P, x): P1 = _Region1(T, P) P2 = _Region2(T, P) - propiedades = {} - propiedades["T"] = T - propiedades["P"] = P - propiedades["v"] = P1["v"]+x*(P2["v"]-P1["v"]) - propiedades["h"] = P1["h"]+x*(P2["h"]-P1["h"]) - propiedades["s"] = P1["s"]+x*(P2["s"]-P1["s"]) - propiedades["cp"] = None - propiedades["cv"] = None - propiedades["w"] = None - propiedades["alfav"] = None - propiedades["kt"] = None - propiedades["region"] = 4 - propiedades["x"] = x - return propiedades + v = P1.v+x*(P2.v-P1.v) + h = P1.h+x*(P2.h-P1.h) + s = P1.s+x*(P2.s-P1.s) + cp = float('nan') + cv = float('nan') + w = float('nan') + alfav = float('nan') + kt = float('nan') + return IAPWS97Properties(T, P, v, h, s, cp, cv, w, alfav, kt, 4, x) -def _Backward4_T_hs(h, s): +def _Backward4_T_hs(h: float, s: float) -> float: """Backward equation for region 4, T=f(h,s) Parameters @@ -3654,7 +3653,7 @@ def _Backward4_T_hs(h, s): 8, 10, 10, 12, 14, 14, 16, 16, 18, 18, 18, 20, 28] J = [0, 3, 12, 0, 1, 2, 5, 0, 5, 8, 0, 2, 3, 4, 0, 1, 1, 2, 4, 16, 6, 8, 22, 1, 20, 36, 24, 1, 28, 12, 32, 14, 22, 36, 24, 36] - n = [0.179882673606601, -0.267507455199603, 0.116276722612600e1, + N = [0.179882673606601, -0.267507455199603, 0.116276722612600e1, 0.147545428713616, -0.512871635973248, 0.421333567697984, 0.563749522189870, 0.429274443819153, -0.335704552142140e1, 0.108890916499278e2, -0.248483390456012, 0.304153221906390, @@ -3669,14 +3668,14 @@ def _Backward4_T_hs(h, s): nu = h/2800 sigma = s/9.2 - suma = 0 - for i, j, ni in zip(I, J, n): - suma += ni * (nu-0.119)**i * (sigma-1.07)**j + suma = 0.0 + for i, j, n in zip(I, J, N): + suma += n * (nu-0.119)**i * (sigma-1.07)**j return 550*suma # Region 5 -def _Region5(T, P): +def _Region5(T: float, P: float) -> IAPWS97Properties: """Basic equation for region 5 Parameters @@ -3708,23 +3707,23 @@ def _Region5(T, P): Examples -------- - >>> _Region5(1500,0.5)["v"] + >>> _Region5(1500,0.5).v 1.38455090 - >>> _Region5(1500,0.5)["h"] + >>> _Region5(1500,0.5).h 5219.76855 - >>> _Region5(1500,0.5)["h"]-500*_Region5(1500,0.5)["v"] + >>> _Region5(1500,0.5).h-500*_Region5(1500,0.5).v 4527.49310 - >>> _Region5(1500,30)["s"] + >>> _Region5(1500,30).s 7.72970133 - >>> _Region5(1500,30)["cp"] + >>> _Region5(1500,30).cp 2.72724317 - >>> _Region5(1500,30)["cv"] + >>> _Region5(1500,30).cv 2.19274829 - >>> _Region5(2000,30)["w"] + >>> _Region5(2000,30).w 1067.36948 - >>> _Region5(2000,30)["alfav"] + >>> _Region5(2000,30).alfav 0.000508830641 - >>> _Region5(2000,30)["kt"] + >>> _Region5(2000,30).kt 0.0329193892 """ if P < 0: @@ -3739,7 +3738,7 @@ def _Region5(T, P): Jr = [1, 2, 3, 3, 9, 7] nr = [0.15736404855259e-2, 0.90153761673944e-3, -0.50270077677648e-2, 0.22440037409485e-5, -0.41163275453471e-5, 0.37919454822955e-7] - gr = grp = grpp = grt = grtt = grpt = 0 + gr = grp = grpp = grt = grtt = grpt = 0.0 for i, j, ni in zip(Ir, Jr, nr): gr += ni * Pr**i * Tr**j grp += ni*i * Pr**(i-1) * Tr**j @@ -3748,25 +3747,21 @@ def _Region5(T, P): grtt += ni*j*(j-1) * Pr**i * Tr**(j-2) grpt += ni*i*j * Pr**(i-1) * Tr**(j-1) - propiedades = {} - propiedades["T"] = T - propiedades["P"] = P - propiedades["v"] = Pr*(gop+grp)*R*T/P/1000 - propiedades["h"] = Tr*(got+grt)*R*T - propiedades["s"] = R*(Tr*(got+grt)-(go+gr)) - propiedades["cp"] = -R*Tr**2*(gott+grtt) - propiedades["cv"] = R*(-Tr**2*(gott+grtt)+((gop+grp)-Tr*(gopt+grpt))**2 / - (gopp+grpp)) - propiedades["w"] = (R*T*1000*(1+2*Pr*grp+Pr**2*grp**2)/(1-Pr**2*grpp+( + R = _global_R + + v = Pr*(gop+grp)*R*T/P/1000 + h = Tr*(got+grt)*R*T + s = R*(Tr*(got+grt)-(go+gr)) + cp = -R*Tr**2*(gott+grtt) + cv = R*(-Tr**2*(gott+grtt)+((gop+grp)-Tr*(gopt+grpt))**2 / (gopp+grpp)) + w = (R*T*1000*(1+2*Pr*grp+Pr**2*grp**2)/(1-Pr**2*grpp+( 1+Pr*grp-Tr*Pr*grpt)**2/Tr**2/(gott+grtt)))**0.5 - propiedades["alfav"] = (1+Pr*grp-Tr*Pr*grpt)/(1+Pr*grp)/T - propiedades["kt"] = (1-Pr**2*grpp)/(1+Pr*grp)/P - propiedades["region"] = 5 - propiedades["x"] = 1 - return propiedades + alfav = (1+Pr*grp-Tr*Pr*grpt)/(1+Pr*grp)/T + kt = (1-Pr**2*grpp)/(1+Pr*grp)/P + return IAPWS97Properties(T, P, v, h, s, cp, cv, w, alfav, kt, 5, 1) -def Region5_cp0(Tr, Pr): +def Region5_cp0(Tr: float, Pr: float) -> Tuple[float, float, float, float, float, float]: """Ideal properties for Region 5 Parameters @@ -3800,7 +3795,7 @@ def Region5_cp0(Tr, Pr): go = log(Pr) gop = Pr**-1 gopp = -Pr**-2 - got = gott = gopt = 0 + got = gott = gopt = 0.0 for j, ni in zip(Jo, no): go += ni * Tr**j got += ni*j * Tr**(j-1) @@ -3809,8 +3804,32 @@ def Region5_cp0(Tr, Pr): return go, gop, gopp, got, gott, gopt +def _solve_T_P(T: float, P: float) -> IAPWS97Properties: + """Solve for properties using T & P.""" + region = _Bound_TP(T, P) + if region == 1: + propiedades = _Region1(T, P) + elif region == 2: + propiedades = _Region2(T, P) + elif region == 3: + if T == Tc and P == Pc: + rho = rhoc + else: + vo = _Backward3_v_PT(P, T) + + def rho_funcion(rho: float) -> float: + return _Region3(rho, T).P-P + rho = float(newton(rho_funcion, 1/vo)) + propiedades = _Region3(rho, T) + elif region == 5: + propiedades = _Region5(T, P) + else: + raise NotImplementedError("Incoming out of bound") + return propiedades + + # Region definitions -def _Bound_TP(T, P): +def _Bound_TP(T: float, P: float) -> Optional[int]: """Region definition for input T and P Parameters @@ -3851,7 +3870,51 @@ def _Bound_TP(T, P): return region -def _Bound_Ph(P, h): +def _solve_P_h(P: float, h: float) -> IAPWS97Properties: + """Solve for properties using P & h.""" + region = _Bound_Ph(P, h) + if region == 1: + To = _Backward1_T_Ph(P, h) + T = float(newton(lambda T: _Region1(T, P).h-h, To)) + propiedades = _Region1(T, P) + elif region == 2: + To = _Backward2_T_Ph(P, h) + T = float(newton(lambda T: _Region2(T, P).h-h, To)) + propiedades = _Region2(T, P) + elif region == 3: + vo = _Backward3_v_Ph(P, h) + To = _Backward3_T_Ph(P, h) + + def funcion(par: Tuple[float, float]) -> Tuple[float, float]: + return (_Region3(par[0], par[1]).h-h, + _Region3(par[0], par[1]).P-P) + rho, T = tuple(map(float, fsolve(funcion, [1/vo, To]))) + propiedades = _Region3(rho, T) + elif region == 4: + T = _TSat_P(P) + if T <= 623.15: + h1 = _Region1(T, P).h + h2 = _Region2(T, P).h + x = (h-h1)/(h2-h1) + propiedades = _Region4(P, x) + else: + vo = _Backward3_v_Ph(P, h) + To = _Backward3_T_Ph(P, h) + + def funcion(par: Tuple[float, float]) -> Tuple[float, float]: + return (_Region3(par[0], par[1]).h-h, + _Region3(par[0], par[1]).P-P) + rho, T = tuple(map(float, fsolve(funcion, [1/vo, To]))) + propiedades = _Region3(rho, T) + elif region == 5: + T = float(newton(lambda T: _Region5(T, P).h-h, 1500)) + propiedades = _Region5(T, P) + else: + raise NotImplementedError("Incoming out of bound") + return propiedades + + +def _Bound_Ph(P: float, h: float) -> Optional[int]: """Region definition for input P y h Parameters @@ -3874,11 +3937,11 @@ def _Bound_Ph(P, h): """ region = None if Pmin <= P <= Ps_623: - h14 = _Region1(_TSat_P(P), P)["h"] - h24 = _Region2(_TSat_P(P), P)["h"] - h25 = _Region2(1073.15, P)["h"] - hmin = _Region1(273.15, P)["h"] - hmax = _Region5(2273.15, P)["h"] + h14 = _Region1(_TSat_P(P), P).h + h24 = _Region2(_TSat_P(P), P).h + h25 = _Region2(1073.15, P).h + hmin = _Region1(273.15, P).h + hmax = _Region5(2273.15, P).h if hmin <= h <= h14: region = 1 elif h14 < h < h24: @@ -3888,11 +3951,11 @@ def _Bound_Ph(P, h): elif h25 < h <= hmax: region = 5 elif Ps_623 < P < Pc: - hmin = _Region1(273.15, P)["h"] - h13 = _Region1(623.15, P)["h"] - h32 = _Region2(_t_P(P), P)["h"] - h25 = _Region2(1073.15, P)["h"] - hmax = _Region5(2273.15, P)["h"] + hmin = _Region1(273.15, P).h + h13 = _Region1(623.15, P).h + h32 = _Region2(_t_P(P), P).h + h25 = _Region2(1073.15, P).h + hmax = _Region5(2273.15, P).h if hmin <= h <= h13: region = 1 elif h13 < h < h32: @@ -3909,11 +3972,11 @@ def _Bound_Ph(P, h): elif h25 < h <= hmax: region = 5 elif Pc <= P <= 100: - hmin = _Region1(273.15, P)["h"] - h13 = _Region1(623.15, P)["h"] - h32 = _Region2(_t_P(P), P)["h"] - h25 = _Region2(1073.15, P)["h"] - hmax = _Region5(2273.15, P)["h"] + hmin = _Region1(273.15, P).h + h13 = _Region1(623.15, P).h + h32 = _Region2(_t_P(P), P).h + h25 = _Region2(1073.15, P).h + hmax = _Region5(2273.15, P).h if hmin <= h <= h13: region = 1 elif h13 < h < h32: @@ -3925,7 +3988,51 @@ def _Bound_Ph(P, h): return region -def _Bound_Ps(P, s): +def _solve_P_s(P: float, s: float) -> IAPWS97Properties: + """Solve for properties using P & s.""" + region = _Bound_Ps(P, s) + if region == 1: + To = _Backward1_T_Ps(P, s) + T = float(newton(lambda T: _Region1(T, P).s-s, To)) + propiedades = _Region1(T, P) + elif region == 2: + To = _Backward2_T_Ps(P, s) + T = float(newton(lambda T: _Region2(T, P).s-s, To)) + propiedades = _Region2(T, P) + elif region == 3: + vo = _Backward3_v_Ps(P, s) + To = _Backward3_T_Ps(P, s) + + def funcion(par: Tuple[float, float]) -> Tuple[float, float]: + return (_Region3(par[0], par[1]).s-s, + _Region3(par[0], par[1]).P-P) + rho, T = tuple(map(float, fsolve(funcion, [1/vo, To]))) + propiedades = _Region3(rho, T) + elif region == 4: + T = _TSat_P(P) + if T <= 623.15: + s1 = _Region1(T, P).s + s2 = _Region2(T, P).s + x = (s-s1)/(s2-s1) + propiedades = _Region4(P, x) + else: + vo = _Backward3_v_Ps(P, s) + To = _Backward3_T_Ps(P, s) + + def funcion(par: Tuple[float, float]) -> Tuple[float, float]: + return (_Region3(par[0], par[1]).s-s, + _Region3(par[0], par[1]).P-P) + rho, T = tuple(map(float, fsolve(funcion, [1/vo, To]))) + propiedades = _Region3(rho, T) + elif region == 5: + T = float(newton(lambda T: _Region5(T, P).s-s, 1500)) + propiedades = _Region5(T, P) + else: + raise NotImplementedError("Incoming out of bound") + return propiedades + + +def _Bound_Ps(P: float, s: float) -> Optional[int]: """Region definition for input P and s Parameters @@ -3948,11 +4055,11 @@ def _Bound_Ps(P, s): """ region = None if Pmin <= P <= Ps_623: - smin = _Region1(273.15, P)["s"] - s14 = _Region1(_TSat_P(P), P)["s"] - s24 = _Region2(_TSat_P(P), P)["s"] - s25 = _Region2(1073.15, P)["s"] - smax = _Region5(2273.15, P)["s"] + smin = _Region1(273.15, P).s + s14 = _Region1(_TSat_P(P), P).s + s24 = _Region2(_TSat_P(P), P).s + s25 = _Region2(1073.15, P).s + smax = _Region5(2273.15, P).s if smin <= s <= s14: region = 1 elif s14 < s < s24: @@ -3962,11 +4069,11 @@ def _Bound_Ps(P, s): elif s25 < s <= smax: region = 5 elif Ps_623 < P < Pc: - smin = _Region1(273.15, P)["s"] - s13 = _Region1(623.15, P)["s"] - s32 = _Region2(_t_P(P), P)["s"] - s25 = _Region2(1073.15, P)["s"] - smax = _Region5(2273.15, P)["s"] + smin = _Region1(273.15, P).s + s13 = _Region1(623.15, P).s + s32 = _Region2(_t_P(P), P).s + s25 = _Region2(1073.15, P).s + smax = _Region5(2273.15, P).s if smin <= s <= s13: region = 1 elif s13 < s < s32: @@ -3983,11 +4090,11 @@ def _Bound_Ps(P, s): elif s25 < s <= smax: region = 5 elif Pc <= P <= 100: - smin = _Region1(273.15, P)["s"] - s13 = _Region1(623.15, P)["s"] - s32 = _Region2(_t_P(P), P)["s"] - s25 = _Region2(1073.15, P)["s"] - smax = _Region5(2273.15, P)["s"] + smin = _Region1(273.15, P).s + s13 = _Region1(623.15, P).s + s32 = _Region2(_t_P(P), P).s + s25 = _Region2(1073.15, P).s + smax = _Region5(2273.15, P).s if smin <= s <= s13: region = 1 elif s13 < s < s32: @@ -3999,7 +4106,97 @@ def _Bound_Ps(P, s): return region -def _Bound_hs(h, s): +def _solve_h_s(h: float, s: float) -> IAPWS97Properties: + """Solve for properties using h & s.""" + region = _Bound_hs(h, s) + if region == 1: + Po = _Backward1_P_hs(h, s) + To = _Backward1_T_Ph(Po, h) + + def funcion(par: Tuple[float, float]) -> Tuple[float, float]: + return (_Region1(par[0], par[1]).h-h, + _Region1(par[0], par[1]).s-s) + T, P = tuple(map(float, fsolve(funcion, [To, Po]))) + propiedades = _Region1(T, P) + elif region == 2: + Po = _Backward2_P_hs(h, s) + To = _Backward2_T_Ph(Po, h) + + def funcion(par: Tuple[float, float]) -> Tuple[float, float]: + return (_Region2(par[0], par[1]).h-h, + _Region2(par[0], par[1]).s-s) + T, P = tuple(map(float, fsolve(funcion, [To, Po]))) + propiedades = _Region2(T, P) + elif region == 3: + P = _Backward3_P_hs(h, s) + vo = _Backward3_v_Ph(P, h) + To = _Backward3_T_Ph(P, h) + + def funcion(par: Tuple[float, float]) -> Tuple[float, float]: + return (_Region3(par[0], par[1]).h-h, + _Region3(par[0], par[1]).s-s) + rho, T = tuple(map(float, fsolve(funcion, [1/vo, To]))) + propiedades = _Region3(rho, T) + elif region == 4: + if round(s-sc, 6) == 0 and round(h-hc, 6) == 0: + propiedades = _Region3(rhoc, Tc) + + else: + To = _Backward4_T_hs(h, s) + if To < 273.15 or To > Tc: + To = 300 + + def funcion2(par: List[float]) -> Tuple[float, float]: + # This passes tests, but these assignments modify the CALLERS + # par tuple, instead of our copy. Was that intended? + if True: + if par[1] < 0: + par[1] = 0 + elif par[1] > 1: + par[1] = 1 + pp1 = par[1] + # And this does not. + else: + pp1 = par[1] + if pp1 < 0: + pp1 = 0 + elif pp1 > 1: + pp1 = 1 + + pp0 = par[0] + if pp0 < 273.15: + pp0 = 273.15 + elif pp0 > Tc: + pp0 = Tc + + Po = _PSat_T(pp0) + liquid = _Region1(pp0, Po) + vapor = _Region2(pp0, Po) + hl = liquid.h + sl = liquid.s + hv = vapor.h + sv = vapor.s + return (hv*pp1+hl*(1-pp1)-h, + sv*pp1+sl*(1-pp1)-s) + T, x = tuple(map(float, fsolve(funcion2, [To, 0.5]))) + P = _PSat_T(T) + + if Pt <= P < Pc and 0 < x < 1: + propiedades = _Region4(P, x) + elif Pt <= P <= Ps_623 and x == 0: + propiedades = _Region1(T, P) + elif region == 5: + def funcion(par: Tuple[float, float]) -> Tuple[float, float]: + return (_Region5(par[0], par[1]).h-h, + _Region5(par[0], par[1]).s-s) + T, P = tuple(map(float, fsolve(funcion, [1400, 1]))) + propiedades = _Region5(T, P) + else: + raise NotImplementedError("Incoming out of bound") + return propiedades + + +def _Bound_hs(h: float, s: float) -> Optional[int]: """Region definition for input h and s Parameters @@ -4021,33 +4218,33 @@ def _Bound_hs(h, s): 2008; doi: 10.1007/978-3-540-74234-0. Fig. 2.14 """ region = None - s13 = _Region1(623.15, 100)["s"] - s13s = _Region1(623.15, Ps_623)["s"] - sTPmax = _Region2(1073.15, 100)["s"] - s2ab = _Region2(1073.15, 4)["s"] + s13 = _Region1(623.15, 100).s + s13s = _Region1(623.15, Ps_623).s + sTPmax = _Region2(1073.15, 100).s + s2ab = _Region2(1073.15, 4).s # Left point in h-s plot - smin = _Region1(273.15, 100)["s"] - hmin = _Region1(273.15, Pmin)["h"] + smin = _Region1(273.15, 100).s + hmin = _Region1(273.15, Pmin).h # Right point in h-s plot _Pmax = _Region2(1073.15, Pmin) - hmax = _Pmax["h"] - smax = _Pmax["s"] + hmax = _Pmax.h + smax = _Pmax.s # Region 4 left and right point _sL = _Region1(273.15, Pmin) - h4l = _sL["h"] - s4l = _sL["s"] + h4l = _sL.h + s4l = _sL.s _sV = _Region2(273.15, Pmin) - h4v = _sV["h"] - s4v = _sV["s"] + h4v = _sV.h + s4v = _sV.s if smin <= s <= s13: hmin = h4l+(s-s4l)/(s4v-s4l)*(h4v-h4l) hs = _h1_s(s) T = _Backward1_T_Ps(100, s)-0.0218 - hmax = _Region1(T, 100)["h"] + hmax = _Region1(T, 100).h if hmin <= h < hs: region = 4 elif hs <= h <= hmax: @@ -4059,7 +4256,7 @@ def _Bound_hs(h, s): h13 = _h13_s(s) v = _Backward3_v_Ps(100, s)*(1+9.6e-5) T = _Backward3_T_Ps(100, s)-0.0248 - hmax = _Region3(1/v, T)["h"] + hmax = _Region3(1/v, T).h if hmin <= h < hs: region = 4 elif hs <= h < h13: @@ -4072,7 +4269,7 @@ def _Bound_hs(h, s): hs = _h3a_s(s) v = _Backward3_v_Ps(100, s)*(1+9.6e-5) T = _Backward3_T_Ps(100, s)-0.0248 - hmax = _Region3(1/v, T)["h"] + hmax = _Region3(1/v, T).h if hmin <= h < hs: region = 4 elif hs <= h <= hmax: @@ -4083,7 +4280,7 @@ def _Bound_hs(h, s): hs = _h2c3b_s(s) v = _Backward3_v_Ps(100, s)*(1+9.6e-5) T = _Backward3_T_Ps(100, s)-0.0248 - hmax = _Region3(1/v, T)["h"] + hmax = _Region3(1/v, T).h if hmin <= h < hs: region = 4 elif hs <= h <= hmax: @@ -4093,10 +4290,10 @@ def _Bound_hs(h, s): # Specific zone with 2-3 boundary in s shape hmin = h4l+(s-s4l)/(s4v-s4l)*(h4v-h4l) hs = _h2c3b_s(s) - h23max = _Region2(863.15, 100)["h"] - h23min = _Region2(623.15, Ps_623)["h"] + h23max = _Region2(863.15, 100).h + h23min = _Region2(623.15, Ps_623).h T = _Backward2_T_Ps(100, s)-0.019 - hmax = _Region2(T, 100)["h"] + hmax = _Region2(T, 100).h if hmin <= h < hs: region = 4 @@ -4114,7 +4311,7 @@ def _Bound_hs(h, s): hmin = h4l+(s-s4l)/(s4v-s4l)*(h4v-h4l) hs = _h2c3b_s(s) T = _Backward2_T_Ps(100, s)-0.019 - hmax = _Region2(T, 100)["h"] + hmax = _Region2(T, 100).h if hmin <= h < hs: region = 4 elif hs <= h <= hmax: @@ -4124,7 +4321,7 @@ def _Bound_hs(h, s): hmin = h4l+(s-s4l)/(s4v-s4l)*(h4v-h4l) hs = _h2ab_s(s) T = _Backward2_T_Ps(100, s)-0.019 - hmax = _Region2(T, 100)["h"] + hmax = _Region2(T, 100).h if hmin <= h < hs: region = 4 elif hs <= h <= hmax: @@ -4134,7 +4331,7 @@ def _Bound_hs(h, s): hmin = h4l+(s-s4l)/(s4v-s4l)*(h4v-h4l) hs = _h2ab_s(s) P = _Backward2_P_hs(h, s) - hmax = _Region2(1073.15, P)["h"] + hmax = _Region2(1073.15, P).h if hmin <= h < hs: region = 4 elif hs <= h <= hmax: @@ -4144,34 +4341,77 @@ def _Bound_hs(h, s): hmin = h4l+(s-s4l)/(s4v-s4l)*(h4v-h4l) hs = _h2ab_s(s) P = _Backward2_P_hs(h, s) - hmax = _Region2(1073.15, P)["h"] + hmax = _Region2(1073.15, P).h if hmin <= h < hs: region = 4 elif hs <= h <= hmax: region = 2 elif s4v <= s <= smax: - hmin = _Region2(273.15, Pmin)["h"] + hmin = _Region2(273.15, Pmin).h P = _Backward2a_P_hs(h, s) - hmax = _Region2(1073.15, P)["h"] + hmax = _Region2(1073.15, P).h if Pmin <= P <= 100 and hmin <= h <= hmax: region = 2 # Check region 5 if not region and \ - _Region5(1073.15, 50)["s"] < s <= _Region5(2273.15, Pmin)["s"] \ - and _Region5(1073.15, 50)["h"] < h <= _Region5(2273.15, Pmin)["h"]: - def funcion(par): - return (_Region5(par[0], par[1])["h"]-h, - _Region5(par[0], par[1])["s"]-s) - T, P = fsolve(funcion, [1400, 1]) + _Region5(1073.15, 50).s < s <= _Region5(2273.15, Pmin).s \ + and _Region5(1073.15, 50).h < h <= _Region5(2273.15, Pmin).h: + def funcion(par: Tuple[float, float]) -> Tuple[float, float]: + return (_Region5(par[0], par[1]).h-h, + _Region5(par[0], par[1]).s-s) + T, P = tuple(map(float, fsolve(funcion, [1400, 1]))) if 1073.15 < T <= 2273.15 and Pmin <= P <= 50: region = 5 return region -def prop0(T, P): +def _solve_P_x(P: float, x: float) -> IAPWS97Properties: + """Solve for properties using P & x.""" + T = _TSat_P(P) + if Pt <= P < Pc and 0 < x < 1: + propiedades = _Region4(P, x) + elif Pt <= P <= Ps_623 and x == 0: + propiedades = _Region1(T, P) + elif Pt <= P <= Ps_623 and x == 1: + propiedades = _Region2(T, P) + elif Ps_623 < P < Pc and x in (0, 1): + def rho_funcion(rho: float) -> float: + return _Region3(rho, T).P-P + rhoo = 1./_Backward3_sat_v_P(P, T, int(x)) + rho = float(fsolve(rho_funcion, rhoo)[0]) + propiedades = _Region3(rho, T) + elif P == Pc and 0 <= x <= 1: + propiedades = _Region3(rhoc, Tc) + else: + raise NotImplementedError("Incoming out of bound") + propiedades.x = x + return propiedades + + +def _solve_T_x(T: float, x: float) -> IAPWS97Properties: + """Solve for properties using T & x.""" + P = _PSat_T(T) + if 273.15 <= T < Tc and 0 < x < 1: + propiedades = _Region4(P, x) + elif 273.15 <= T <= 623.15 and x == 0: + propiedades = _Region1(T, P) + elif 273.15 <= T <= 623.15 and x == 1: + propiedades = _Region2(T, P) + elif 623.15 < T < Tc and x in (0, 1): + rho = 1./_Backward3_sat_v_P(P, T, int(x)) + propiedades = _Region3(rho, T) + elif T == Tc and 0 <= x <= 1: + propiedades = _Region3(rhoc, Tc) + else: + raise NotImplementedError("Incoming out of bound") + propiedades.x = x + return propiedades + + +def prop0(T: float, P: float) -> Dict[str, float]: """Ideal gas properties Parameters @@ -4204,6 +4444,8 @@ def prop0(T, P): Pr = P/1. go, gop, gopp, got, gott, gopt = Region5_cp0(Tr, Pr) + R = _global_R + prop0 = {} prop0["v"] = Pr*gop*R*T/P/1000 prop0["h"] = Tr*got*R*T @@ -4217,7 +4459,7 @@ def prop0(T, P): return prop0 -class IAPWS97(object): +class IAPWS97(_fase): """Class to model a state of liquid water or steam with the IAPWS-IF97 Parameters @@ -4315,298 +4557,107 @@ class IAPWS97(object): >>> water.cp0, water.cv0, water.h0, water.s0, water.w0 1.8714 1.4098 2594.66 9.471 444.93 """ - kwargs = {"T": 0.0, - "P": 0.0, + + kwargs = {"T": None, + "P": None, "x": None, "h": None, "s": None, - "v": 0.0, + "v": None, "l": 0.5893} status = 0 msg = "Unknown variables" + _constant_rhoc = rhoc + _constant_Tref = Tc + def __init__(self, **kwargs): + super().__init__() + self.v0: Optional[float] = None + self.h0: Optional[float] = None + self.u0: Optional[float] = None + self.s0: Optional[float] = None + self.a0: Optional[float] = None + self.g0: float = 0.0 + + self.cp0: Optional[float] = None + self.cv0: Optional[float] = None + self.cp0_cv: Optional[float] = None + self.w0: Optional[float] = None + self.gamma0: Optional[float] = None self.kwargs = IAPWS97.kwargs.copy() self.__call__(**kwargs) - def __call__(self, **kwargs): + def __call__(self, **kwargs) -> None: + """Invoke the solver.""" self.kwargs.update(kwargs) - if self.calculable: - self.status = 1 - self.calculo() - self.msg = "Solved" + self.calculo() + self.msg = "Solved" @property - def calculable(self): + def calculable(self) -> bool: """Check if class is calculable by its kwargs""" - self._thermo = "" - if self.kwargs["T"] and self.kwargs["P"]: - self._thermo = "TP" - elif self.kwargs["P"] and self.kwargs["h"] is not None: - self._thermo = "Ph" - elif self.kwargs["P"] and self.kwargs["s"] is not None: - self._thermo = "Ps" + return bool(self._calculable) + + def calculo(self) -> None: + """Calculate procedure""" + T = self.kwargs["T"] + P = self.kwargs["P"] + h = self.kwargs["h"] + s = self.kwargs["s"] + x = self.kwargs["x"] + + self.status = 0 + self._calculable = False + if T is not None and P is not None: + propiedades = _solve_T_P(T, P) + elif P is not None and h is not None: + propiedades = _solve_P_h(P, h) + elif P is not None and s is not None: + propiedades = _solve_P_s(P, s) # TODO: Add other pairs definitions options - # elif self.kwargs["P"] and self.kwargs["v"]: - # self._thermo = "Pv" - # elif self.kwargs["T"] and self.kwargs["s"] is not None: - # self._thermo = "Ts" - elif self.kwargs["h"] is not None and self.kwargs["s"] is not None: - self._thermo = "hs" - elif self.kwargs["T"] and self.kwargs["x"] is not None: - self._thermo = "Tx" - elif self.kwargs["P"] and self.kwargs["x"] is not None: - self._thermo = "Px" - return self._thermo - - def calculo(self): - propiedades = None - args = (self.kwargs[self._thermo[0]], self.kwargs[self._thermo[1]]) - if self._thermo == "TP": - T, P = args - region = _Bound_TP(T, P) - if region == 1: - propiedades = _Region1(T, P) - elif region == 2: - propiedades = _Region2(T, P) - elif region == 3: - if T == Tc and P == Pc: - rho = rhoc - else: - vo = _Backward3_v_PT(P, T) - - def funcion(rho): - return _Region3(rho, self.kwargs["T"])["P"]-P - rho = newton(funcion, 1/vo) - propiedades = _Region3(rho, T) - elif region == 5: - propiedades = _Region5(T, P) - else: - raise NotImplementedError("Incoming out of bound") - - elif self._thermo == "Ph": - P, h = args - region = _Bound_Ph(P, h) - if region == 1: - To = _Backward1_T_Ph(P, h) - T = newton(lambda T: _Region1(T, P)["h"]-h, To) - propiedades = _Region1(T, P) - elif region == 2: - To = _Backward2_T_Ph(P, h) - T = newton(lambda T: _Region2(T, P)["h"]-h, To) - propiedades = _Region2(T, P) - elif region == 3: - vo = _Backward3_v_Ph(P, h) - To = _Backward3_T_Ph(P, h) - - def funcion(par): - return (_Region3(par[0], par[1])["h"]-h, - _Region3(par[0], par[1])["P"]-P) - rho, T = fsolve(funcion, [1/vo, To]) - propiedades = _Region3(rho, T) - elif region == 4: - T = _TSat_P(P) - if T <= 623.15: - h1 = _Region1(T, P)["h"] - h2 = _Region2(T, P)["h"] - x = (h-h1)/(h2-h1) - propiedades = _Region4(P, x) - else: - vo = _Backward3_v_Ph(P, h) - To = _Backward3_T_Ph(P, h) - - def funcion(par): - return (_Region3(par[0], par[1])["h"]-h, - _Region3(par[0], par[1])["P"]-P) - rho, T = fsolve(funcion, [1/vo, To]) - propiedades = _Region3(rho, T) - elif region == 5: - T = newton(lambda T: _Region5(T, P)["h"]-h, 1500) - propiedades = _Region5(T, P) - else: - raise NotImplementedError("Incoming out of bound") - - elif self._thermo == "Ps": - P, s = args - region = _Bound_Ps(P, s) - if region == 1: - To = _Backward1_T_Ps(P, s) - T = newton(lambda T: _Region1(T, P)["s"]-s, To) - propiedades = _Region1(T, P) - elif region == 2: - To = _Backward2_T_Ps(P, s) - T = newton(lambda T: _Region2(T, P)["s"]-s, To) - propiedades = _Region2(T, P) - elif region == 3: - vo = _Backward3_v_Ps(P, s) - To = _Backward3_T_Ps(P, s) - - def funcion(par): - return (_Region3(par[0], par[1])["s"]-s, - _Region3(par[0], par[1])["P"]-P) - rho, T = fsolve(funcion, [1/vo, To]) - propiedades = _Region3(rho, T) - elif region == 4: - T = _TSat_P(P) - if T <= 623.15: - s1 = _Region1(T, P)["s"] - s2 = _Region2(T, P)["s"] - x = (s-s1)/(s2-s1) - propiedades = _Region4(P, x) - else: - vo = _Backward3_v_Ps(P, s) - To = _Backward3_T_Ps(P, s) - - def funcion(par): - return (_Region3(par[0], par[1])["s"]-s, - _Region3(par[0], par[1])["P"]-P) - rho, T = fsolve(funcion, [1/vo, To]) - propiedades = _Region3(rho, T) - elif region == 5: - T = newton(lambda T: _Region5(T, P)["s"]-s, 1500) - propiedades = _Region5(T, P) - else: - raise NotImplementedError("Incoming out of bound") - - elif self._thermo == "hs": - h, s = args - region = _Bound_hs(h, s) - if region == 1: - Po = _Backward1_P_hs(h, s) - To = _Backward1_T_Ph(Po, h) - - def funcion(par): - return (_Region1(par[0], par[1])["h"]-h, - _Region1(par[0], par[1])["s"]-s) - T, P = fsolve(funcion, [To, Po]) - propiedades = _Region1(T, P) - elif region == 2: - Po = _Backward2_P_hs(h, s) - To = _Backward2_T_Ph(Po, h) - - def funcion(par): - return (_Region2(par[0], par[1])["h"]-h, - _Region2(par[0], par[1])["s"]-s) - T, P = fsolve(funcion, [To, Po]) - propiedades = _Region2(T, P) - elif region == 3: - P = _Backward3_P_hs(h, s) - vo = _Backward3_v_Ph(P, h) - To = _Backward3_T_Ph(P, h) - - def funcion(par): - return (_Region3(par[0], par[1])["h"]-h, - _Region3(par[0], par[1])["s"]-s) - rho, T = fsolve(funcion, [1/vo, To]) - propiedades = _Region3(rho, T) - elif region == 4: - if round(s-sc, 6) == 0 and round(h-hc, 6) == 0: - propiedades = _Region3(rhoc, Tc) - - else: - To = _Backward4_T_hs(h, s) - if To < 273.15 or To > Tc: - To = 300 - - def funcion(par): - if par[1] < 0: - par[1] = 0 - elif par[1] > 1: - par[1] = 1 - if par[0] < 273.15: - par[0] = 273.15 - elif par[0] > Tc: - par[0] = Tc - - Po = _PSat_T(par[0]) - liquid = _Region1(par[0], Po) - vapor = _Region2(par[0], Po) - hl = liquid["h"] - sl = liquid["s"] - hv = vapor["h"] - sv = vapor["s"] - return (hv*par[1]+hl*(1-par[1])-h, - sv*par[1]+sl*(1-par[1])-s) - T, x = fsolve(funcion, [To, 0.5]) - P = _PSat_T(T) - - if Pt <= P < Pc and 0 < x < 1: - propiedades = _Region4(P, x) - elif Pt <= P <= Ps_623 and x == 0: - propiedades = _Region1(T, P) - elif region == 5: - def funcion(par): - return (_Region5(par[0], par[1])["h"]-h, - _Region5(par[0], par[1])["s"]-s) - T, P = fsolve(funcion, [1400, 1]) - propiedades = _Region5(T, P) - else: - raise NotImplementedError("Incoming out of bound") + # elif P is not None and v is not None: + # propiedades = self.solve_P_v(P, v) + # elif T is not None and s is not None: + # propiedades = self.solve_T_s(T, s) + elif h is not None and s is not None: + propiedades = _solve_h_s(h, s) + elif P is not None and x is not None: + propiedades = _solve_P_x(P, x) + self.sigma = _Tension(propiedades.T) + elif T is not None and x is not None: + propiedades = _solve_T_x(T, x) + self.sigma = _Tension(propiedades.T) + else: + return - elif self._thermo == "Px": - P, x = args - T = _TSat_P(P) - if Pt <= P < Pc and 0 < x < 1: - propiedades = _Region4(P, x) - elif Pt <= P <= Ps_623 and x == 0: - propiedades = _Region1(T, P) - elif Pt <= P <= Ps_623 and x == 1: - propiedades = _Region2(T, P) - elif Ps_623 < P < Pc and x in (0, 1): - def funcion(rho): - return _Region3(rho, T)["P"]-P - rhoo = 1./_Backward3_sat_v_P(P, T, x) - rho = fsolve(funcion, rhoo)[0] - propiedades = _Region3(rho, T) - elif P == Pc and 0 <= x <= 1: - propiedades = _Region3(rhoc, Tc) - else: - raise NotImplementedError("Incoming out of bound") - self.sigma = _Tension(T) - propiedades["x"] = x - - elif self._thermo == "Tx": - T, x = args - P = _PSat_T(T) - if 273.15 <= T < Tc and 0 < x < 1: - propiedades = _Region4(P, x) - elif 273.15 <= T <= 623.15 and x == 0: - propiedades = _Region1(T, P) - elif 273.15 <= T <= 623.15 and x == 1: - propiedades = _Region2(T, P) - elif 623.15 < T < Tc and x in (0, 1): - rho = 1./_Backward3_sat_v_P(P, T, x) - propiedades = _Region3(rho, T) - elif T == Tc and 0 <= x <= 1: - propiedades = _Region3(rhoc, Tc) - else: - raise NotImplementedError("Incoming out of bound") - self.sigma = _Tension(T) - propiedades["x"] = x + self.dofill(propiedades) + def dofill(self, propiedades: IAPWS97Properties) -> None: + """Finish filling the class.""" + self.status = 1 + self._calculable = True self.M = 18.015257 # kg/kmol self.Pc = Pc - self.Tc = Tc - self.rhoc = rhoc self.Tt = Tt self.Tb = Tb self.f_accent = f_acent self.dipole = Dipole - self.x = propiedades["x"] - self.region = propiedades["region"] + self.x = propiedades.x + self.region = propiedades.region self.name = "water" self.synonim = "R-718" self.CAS = "7732-18-5" - self.T = propiedades["T"] - self.P = propiedades["P"] - self.v = propiedades["v"] + self.T = propiedades.T + self.P = propiedades.P + self.v = propiedades.v self.rho = 1/self.v - self.phase = getphase(self.Tc, self.Pc, self.T, self.P, self.x, - self.region) - self.Tr = self.T/self.Tc + self.phase = getphase(self._constant_Tref, self.Pc, self.T, + self.P, self.x, self.region) + self.Tr = self.T/self._constant_Tref self.Pr = self.P/self.Pc # Ideal properties @@ -4656,34 +4707,35 @@ def funcion(rho): vapor = _Region2(self.T, self.P) self.fill(self.Vapor, vapor) - self.h = propiedades["h"] + self.h = propiedades.h self.u = self.h-self.P*1000*self.v - self.s = propiedades["s"] + self.s = propiedades.s self.a = self.u-self.T*self.s self.g = self.h-self.T*self.s self.sigma = _Tension(self.T) - self.Hvap = vapor["h"]-liquido["h"] - self.Svap = vapor["s"]-liquido["s"] + self.Hvap = vapor.h-liquido.h + self.Svap = vapor.s-liquido.s - def fill(self, fase, estado): - fase.v = estado["v"] + def fill(self, fase: _fase, estado: IAPWS97Properties) -> None: + """Fill phase properties""" + fase.v = estado.v fase.rho = 1/fase.v - fase.h = estado["h"] - fase.s = estado["s"] + fase.h = estado.h + fase.s = estado.s fase.u = fase.h-self.P*1000*fase.v fase.a = fase.u-self.T*fase.s fase.g = fase.h-self.T*fase.s - fase.cv = estado["cv"] - fase.cp = estado["cp"] + fase.cv = estado.cv + fase.cp = estado.cp fase.cp_cv = fase.cp/fase.cv - fase.w = estado["w"] + fase.w = estado.w - fase.Z = self.P*fase.v/R*1000/self.T - fase.alfav = estado["alfav"] - fase.xkappa = estado["kt"] + fase.Z = self.P*fase.v/_global_R*1000/self.T + fase.alfav = estado.alfav + fase.xkappa = estado.kt fase.kappas = -1/fase.v*self.derivative("v", "P", "s", fase) fase.joule = self.derivative("T", "P", "h", fase) @@ -4693,7 +4745,7 @@ def fill(self, fase, estado): fase.alfap = fase.alfav/self.P/fase.xkappa fase.betap = -1/self.P*self.derivative("P", "v", "T", fase) - fase.fi = exp((fase.g-self.g0)/R/self.T) + fase.fi = exp((fase.g-self.g0)/_global_R/self.T) fase.f = self.P*fase.fi fase.mu = _Viscosity(fase.rho, self.T) @@ -4709,42 +4761,50 @@ def fill(self, fase, estado): except NotImplementedError: fase.epsilon = None fase.Prandt = fase.mu*fase.cp*1000/fase.k - try: - fase.n = _Refractive(fase.rho, self.T, self.kwargs["l"]) - except NotImplementedError: - fase.n = None + if self.kwargs["l"] is not None: + try: + fase.n = _Refractive(fase.rho, self.T, self.kwargs["l"]) + except NotImplementedError: + fase.n = None - def derivative(self, z, x, y, fase): - """Wrapper derivative for custom derived properties - where x, y, z can be: P, T, v, u, h, s, g, a""" + def derivative(self, z: str, x: str, y: str, fase: _fase) -> float: + """ + Wrapper derivative for custom derived properties + where x, y, z can be: P, T, v, u, h, s, g, a + """ return deriv_G(self, z, x, y, fase) class IAPWS97_PT(IAPWS97): """Derivated class for direct P and T input""" - def __init__(self, P, T): + + def __init__(self, P: float, T: float): IAPWS97.__init__(self, T=T, P=P) class IAPWS97_Ph(IAPWS97): """Derivated class for direct P and h input""" - def __init__(self, P, h): + + def __init__(self, P: float, h: float): IAPWS97.__init__(self, P=P, h=h) class IAPWS97_Ps(IAPWS97): """Derivated class for direct P and s input""" - def __init__(self, P, s): + + def __init__(self, P: float, s: float): IAPWS97.__init__(self, P=P, s=s) class IAPWS97_Px(IAPWS97): """Derivated class for direct P and x input""" - def __init__(self, P, x): + + def __init__(self, P: float, x): IAPWS97.__init__(self, P=P, x=x) class IAPWS97_Tx(IAPWS97): """Derivated class for direct T and x input""" - def __init__(self, T, x): + + def __init__(self, T: float, x): IAPWS97.__init__(self, T=T, x=x) diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 0000000..1c4198e --- /dev/null +++ b/mypy.ini @@ -0,0 +1,30 @@ +[mypy] +mypy_path = stubs +files = **/*.py +python_version = 3.8 +#show_error_codes = True +#pretty = True +warn_unused_configs = True +warn_redundant_casts = True +strict_optional = True + +# Strict mode; enables the following flags: --warn-unused-configs, +# --disallow-any-generics, --disallow-subclassing-any, +# --disallow-untyped-calls, --disallow-untyped-defs, +# --disallow-incomplete-defs, --check-untyped-defs, +# --disallow-untyped-decorators, --no-implicit-optional, +# --warn-redundant-casts, --warn-unused-ignores, --warn-return-any, +# --no-implicit-reexport + +[iapws.*] +disallow_any_generics = True +disallow_subclassing_any = True +disallow_untyped_calls = True +disallow_untyped_defs = True +disallow_incomplete_defs = True +check_untyped_defs = True +disallow_untyped_decorators = True +no_implicit_optional = True +warn_unused_ignores = True +warn_return_any = True +no_implicit_reexport = True diff --git a/plots.py b/plots.py old mode 100644 new mode 100755 index 8b7ff3d..2b79bac --- a/plots.py +++ b/plots.py @@ -1,10 +1,11 @@ #!/usr/bin/python3 # -*- coding: utf-8 -*- - +"""Generate various plots using IAPWS.""" from math import pi, atan, log import matplotlib.pyplot as plt import numpy as np +from typing import Dict, List, Any import iapws from iapws._iapws import Pt, Pc, Tc @@ -144,7 +145,7 @@ # Calculate isoquality lines print("Calculating isoquality lines...") -Q = {} +Q: Dict[str, Dict[str, List[Any]]] = {} for q in isoq: Q["%s" % q] = {} txt = "x=%s" % q @@ -160,7 +161,7 @@ # Calculate isotherm lines if xAxis != "T" and yAxis != "T": print("Calculating isotherm lines...") - T_ = {} + T_: Dict[str, Dict[str, List[Any]]] = {} for T in isoT: T_["%s" % T] = {} print(" T=%sºC" % T) @@ -172,9 +173,9 @@ else: sat = False pts = [] - for p in Pl: + for pressure in Pl: try: - point = fluid(P=p, T=T+273.15) + point = fluid(P=pressure, T=T+273.15) if fluid == iapws.IAPWS97 and not region5 and \ point.region == 5: continue @@ -200,7 +201,7 @@ # Calculate isobar lines if xAxis != "P" and yAxis != "P": print("Calculating isobar lines...") - P_ = {} + P_: Dict[str, Dict[str, List[Any]]] = {} for P in isoP: print(" P=%sMPa" % P) P_["%s" % P] = {} @@ -214,7 +215,7 @@ pts = [] for t in Tl: try: - point = fluid(P=P, T=t+273.15) + point = fluid(P=P, T=float(t)+273.15) if fluid == iapws.IAPWS97 and not region5 and \ point.region == 5: continue @@ -240,14 +241,16 @@ # Calculate isoenthalpic lines if xAxis != "h" and yAxis != "h": print("Calculating isoenthalpic lines...") - H_ = {} - for h in isoh: + H_: Dict[str, Dict[str, List[Any]]] = {} + for numpy_h in isoh: + # Convert numpy.Float64 to Python float. + h = float(numpy_h) print(" h=%skJ/kg" % h) H_["%s" % h] = {} pts = [] - for p in Pl: + for pressure in Pl: try: - point = fluid(P=p, h=h) + point = fluid(P=pressure, h=h) if fluid == iapws.IAPWS97 and not region5 and \ point.region == 5: continue @@ -267,14 +270,14 @@ # Calculate isoentropic lines if xAxis != "s" and yAxis != "s": print("Calculating isoentropic lines...") - S_ = {} + S_: Dict[str, Dict[str, List[Any]]] = {} for s in isos: print(" s=%skJ/kgK" % s) S_["%s" % s] = {} pts = [] - for p in Pl: + for pressure in Pl: try: - point = fluid(P=p, s=s) + point = fluid(P=pressure, s=s) if fluid == iapws.IAPWS97 and not region5 and \ point.region == 5: continue @@ -296,13 +299,13 @@ print("Calculating isochor lines...") for v in isov: print(" v=%s" % v) - pts = [iapws.IAPWS95(T=t, v=v) for t in Tl] + pts95 = [iapws.IAPWS95(T=float(t), v=v) for t in Tl] x = [] y = [] - for p in pts: - if p.status: - x.append(p.__getattribute__(xAxis)) - y.append(p.__getattribute__(yAxis)) + for p95 in pts95: + if p95.status: + x.append(p95.__getattribute__(xAxis)) + y.append(p95.__getattribute__(yAxis)) plt.plot(x, y, **isov_kw) @@ -310,17 +313,17 @@ if regionBoundary: # Boundary 1-3 Po = _PSat_T(623.15) - P = np.linspace(Po, 100, points) - pts = [fluid(P=p, T=623.15) for p in P] + numpy_pressure_points = np.linspace(Po, 100, points) + pts = [fluid(P=float(p), T=623.15) for p in numpy_pressure_points] x = [p.__getattribute__(xAxis) for p in pts] y = [p.__getattribute__(yAxis) for p in pts] plt.plot(x, y, **isosat_kw) # Boundary 2-3 - T = np.linspace(623.15, 863.15) - P = [_P23_T(t) for t in T] - P[-1] = 100 # Avoid round problem with value out of range > 100 MPa - pts = [fluid(P=p, T=t) for p, t in zip(P, T)] + numpy_temp_points = list(map(float, np.linspace(623.15, 863.15))) + Psf = [_P23_T(t) for t in numpy_temp_points] + Psf[-1] = 100.0 # Avoid round problem with value out of range > 100 MPa + pts = [fluid(P=p, T=t) for p, t in zip(Psf, numpy_temp_points)] x = [p.__getattribute__(xAxis) for p in pts] y = [p.__getattribute__(yAxis) for p in pts] plt.plot(x, y, **isosat_kw) @@ -395,7 +398,9 @@ plt.annotate(txt, (x[i], y[i]), rotation=rot, **labelP_kw) if xAxis != "h" and yAxis != "h": - for h in isoh: + for numpy_h in isoh: + # Convert numpy.Float64 to Python float. + h = float(numpy_h) x = H_["%s" % h]["x"] y = H_["%s" % h]["y"] diff --git a/setup.py b/setup.py old mode 100644 new mode 100755 index 25c6cdd..bc9af3d --- a/setup.py +++ b/setup.py @@ -1,3 +1,7 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +"""Install IAPWS module using setuptools.""" + from setuptools import setup import io # for backwards compatibility with Python 2 @@ -36,6 +40,6 @@ "Topic :: Scientific/Engineering", "Topic :: Scientific/Engineering :: Chemistry", "Topic :: Scientific/Engineering :: Physics", - "Topic :: Software Development :: Libraries :: Python Modules" - ] + "Topic :: Software Development :: Libraries :: Python Modules", + ], ) diff --git a/stubs/matplotlib/__init__.pyi b/stubs/matplotlib/__init__.pyi new file mode 100644 index 0000000..3d329c9 --- /dev/null +++ b/stubs/matplotlib/__init__.pyi @@ -0,0 +1,5 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +"""IAPWS stubs for matplotlib""" + +# Nothing at top level. diff --git a/stubs/matplotlib/pyplot.pyi b/stubs/matplotlib/pyplot.pyi new file mode 100644 index 0000000..25bc740 --- /dev/null +++ b/stubs/matplotlib/pyplot.pyi @@ -0,0 +1,41 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +"""IAPWS stubs for matplotlib.plylot""" + +from typing import Mapping, Tuple, Optional, Any + +# Just what we need... + +def title(label: str) -> None: + ... + +def xlabel(xlabel: str) -> None: + ... + +def ylabel(ylabel: str) -> None: + ... + +def xlim() -> Tuple[float, float]: + ... + +def ylim() -> Tuple[float, float]: + ... + +def grid(b: Optional[bool] = None) -> None: + ... + +def xscale(value: str) -> None: + ... + +def yscale(value: str) -> None: + ... + +def annotate(txt: str, xy: Tuple[float, float], + rotation: Optional[float] = None, **kwargs) -> None: + ... + +def plot(x: Any, y: Any, **kwargs) -> None: + ... + +def show() -> None: + ... diff --git a/stubs/numpy/__init__.pyi b/stubs/numpy/__init__.pyi new file mode 100644 index 0000000..8e723ab --- /dev/null +++ b/stubs/numpy/__init__.pyi @@ -0,0 +1,37 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +"""IAPWS stubs for numpy""" + +from typing import Tuple, List, Any + +# With this mypy type definition, we shouldn't be able to do anything +# with the numpy.Float64 except cast them to the Python floats. +class Float64(object): + def __float__(self) -> float: + ... + +# We can still iterate over the ndarrays however, because this +# definition lies and says ndarray is derived from Tuple. +class ndarray(Tuple[Float64, ...]): + ... + +def linspace(start: Any, stop: Any, num: int = 1) -> ndarray: + ... + +def concatenate(a: List[Any]) -> ndarray: + ... + +def arange(start: Any, stop: Any, step: Any) -> ndarray: + ... + +def log10(Any) -> Float64: + ... + +def log(Any) -> Float64: + ... + +def exp(Any) -> Float64: + ... + +def logspace(start: Any, stop: Any, num: int) -> ndarray: + ... diff --git a/stubs/scipy/__init__.pyi b/stubs/scipy/__init__.pyi new file mode 100644 index 0000000..901debb --- /dev/null +++ b/stubs/scipy/__init__.pyi @@ -0,0 +1,5 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +"""IAPWS stubs for scipy""" + +# Nothing at top level. diff --git a/stubs/scipy/constants.pyi b/stubs/scipy/constants.pyi new file mode 100644 index 0000000..0d431d7 --- /dev/null +++ b/stubs/scipy/constants.pyi @@ -0,0 +1,7 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +"""IAPWS stubs for scipy.constants""" + +import numpy + +Boltzmann: numpy.Float64 diff --git a/stubs/scipy/optimize.pyi b/stubs/scipy/optimize.pyi new file mode 100644 index 0000000..73aa093 --- /dev/null +++ b/stubs/scipy/optimize.pyi @@ -0,0 +1,40 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +"""IAPWS stubs for scipy.optimize""" + +from typing import Tuple, Dict, Callable, Optional, Literal, Union, Any, overload +import numpy + +# With no full_output option, fsolve returns a numpy.ndarray. +@overload +def fsolve( + func: Callable[..., Any], + x0: Union[float, int, Tuple[Any], Any])-> numpy.ndarray: + ... + +# If someone specified a False full_output option the return value is the same. +@overload +def fsolve( + func: Callable[..., Any], + x0: Union[float, int, Tuple[Any], Any], + full_output: Literal[False])-> numpy.ndarray: + ... + +# With a True full_option, the return value is a different format. +@overload +def fsolve( + func: Callable[..., Any], + x0: Union[float, int, Tuple[Any], Any], + full_output: Literal[True] +) -> Tuple[numpy.ndarray, Dict[str, Any], int, str]: + ... + + +def minimize(fun: Callable[..., Any], x0: Tuple[float], + bounds: Optional[Tuple[Tuple[float, ...]]] = None, + jac: Optional[Callable[..., Any]] = None): + ... + + +def newton(func: Callable[..., Any], x0: float) -> numpy.Float64: + ... diff --git a/stubs/setuptools.pyi b/stubs/setuptools.pyi new file mode 100644 index 0000000..32f829d --- /dev/null +++ b/stubs/setuptools.pyi @@ -0,0 +1,22 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +"""IAPWS stubs for setuptools""" + +from typing import Any, List + +def setup(name: str, + version: Any, + packages: List[str], + include_package_data: bool, + author: str, + author_email: str, + url: str, + download_url: str, + description: str, + long_description: str, + license: str, + python_requires: str, + install_requires: List[str], + classifiers: List[str] +) -> None: + ... diff --git a/test.py b/test.py old mode 100644 new mode 100755 index 74b16ec..af53136 --- a/test.py +++ b/test.py @@ -1,5 +1,6 @@ #!/usr/bin/python # -*- coding: utf-8 -*- +"""Test IAPWS mopdule.""" from math import log import sys @@ -41,7 +42,8 @@ class Test(unittest.TestCase): Global unittest for module Run for python2 and python3 before to release distribution """ - def test_Helmholtz(self): + + def test_Helmholtz(self) -> None: """Table 6 from IAPWS95, pag 14""" T = 500 rho = 838.025 @@ -50,28 +52,28 @@ def test_Helmholtz(self): tau = fluid.Tc/T ideal = fluid._phi0(tau, delta) - self.assertEqual(round(ideal["fio"], 8), 2.04797733) - self.assertEqual(round(ideal["fiod"], 9), 0.384236747) - self.assertEqual(round(ideal["fiodd"], 9), -0.147637878) - self.assertEqual(round(ideal["fiot"], 8), 9.04611106) - self.assertEqual(round(ideal["fiott"], 8), -1.93249185) - self.assertEqual(round(ideal["fiodt"], 8), 0.0) - - res = fluid._phir(tau, delta) - self.assertEqual(round(res["fir"], 8), -3.42693206) - self.assertEqual(round(res["fird"], 9), -0.364366650) - self.assertEqual(round(res["firdd"], 9), 0.856063701) - self.assertEqual(round(res["firt"], 8), -5.81403435) - self.assertEqual(round(res["firtt"], 8), -2.23440737) - self.assertEqual(round(res["firdt"], 8), -1.12176915) + self.assertEqual(round(ideal.fi, 8), 2.04797733) + self.assertEqual(round(ideal.fid, 9), 0.384236747) + self.assertEqual(round(ideal.fidd, 9), -0.147637878) + self.assertEqual(round(ideal.fit, 8), 9.04611106) + self.assertEqual(round(ideal.fitt, 8), -1.93249185) + self.assertEqual(round(ideal.fidt, 8), 0.0) + + res = fluid.residual.helmholtz(tau, delta) + self.assertEqual(round(res.fi, 8), -3.42693206) + self.assertEqual(round(res.fid, 9), -0.364366650) + self.assertEqual(round(res.fidd, 9), 0.856063701) + self.assertEqual(round(res.fit, 8), -5.81403435) + self.assertEqual(round(res.fitt, 8), -2.23440737) + self.assertEqual(round(res.fidt, 8), -1.12176915) # Revised release of 2018 # Virial coefficient in Table 3 - vir = fluid._virial(600) + vir = fluid.residual._virial(600, fluid.Tc) self.assertEqual(round(vir["B"]/fluid.rhoc, 11), -0.555366808e-2) self.assertEqual(round(vir["C"]/fluid.rhoc**2, 14), -0.669015050e-5) - def test_phase(self): + def test_phase(self) -> None: """Table 7 from IAPWS95, pag 14""" state = IAPWS95(rho=996.556, T=300) # See footnote for imprecise P value in last significant figures @@ -140,7 +142,7 @@ def test_phase(self): self.assertEqual(round(state.w, 5), 2019.33608) self.assertEqual(round(state.s, 8), 4.17223802) - def test_saturation(self): + def test_saturation(self) -> None: """Table 8 from IAPWS95, pag 14""" fluid = IAPWS95() @@ -150,10 +152,10 @@ def test_saturation(self): self.assertEqual(round(Ps, 9), 0.698451167) self.assertEqual(round(rhol, 6), 999.887406) self.assertEqual(round(rhov, 11), 0.00550664919) - self.assertEqual(round(liquid["h"], 8), 7.75972202) - self.assertEqual(round(vapor["h"], 5), 2504.28995) - self.assertEqual(round(liquid["s"], 10), 0.0283094670) - self.assertEqual(round(vapor["s"], 8), 9.10660121) + self.assertEqual(round(liquid.h, 8), 7.75972202) + self.assertEqual(round(vapor.h, 5), 2504.28995) + self.assertEqual(round(liquid.s, 10), 0.0283094670) + self.assertEqual(round(vapor.s, 8), 9.10660121) rhol, rhov, Ps = fluid._saturation(450) liquid = fluid._Helmholtz(rhol, 450) @@ -161,10 +163,10 @@ def test_saturation(self): self.assertEqual(round(Ps, 6), 932.203564) self.assertEqual(round(rhol, 6), 890.341250) self.assertEqual(round(rhov, 8), 4.81200360) - self.assertEqual(round(liquid["h"], 6), 749.161585) - self.assertEqual(round(vapor["h"], 5), 2774.41078) - self.assertEqual(round(liquid["s"], 8), 2.10865845) - self.assertEqual(round(vapor["s"], 8), 6.60921221) + self.assertEqual(round(liquid.h, 6), 749.161585) + self.assertEqual(round(vapor.h, 5), 2774.41078) + self.assertEqual(round(liquid.s, 8), 2.10865845) + self.assertEqual(round(vapor.s, 8), 6.60921221) rhol, rhov, Ps = fluid._saturation(625) liquid = fluid._Helmholtz(rhol, 625) @@ -172,12 +174,12 @@ def test_saturation(self): self.assertEqual(round(Ps, 4), 16908.2693) self.assertEqual(round(rhol, 6), 567.090385) self.assertEqual(round(rhov, 6), 118.290280) - self.assertEqual(round(liquid["h"], 5), 1686.26976) - self.assertEqual(round(vapor["h"], 5), 2550.71625) - self.assertEqual(round(liquid["s"], 8), 3.80194683) - self.assertEqual(round(vapor["s"], 8), 5.18506121) + self.assertEqual(round(liquid.h, 5), 1686.26976) + self.assertEqual(round(vapor.h, 5), 2550.71625) + self.assertEqual(round(liquid.s, 8), 3.80194683) + self.assertEqual(round(vapor.s, 8), 5.18506121) - def test_LowT(self): + def test_LowT(self) -> None: """Table 3, pag 5""" fluid = IAPWS95() fex, fext, fextt = fluid._phiex(50) @@ -194,7 +196,7 @@ def test_LowT(self): fluid = IAPWS95(T=120, P=0.1) - def test_Melting(self): + def test_Melting(self) -> None: """Table 3, pag 7""" self.assertRaises(NotImplementedError, _Sublimation_Pressure, 49) self.assertRaises(NotImplementedError, _Sublimation_Pressure, 274) @@ -206,7 +208,7 @@ def test_Melting(self): self.assertEqual(round(_Melting_Pressure(320, "VI"), 2), 1356.76) self.assertEqual(round(_Melting_Pressure(550, "VII"), 2), 6308.71) - def test_Viscosity_1(self): + def test_Viscosity_1(self) -> None: """Table 4, pag 8""" self.assertEqual(round(_Viscosity(998, 298.15)*1e6, 6), 889.735100) self.assertEqual(round(_Viscosity(1200, 298.15)*1e6, 6), 1437.649467) @@ -234,7 +236,7 @@ def test_Viscosity_1(self): fluid = IAPWS95(rho=422, T=647.35) self.assertEqual(round(fluid.mu*1e6, 6), 49.436256) - def test_ThCond(self): + def test_ThCond(self) -> None: """Table 4, pag 10""" self.assertEqual(round(_ThCond(0, 298.15)*1000, 7), 18.4341883) self.assertEqual(round(_ThCond(998, 298.15)*1000, 6), 607.712868) @@ -242,40 +244,40 @@ def test_ThCond(self): self.assertEqual(round(_ThCond(0, 873.15)*1000, 7), 79.1034659) # Table 5, pag 10 - fluid = IAPWS95(rho=1, T=647.35) - self.assertEqual(round(fluid.k*1000, 7), 51.9298924) - fluid = IAPWS95(rho=122, T=647.35) - self.assertEqual(round(fluid.k*1000, 6), 130.922885) - fluid = IAPWS95(rho=222, T=647.35) - self.assertEqual(round(fluid.k*1000, 6), 367.787459) - fluid = IAPWS95(rho=272, T=647.35) - self.assertEqual(round(fluid.k*1000, 6), 757.959776) - fluid = IAPWS95(rho=322, T=647.35) - self.assertEqual(round(fluid.k*1000, 5), 1443.75556) - fluid = IAPWS95(rho=372, T=647.35) - self.assertEqual(round(fluid.k*1000, 6), 650.319402) - fluid = IAPWS95(rho=422, T=647.35) - self.assertEqual(round(fluid.k*1000, 6), 448.883487) - fluid = IAPWS95(rho=750, T=647.35) - self.assertEqual(round(fluid.k*1000, 6), 600.961346) + fluid95 = IAPWS95(rho=1, T=647.35) + self.assertEqual(round(fluid95.k*1000, 7), 51.9298924) + fluid95 = IAPWS95(rho=122, T=647.35) + self.assertEqual(round(fluid95.k*1000, 6), 130.922885) + fluid95 = IAPWS95(rho=222, T=647.35) + self.assertEqual(round(fluid95.k*1000, 6), 367.787459) + fluid95 = IAPWS95(rho=272, T=647.35) + self.assertEqual(round(fluid95.k*1000, 6), 757.959776) + fluid95 = IAPWS95(rho=322, T=647.35) + self.assertEqual(round(fluid95.k*1000, 5), 1443.75556) + fluid95 = IAPWS95(rho=372, T=647.35) + self.assertEqual(round(fluid95.k*1000, 6), 650.319402) + fluid95 = IAPWS95(rho=422, T=647.35) + self.assertEqual(round(fluid95.k*1000, 6), 448.883487) + fluid95 = IAPWS95(rho=750, T=647.35) + self.assertEqual(round(fluid95.k*1000, 6), 600.961346) # Industrial formulation, Table 7, 8, 9 - fluid = IAPWS97(T=620, P=20) - self.assertEqual(round(fluid.k*1000, 6), 481.485195) - fluid = IAPWS97(T=620, P=50) - self.assertEqual(round(fluid.k*1000, 6), 545.038940) - fluid = IAPWS97(T=650, P=0.3) - self.assertEqual(round(fluid.k*1000, 7), 52.2311024) - fluid = IAPWS97(T=800, P=50) - self.assertEqual(round(fluid.k*1000, 6), 177.709914) - P = _Region3(T=647.35, rho=222)["P"] - fluid = IAPWS97(T=647.35, P=P) - self.assertEqual(round(fluid.k*1000, 6), 366.879411) - P = _Region3(T=647.35, rho=322)["P"] - fluid = IAPWS97(T=647.35, P=P) - self.assertEqual(round(fluid.k*1000, 5), 1241.82415) - - def test_Tension(self): + fluid97 = IAPWS97(T=620, P=20) + self.assertEqual(round(fluid97.k*1000, 6), 481.485195) + fluid97 = IAPWS97(T=620, P=50) + self.assertEqual(round(fluid97.k*1000, 6), 545.038940) + fluid97 = IAPWS97(T=650, P=0.3) + self.assertEqual(round(fluid97.k*1000, 7), 52.2311024) + fluid97 = IAPWS97(T=800, P=50) + self.assertEqual(round(fluid97.k*1000, 6), 177.709914) + P = _Region3(T=647.35, rho=222).P + fluid97 = IAPWS97(T=647.35, P=P) + self.assertEqual(round(fluid97.k*1000, 6), 366.879411) + P = _Region3(T=647.35, rho=322).P + fluid97 = IAPWS97(T=647.35, P=P) + self.assertEqual(round(fluid97.k*1000, 5), 1241.82415) + + def test_Tension(self) -> None: """Selected values from table 1""" self.assertRaises(NotImplementedError, _Tension, 230) self.assertEqual(round(_Tension(273.16)*1000, 2), 75.65) @@ -299,49 +301,67 @@ def test_Tension(self): self.assertEqual(round(_Tension(623.15)*1000, 2), 3.67) self.assertEqual(round(_Tension(643.15)*1000, 2), 0.39) - def test_Dielect(self): + def test_Dielect(self) -> None: """Table 4, pag 8""" fluid = IAPWS95(P=0.101325, T=240) + assert(fluid.epsilon is not None) self.assertEqual(round(fluid.epsilon, 5), 104.34982) fluid = IAPWS95(P=0.101325, T=300) + assert(fluid.epsilon is not None) self.assertEqual(round(fluid.epsilon, 5), 77.74735) fluid = IAPWS95(P=10, T=300) + assert(fluid.epsilon is not None) self.assertEqual(round(fluid.epsilon, 5), 78.11269) fluid = IAPWS95(P=1000, T=300) + assert(fluid.epsilon is not None) self.assertEqual(round(fluid.epsilon, 5), 103.69632) fluid = IAPWS95(P=10, T=650) + assert(fluid.epsilon is not None) self.assertEqual(round(fluid.epsilon, 5), 1.26715) fluid = IAPWS95(P=100, T=650) + assert(fluid.epsilon is not None) self.assertEqual(round(fluid.epsilon, 5), 17.71733) fluid = IAPWS95(P=500, T=650) + assert(fluid.epsilon is not None) self.assertEqual(round(fluid.epsilon, 5), 26.62132) fluid = IAPWS95(P=10, T=870) + assert(fluid.epsilon is not None) self.assertEqual(round(fluid.epsilon, 5), 1.12721) fluid = IAPWS95(P=100, T=870) + assert(fluid.epsilon is not None) self.assertEqual(round(fluid.epsilon, 5), 4.98281) fluid = IAPWS95(P=500, T=870) + assert(fluid.epsilon is not None) self.assertEqual(round(fluid.epsilon, 5), 15.09746) - def test_Refractive(self): + def test_Refractive(self) -> None: """Selected values from table 3, pag 6""" fluid = IAPWS95(P=0.1, T=273.15, l=0.2265) + assert(fluid.n is not None) self.assertEqual(round(fluid.n, 6), 1.394527) fluid = IAPWS95(P=10., T=273.15, l=0.2265) + assert(fluid.n is not None) self.assertEqual(round(fluid.n, 6), 1.396526) fluid = IAPWS95(P=1., T=373.15, l=0.2265) + assert(fluid.n is not None) self.assertEqual(round(fluid.n, 6), 1.375622) fluid = IAPWS95(P=100., T=373.15, l=0.2265) + assert(fluid.n is not None) self.assertEqual(round(fluid.n, 6), 1.391983) fluid = IAPWS95(P=0.1, T=473.15, l=0.589) + assert(fluid.n is not None) self.assertEqual(round(fluid.n, 7), 1.0001456) fluid = IAPWS95(P=1., T=773.15, l=0.589) + assert(fluid.n is not None) self.assertEqual(round(fluid.n, 7), 1.0008773) fluid = IAPWS95(P=10., T=273.15, l=1.01398) + assert(fluid.n is not None) self.assertEqual(round(fluid.n, 6), 1.327710) fluid = IAPWS95(P=100., T=473.15, l=1.01398) + assert(fluid.n is not None) self.assertEqual(round(fluid.n, 6), 1.298369) - def test_kw(self): + def test_kw(self) -> None: """Table 3, pag 5""" self.assertRaises(NotImplementedError, _Kw, *(1000, 270)) self.assertEqual(round(_Kw(1000, 300), 6), 13.906565) @@ -350,7 +370,7 @@ def test_kw(self): self.assertEqual(round(_Kw(200, 800), 6), 15.089765) self.assertEqual(round(_Kw(1200, 800), 6), 6.438330) - def test_liquid(self): + def test_liquid(self) -> None: """Table 8, pag 11""" liq = _Liquid(260) self.assertEqual(round(liq["g"], 7), -1.2659892) @@ -398,7 +418,7 @@ def test_liquid(self): self.assertWarns(Warning, _Liquid, *(375, 0.2)) self.assertRaises(NotImplementedError, _Liquid, *(375, 0.4)) - def test_superCooled(self): + def test_superCooled(self) -> None: """Table 5, pag 9""" liq = _Supercooled(273.15, 0.101325) self.assertEqual(round(liq["x"], 8), 0.09665472) @@ -448,7 +468,7 @@ def test_superCooled(self): self.assertRaises(NotImplementedError, _Supercooled, *(200, 100)) self.assertRaises(NotImplementedError, _Supercooled, *(180, 300)) - def test_auxiliarySaturation(self): + def test_auxiliarySaturation(self) -> None: """Table 1 pag 7""" fluid = IAPWS95() self.assertEqual(round(fluid._Vapor_Pressure(273.16), 9), 0.000611657) @@ -484,32 +504,31 @@ def test_auxiliarySaturation(self): self.assertEqual(round(fluid._Liquid_Entropy(647.096), 3), 4.410) self.assertEqual(round(fluid._Vapor_Entropy(647.096), 3), 4.410) - def test_IAPWS97_1(self): + def test_IAPWS97_1(self) -> None: """Table 5, pag 9""" - fluid = _Region1(300, 3) - self.assertEqual(round(fluid["v"], 11), 0.00100215168) - self.assertEqual(round(fluid["h"], 6), 115.331273) - self.assertEqual(round(fluid["h"]-fluid["P"]*1000*fluid["v"], 6), 112.324818) - self.assertEqual(round(fluid["s"], 9), 0.392294792) - self.assertEqual(round(fluid["cp"], 8), 4.17301218) - self.assertEqual(round(fluid["w"], 5), 1507.73921) + self.assertEqual(round(fluid.v, 11), 0.00100215168) + self.assertEqual(round(fluid.h, 6), 115.331273) + self.assertEqual(round(fluid.h-fluid.P*1000*fluid.v, 6), 112.324818) + self.assertEqual(round(fluid.s, 9), 0.392294792) + self.assertEqual(round(fluid.cp, 8), 4.17301218) + self.assertEqual(round(fluid.w, 5), 1507.73921) fluid = _Region1(300, 80) - self.assertEqual(round(fluid["v"], 12), 0.000971180894) - self.assertEqual(round(fluid["h"], 6), 184.142828) - self.assertEqual(round(fluid["h"]-fluid["P"]*1000*fluid["v"], 6), 106.448356) - self.assertEqual(round(fluid["s"], 9), 0.368563852) - self.assertEqual(round(fluid["cp"], 8), 4.01008987) - self.assertEqual(round(fluid["w"], 5), 1634.69054) + self.assertEqual(round(fluid.v, 12), 0.000971180894) + self.assertEqual(round(fluid.h, 6), 184.142828) + self.assertEqual(round(fluid.h-fluid.P*1000*fluid.v, 6), 106.448356) + self.assertEqual(round(fluid.s, 9), 0.368563852) + self.assertEqual(round(fluid.cp, 8), 4.01008987) + self.assertEqual(round(fluid.w, 5), 1634.69054) fluid = _Region1(500, 3) - self.assertEqual(round(fluid["v"], 10), 0.0012024180) - self.assertEqual(round(fluid["h"], 6), 975.542239) - self.assertEqual(round(fluid["h"]-fluid["P"]*1000*fluid["v"], 6), 971.934985) - self.assertEqual(round(fluid["s"], 9), 2.58041912) - self.assertEqual(round(fluid["cp"], 8), 4.65580682) - self.assertEqual(round(fluid["w"], 5), 1240.71337) + self.assertEqual(round(fluid.v, 10), 0.0012024180) + self.assertEqual(round(fluid.h, 6), 975.542239) + self.assertEqual(round(fluid.h-fluid.P*1000*fluid.v, 6), 971.934985) + self.assertEqual(round(fluid.s, 9), 2.58041912) + self.assertEqual(round(fluid.cp, 8), 4.65580682) + self.assertEqual(round(fluid.w, 5), 1240.71337) # _Backward1_T_Ph Table 7 pag 11 self.assertEqual(round(_Backward1_T_Ph(3, 500), 6), 391.798509) @@ -526,7 +545,7 @@ def test_IAPWS97_1(self): self.assertEqual(round(_Backward1_P_hs(90, 0), 8), 91.92954727) self.assertEqual(round(_Backward1_P_hs(1500, 3.4), 8), 58.68294423) - def test_IAPWS97_2(self): + def test_IAPWS97_2(self) -> None: """Table 15, pag 17""" # Auxiliary equation for the boundary 2-3 self.assertEqual(round(_P23_T(623.15), 7), 16.5291643) @@ -538,28 +557,28 @@ def test_IAPWS97_2(self): self.assertEqual(round(_hab_s(7), 6), 3376.437884) fluid = _Region2(300, 0.0035) - self.assertEqual(round(fluid["v"], 7), 39.4913866) - self.assertEqual(round(fluid["h"], 5), 2549.91145) - self.assertEqual(round(fluid["h"]-fluid["P"]*1000*fluid["v"], 5), 2411.69160) - self.assertEqual(round(fluid["s"], 8), 8.52238967) - self.assertEqual(round(fluid["cp"], 8), 1.91300162) - self.assertEqual(round(fluid["w"], 6), 427.920172) + self.assertEqual(round(fluid.v, 7), 39.4913866) + self.assertEqual(round(fluid.h, 5), 2549.91145) + self.assertEqual(round(fluid.h-fluid.P*1000*fluid.v, 5), 2411.69160) + self.assertEqual(round(fluid.s, 8), 8.52238967) + self.assertEqual(round(fluid.cp, 8), 1.91300162) + self.assertEqual(round(fluid.w, 6), 427.920172) fluid = _Region2(700, 0.0035) - self.assertEqual(round(fluid["v"], 7), 92.3015898) - self.assertEqual(round(fluid["h"], 5), 3335.68375) - self.assertEqual(round(fluid["h"]-fluid["P"]*1000*fluid["v"], 5), 3012.62819) - self.assertEqual(round(fluid["s"], 7), 10.1749996) - self.assertEqual(round(fluid["cp"], 8), 2.08141274) - self.assertEqual(round(fluid["w"], 6), 644.289068) + self.assertEqual(round(fluid.v, 7), 92.3015898) + self.assertEqual(round(fluid.h, 5), 3335.68375) + self.assertEqual(round(fluid.h-fluid.P*1000*fluid.v, 5), 3012.62819) + self.assertEqual(round(fluid.s, 7), 10.1749996) + self.assertEqual(round(fluid.cp, 8), 2.08141274) + self.assertEqual(round(fluid.w, 6), 644.289068) fluid = _Region2(700, 30) - self.assertEqual(round(fluid["v"], 11), 0.00542946619) - self.assertEqual(round(fluid["h"], 5), 2631.49474) - self.assertEqual(round(fluid["h"]-fluid["P"]*1000*fluid["v"], 5), 2468.61076) - self.assertEqual(round(fluid["s"], 8), 5.17540298) - self.assertEqual(round(fluid["cp"], 7), 10.3505092) - self.assertEqual(round(fluid["w"], 6), 480.386523) + self.assertEqual(round(fluid.v, 11), 0.00542946619) + self.assertEqual(round(fluid.h, 5), 2631.49474) + self.assertEqual(round(fluid.h-fluid.P*1000*fluid.v, 5), 2468.61076) + self.assertEqual(round(fluid.s, 8), 5.17540298) + self.assertEqual(round(fluid.cp, 7), 10.3505092) + self.assertEqual(round(fluid.w, 6), 480.386523) # Backward2_T_Ph Table 24 pag 25 self.assertEqual(round(_Backward2_T_Ph(0.001, 3000), 6), 534.433241) @@ -594,36 +613,36 @@ def test_IAPWS97_2(self): self.assertEqual(round(_Backward2_P_hs(2800, 5.8), 9), 8.414574124) self.assertEqual(round(_Backward2_P_hs(3400, 5.8), 8), 83.76903879) - def test_IAPWS97_3(self): + def test_IAPWS97_3(self) -> None: """Table 33, pag 49""" fluid = _Region3(500, 650) - self.assertEqual(round(fluid["P"], 7), 25.5837018) - self.assertEqual(round(fluid["h"], 5), 1863.43019) - self.assertEqual(round(fluid["h"]-fluid["P"]*1000*fluid["v"], 5), 1812.26279) - self.assertEqual(round(fluid["s"], 8), 4.05427273) - self.assertEqual(round(fluid["cp"], 7), 13.8935717) - self.assertEqual(round(fluid["w"], 6), 502.005554) + self.assertEqual(round(fluid.P, 7), 25.5837018) + self.assertEqual(round(fluid.h, 5), 1863.43019) + self.assertEqual(round(fluid.h-fluid.P*1000*fluid.v, 5), 1812.26279) + self.assertEqual(round(fluid.s, 8), 4.05427273) + self.assertEqual(round(fluid.cp, 7), 13.8935717) + self.assertEqual(round(fluid.w, 6), 502.005554) fluid = _Region3(200, 650) - self.assertEqual(round(fluid["P"], 7), 22.2930643) - self.assertEqual(round(fluid["h"], 5), 2375.12401) - self.assertEqual(round(fluid["h"]-fluid["P"]*1000*fluid["v"], 5), 2263.65868) - self.assertEqual(round(fluid["s"], 8), 4.85438792) - self.assertEqual(round(fluid["cp"], 7), 44.6579342) - self.assertEqual(round(fluid["w"], 6), 383.444594) + self.assertEqual(round(fluid.P, 7), 22.2930643) + self.assertEqual(round(fluid.h, 5), 2375.12401) + self.assertEqual(round(fluid.h-fluid.P*1000*fluid.v, 5), 2263.65868) + self.assertEqual(round(fluid.s, 8), 4.85438792) + self.assertEqual(round(fluid.cp, 7), 44.6579342) + self.assertEqual(round(fluid.w, 6), 383.444594) fluid = _Region3(500, 750) - self.assertEqual(round(fluid["P"], 7), 78.3095639) - self.assertEqual(round(fluid["h"], 5), 2258.68845) - self.assertEqual(round(fluid["h"]-fluid["P"]*1000*fluid["v"], 5), 2102.06932) - self.assertEqual(round(fluid["s"], 8), 4.46971906) - self.assertEqual(round(fluid["cp"], 8), 6.34165359) - self.assertEqual(round(fluid["w"], 6), 760.696041) + self.assertEqual(round(fluid.P, 7), 78.3095639) + self.assertEqual(round(fluid.h, 5), 2258.68845) + self.assertEqual(round(fluid.h-fluid.P*1000*fluid.v, 5), 2102.06932) + self.assertEqual(round(fluid.s, 8), 4.46971906) + self.assertEqual(round(fluid.cp, 8), 6.34165359) + self.assertEqual(round(fluid.w, 6), 760.696041) # _h_3ab pag 7 self.assertEqual(round(_h_3ab(25), 6), 2095.936454) - def test_IAPWS97_3_Sup03(self): + def test_IAPWS97_3_Sup03(self) -> None: """Test for supplementary 03 for region 3""" # _Backward3_T_Ph Table 5 pag 8 self.assertEqual(round(_Backward3_T_Ph(20, 1700), 7), 629.3083892) @@ -669,7 +688,7 @@ def test_IAPWS97_3_Sup03(self): self.assertEqual(round(_PSat_s(4.2), 8), 21.64451789) self.assertEqual(round(_PSat_s(5.2), 8), 16.68968482) - def test_IAPWS97_3_Sup04(self): + def test_IAPWS97_3_Sup04(self) -> None: """Test for supplementary 04 for region 3""" # _Backward3_P_hs Table 5 pag 10 self.assertEqual(round(_Backward3_P_hs(1700, 3.8), 8), 25.55703246) @@ -716,7 +735,7 @@ def test_IAPWS97_3_Sup04(self): self.assertEqual(round(_Backward4_T_hs(2400, 6.0), 7), 425.1373305) self.assertEqual(round(_Backward4_T_hs(2500, 5.5), 7), 522.5579013) - def test_IAPWS97_3_Sup05(self): + def test_IAPWS97_3_Sup05(self) -> None: """Test for supplementary 05 for region 3 v=f(T,P)""" # T=f(P) limit Table 3 pag 11 self.assertEqual(round(_tab_P(40), 7), 693.0341408) @@ -790,7 +809,7 @@ def test_IAPWS97_3_Sup05(self): self.assertEqual(round(_Backward3_v_PT(22, 646.89), 12), 3.798732962e-3) self.assertEqual(round(_Backward3_v_PT(22.064, 647.15), 11), 3.701940010e-3) - def test_IAPWS97_4(self): + def test_IAPWS97_4(self) -> None: """Saturation line""" # _PSat_T Table 35 pag 34 self.assertRaises(NotImplementedError, _PSat_T, 270) @@ -804,37 +823,37 @@ def test_IAPWS97_4(self): self.assertEqual(round(_TSat_P(1), 6), 453.035632) self.assertEqual(round(_TSat_P(10), 6), 584.149488) - def test_IAPWS97_5(self): + def test_IAPWS97_5(self) -> None: """Table 42, pag 40""" fluid = _Region5(1500, 0.5) - self.assertEqual(round(fluid["v"], 8), 1.38455090) - self.assertEqual(round(fluid["h"], 5), 5219.76855) - self.assertEqual(round(fluid["h"]-fluid["P"]*1000*fluid["v"], 5), 4527.49310) - self.assertEqual(round(fluid["s"], 8), 9.65408875) - self.assertEqual(round(fluid["cp"], 8), 2.61609445) - self.assertEqual(round(fluid["w"], 6), 917.068690) + self.assertEqual(round(fluid.v, 8), 1.38455090) + self.assertEqual(round(fluid.h, 5), 5219.76855) + self.assertEqual(round(fluid.h-fluid.P*1000*fluid.v, 5), 4527.49310) + self.assertEqual(round(fluid.s, 8), 9.65408875) + self.assertEqual(round(fluid.cp, 8), 2.61609445) + self.assertEqual(round(fluid.w, 6), 917.068690) fluid = _Region5(1500, 30) - self.assertEqual(round(fluid["v"], 10), 0.0230761299) - self.assertEqual(round(fluid["h"], 5), 5167.23514) - self.assertEqual(round(fluid["h"]-fluid["P"]*1000*fluid["v"], 5), 4474.95124) - self.assertEqual(round(fluid["s"], 8), 7.72970133) - self.assertEqual(round(fluid["cp"], 8), 2.72724317) - self.assertEqual(round(fluid["w"], 6), 928.548002) + self.assertEqual(round(fluid.v, 10), 0.0230761299) + self.assertEqual(round(fluid.h, 5), 5167.23514) + self.assertEqual(round(fluid.h-fluid.P*1000*fluid.v, 5), 4474.95124) + self.assertEqual(round(fluid.s, 8), 7.72970133) + self.assertEqual(round(fluid.cp, 8), 2.72724317) + self.assertEqual(round(fluid.w, 6), 928.548002) fluid = _Region5(2000, 30) - self.assertEqual(round(fluid["v"], 10), 0.0311385219) - self.assertEqual(round(fluid["h"], 5), 6571.22604) - self.assertEqual(round(fluid["h"]-fluid["P"]*1000*fluid["v"], 5), 5637.07038) - self.assertEqual(round(fluid["s"], 8), 8.53640523) - self.assertEqual(round(fluid["cp"], 8), 2.88569882) - self.assertEqual(round(fluid["w"], 5), 1067.36948) - - def test_IAPWS97_custom(self): + self.assertEqual(round(fluid.v, 10), 0.0311385219) + self.assertEqual(round(fluid.h, 5), 6571.22604) + self.assertEqual(round(fluid.h-fluid.P*1000*fluid.v, 5), 5637.07038) + self.assertEqual(round(fluid.s, 8), 8.53640523) + self.assertEqual(round(fluid.cp, 8), 2.88569882) + self.assertEqual(round(fluid.w, 5), 1067.36948) + + def test_IAPWS97_custom(self) -> None: """Cycle input parameter from selected point for IAPWS97""" # Region 1 - P = 50 # MPa - T = 470 # K + P = 50.0 # MPa + T = 470 # K f_pt = IAPWS97(P=P, T=T) f_ph = IAPWS97(h=f_pt.h, P=f_pt.P) f_ps = IAPWS97(P=f_ph.P, s=f_ph.s) @@ -842,8 +861,8 @@ def test_IAPWS97_custom(self): self.assertEqual(round(f_hs.P-P, 6), 0) self.assertEqual(round(f_hs.T-T, 6), 0) - P = 20 # MPa - T = 370 # K + P = 20.0 # MPa + T = 370 # K f_pt = IAPWS97(P=P, T=T) f_ph = IAPWS97(h=f_pt.h, P=f_pt.P) f_ps = IAPWS97(P=f_ph.P, s=f_ph.s) @@ -852,8 +871,8 @@ def test_IAPWS97_custom(self): self.assertEqual(round(f_hs.T-T, 6), 0) # Region 2 - P = 25 # MPa - T = 700 # K + P = 25.0 # MPa + T = 700 # K f_pt = IAPWS97(P=P, T=T) f_ph = IAPWS97(h=f_pt.h, P=f_pt.P) f_ps = IAPWS97(P=f_ph.P, s=f_ph.s) @@ -861,8 +880,8 @@ def test_IAPWS97_custom(self): self.assertEqual(round(f_hs.P-P, 6), 0) self.assertEqual(round(f_hs.T-T, 6), 0) - P = 10 # MPa - T = 700 # K + P = 10.0 # MPa + T = 700 # K f_pt = IAPWS97(P=P, T=T) f_ph = IAPWS97(h=f_pt.h, P=f_pt.P) f_ps = IAPWS97(P=f_ph.P, s=f_ph.s) @@ -879,7 +898,7 @@ def test_IAPWS97_custom(self): self.assertEqual(round(f_hs.P-P, 6), 0) self.assertEqual(round(f_hs.T-T, 6), 0) - P = 0.01 # MPa + P = 0.01 # MPa T = 1000 # K f_pt = IAPWS97(P=P, T=T) f_ph = IAPWS97(h=f_pt.h, P=f_pt.P) @@ -888,7 +907,7 @@ def test_IAPWS97_custom(self): self.assertEqual(round(f_hs.P-P, 6), 0) self.assertEqual(round(f_hs.T-T, 6), 0) - P = 2 # MPa + P = 2.0 # MPa T = 1000 # K f_pt = IAPWS97(P=P, T=T) f_ph = IAPWS97(h=f_pt.h, P=f_pt.P) @@ -898,8 +917,8 @@ def test_IAPWS97_custom(self): self.assertEqual(round(f_hs.T-T, 6), 0) # Region 3 - P = 50 # MPa - T = 700 # K + P = 50.0 # MPa + T = 700 # K f_pt = IAPWS97(P=P, T=T) f_ph = IAPWS97(h=f_pt.h, P=f_pt.P) f_ps = IAPWS97(P=f_ph.P, s=f_ph.s) @@ -907,8 +926,8 @@ def test_IAPWS97_custom(self): self.assertEqual(round(f_hs.P-P, 6), 0) self.assertEqual(round(f_hs.T-T, 6), 0) - P = 20 # MPa - s = 4 # kJ/kgK + P = 20.0 # MPa + s = 4 # kJ/kgK f_ps = IAPWS97(P=P, s=s) f_ph = IAPWS97(h=f_pt.h, P=f_pt.P) f_pt = IAPWS97(P=f_ph.P, T=f_ph.T) @@ -916,35 +935,35 @@ def test_IAPWS97_custom(self): self.assertEqual(round(f_hs.P-P, 6), 0) self.assertEqual(round(f_hs.s-s, 6), 0) - P = 19 # MPa + P = 19.0 # MPa f_px = IAPWS97(P=P, x=0) f_tx = IAPWS97(T=f_px.T, x=f_px.x) self.assertEqual(round(f_tx.P-P, 3), 0) self.assertEqual(round(f_tx.x, 6), 0) - P = 19 # MPa + P = 19.0 # MPa f_px = IAPWS97(P=P, x=1) f_tx = IAPWS97(T=f_px.T, x=f_px.x) self.assertEqual(round(f_tx.P-P, 3), 0) self.assertEqual(round(f_tx.x, 6), 1) - P = 21 # MPa + P = 21.0 # MPa f_px = IAPWS97(P=P, x=1) f_tx = IAPWS97(T=f_px.T, x=f_px.x) self.assertEqual(round(f_tx.P-P, 2), 0) self.assertEqual(round(f_tx.x, 3), 1) - P = 21.5 # MPa + P = 21.5 # MPa f_px = IAPWS97(P=P, x=0) f_tx = IAPWS97(T=f_px.T, x=f_px.x) self.assertEqual(round(f_tx.P-P, 2), 0) self.assertEqual(round(f_tx.x, 3), 0) - P = 21.5 # MPa + P = 21.5 # MPa f_px = IAPWS97(P=P, x=1) f_tx = IAPWS97(T=f_px.T, x=f_px.x) self.assertEqual(round(f_tx.P-P, 2), 0) self.assertEqual(round(f_tx.x, 3), 1) - P = 22.02 # MPa + P = 22.02 # MPa f_px = IAPWS97(P=P, x=0) f_tx = IAPWS97(T=f_px.T, x=f_px.x) self.assertEqual(round(f_tx.P-P, 2), 0) @@ -955,7 +974,7 @@ def test_IAPWS97_custom(self): self.assertEqual(round(f_tx.P-P, 2), 0) self.assertEqual(round(f_tx.x, 3), 1) - P = 24. # MPa + P = 24.0 # MPa T = 630 # K f_pt = IAPWS97(P=P, T=T) f_hs = IAPWS97(h=f_pt.h, s=f_pt.s) @@ -1022,7 +1041,7 @@ def test_IAPWS97_custom(self): f_hs = IAPWS97(h=f_px.h, s=f_px.s) self.assertEqual(round(f_hs.T-T, 0), 0) - P = 17 # MPa + P = 17.0 # MPa h = 2000 # kJkg f_ph = IAPWS97(P=P, h=h) f_ps = IAPWS97(P=f_ph.P, s=f_ph.s) @@ -1042,7 +1061,7 @@ def test_IAPWS97_custom(self): self.assertEqual(round(f_hs.T-T, 6), 0) # Region 5 - P = 25 # MPa + P = 25.0 # MPa T = 1100 # K f_pt = IAPWS97(P=P, T=T) f_ph = IAPWS97(h=f_pt.h, P=f_pt.P) @@ -1051,7 +1070,7 @@ def test_IAPWS97_custom(self): self.assertEqual(round(f_hs.P-P, 6), 0) self.assertEqual(round(f_hs.T-T, 6), 0) - P = 10 # MPa + P = 10.0 # MPa T = 1100 # K f_pt = IAPWS97(P=P, T=T) f_ph = IAPWS97(h=f_pt.h, P=f_pt.P) @@ -1060,7 +1079,7 @@ def test_IAPWS97_custom(self): self.assertEqual(round(f_hs.P-P, 6), 0) self.assertEqual(round(f_hs.T-T, 6), 0) - P = 20 # MPa + P = 20.0 # MPa T = 1100 # K f_pt = IAPWS97(P=P, T=T) f_ph = IAPWS97(h=f_pt.h, P=f_pt.P) @@ -1070,12 +1089,14 @@ def test_IAPWS97_custom(self): self.assertEqual(round(f_hs.T-T, 6), 0) # P-T subregion 3 - P = [17, 21, 21, 21, 21, 22, 23.2, 23.2, 23.2, 23.2, 23.2, 23.2, 23.2, - 23, 23, 22.2, 22.065, 22.065, 22.065, 22.065, 21.8, 22] - T = [625, 625, 640, 643, 645, 630, 640, 650, 651, 652, 653, 656, 660, - 640, 652, 647, 646, 647.05, 647.1, 647.2, 647, 647] - for p, t in zip(P, T): - f_pt = IAPWS97(P=p, T=t) + Plist = [17.0, 21.0, 21.0, 21.0, 21.0, 22.0, 23.2, 23.2, 23.2, 23.2, + 23.2, 23.2, 23.2, 23.0, 23.0, 22.2, 22.065, 22.065, 22.065, + 22.065, 21.8, 22.0] + Tlist = [625.0, 625.0, 640.0, 643.0, 645.0, 630.0, 640.0, 650.0, 651.0, + 652.0, 653.0, 656.0, 660.0, 640.0, 652.0, 647.0, 646.0, 647.05, + 647.1, 647.2, 647.0, 647.0] + for p, t in zip(Plist, Tlist): + f_pt = IAPWS97(P=float(p), T=t) f_ph = IAPWS97(h=f_pt.h, P=f_pt.P) f_ps = IAPWS97(P=f_ph.P, s=f_ph.s) f_hs = IAPWS97(h=f_ps.h, s=f_ps.s) @@ -1083,9 +1104,9 @@ def test_IAPWS97_custom(self): self.assertEqual(round(f_hs.T-t, 6), 0) # Other h-s region - h = [2700, 2700, 1500, 2500, 2000, 2000, 3000, 2400, 2500, 2850, 2600] - s = [5.15, 5.87, 3.5, 5, 5.5, 7, 6, 5.1, 5.05, 5.25, 5.25] - for H, S in zip(h, s): + hlist = [2700, 2700, 1500, 2500, 2000, 2000, 3000, 2400, 2500, 2850, 2600] + slist = [5.15, 5.87, 3.5, 5, 5.5, 7, 6, 5.1, 5.05, 5.25, 5.25] + for H, S in zip(hlist, slist): f_hs = IAPWS97(h=H, s=S) f_pt = IAPWS97(P=f_hs.P, T=f_hs.T) self.assertEqual(round(f_hs.h-H, 6), 0) @@ -1116,10 +1137,10 @@ def test_IAPWS97_custom(self): self.assertRaises(NotImplementedError, IAPWS97, **{"P": 65, "s": 9}) self.assertRaises(NotImplementedError, IAPWS97, **{"h": 700, "s": -1}) - def test_IAPWS95_custom(self): + def test_IAPWS95_custom1(self) -> None: """Cycle input parameter from selected point for IAPWS95""" - P = 50 # MPa - T = 470 # K + P = 50.0 # MPa + T = 470 # K f_pt = IAPWS95_PT(P, T) f_ph = IAPWS95_Ph(f_pt.P, f_pt.h) f_ps = IAPWS95_Ps(f_ph.P, f_ph.s) @@ -1127,7 +1148,9 @@ def test_IAPWS95_custom(self): self.assertEqual(round(f_hs.P-P, 5), 0) self.assertEqual(round(f_hs.T-T, 5), 0) - P = 2 # MPa + def test_IAPWS95_custom2(self) -> None: + """Cycle input parameter from selected point for IAPWS95""" + P = 2.0 # MPa f_px = IAPWS95_Px(P, 0.5) f_tx = IAPWS95_Tx(f_px.T, f_px.x) f_tv = IAPWS95(T=f_px.T, v=f_px.v) @@ -1147,8 +1170,8 @@ def test_IAPWS95_custom(self): self.assertEqual(round(f_Prho.P-P, 5), 0) self.assertEqual(round(f_Prho.x-0.5, 5), 0) - P = 50 # MPa - T = 770 # K + P = 50.0 # MPa + T = 770 # K f_pt = IAPWS95_PT(P, T) f_tv = IAPWS95(T=f_pt.T, v=f_pt.v) f_th = IAPWS95(T=f_tv.T, h=f_tv.h) @@ -1167,7 +1190,7 @@ def test_IAPWS95_custom(self): self.assertEqual(round(f_Prho.P-P, 5), 0) self.assertEqual(round(f_Prho.T-T, 5), 0) - P = 0.1 # MPa + P = 0.1 # MPa T = 300 # K f_pt = IAPWS95_PT(P, T) f_tv = IAPWS95(T=f_pt.T, v=f_pt.v) @@ -1187,7 +1210,7 @@ def test_IAPWS95_custom(self): self.assertEqual(round(f_Prho.P-P, 5), 0) self.assertEqual(round(f_Prho.T-T, 5), 0) - P = 0.1 # MPa + P = 0.1 # MPa T = 500 # K f_pt = IAPWS95_PT(P, T) f_tv = IAPWS95(T=f_pt.T, v=f_pt.v) @@ -1205,7 +1228,7 @@ def test_IAPWS95_custom(self): self.assertEqual(round(f_Prho.P-P, 5), 0) self.assertEqual(round(f_Prho.T-T, 5), 0) - P = 2 # MPa + P = 2.0 # MPa f_px = IAPWS95_Px(P, 0) f_tx = IAPWS95_Tx(f_px.T, f_px.x) self.assertEqual(round(f_tx.P-P, 5), 0) @@ -1213,35 +1236,36 @@ def test_IAPWS95_custom(self): f_tx = IAPWS95_Tx(f_px.T, f_px.x) self.assertEqual(round(f_tx.P-P, 5), 0) - P = 0.1 # MPa + P = 0.1 # MPa T = 300 # K - f_pt = D2O(P=P, T=T) - self.assertEqual(round(f_pt.P-P, 5), 0) - self.assertEqual(round(f_pt.T-T, 5), 0) + d2o_pt = D2O(P=P, T=T) + self.assertEqual(round(d2o_pt.P-P, 5), 0) + self.assertEqual(round(d2o_pt.T-T, 5), 0) self.assertRaises(NotImplementedError, IAPWS95, **{"T": 700, "x": 0}) self.assertRaises(NotImplementedError, IAPWS95, **{"P": 25, "x": 1}) - def test_D2O(self): + def test_D2O(self) -> None: + """Tables 6-8, page 12-13.""" # Table 6, pag 12""" fluid = D2O() delta = 46.26*fluid.M/fluid.rhoc tau = fluid.Tc/500 ideal = fluid._phi0(tau, delta) - self.assertEqual(round(ideal["fio"], 8), 1.96352717) - self.assertEqual(round(ideal["fiod"], 9), 0.384253134) - self.assertEqual(round(ideal["fiodd"], 9), -0.147650471) - self.assertEqual(round(ideal["fiot"], 8), 9.39259413) - self.assertEqual(round(ideal["fiott"], 8), -2.09517144) - self.assertEqual(round(ideal["fiodt"], 8), 0) - res = fluid._phir(tau, delta) - self.assertEqual(round(res["fir"], 8), -3.42291092) - self.assertEqual(round(res["fird"], 9), -0.367562780) - self.assertEqual(round(res["firdd"], 9), 0.835183806) - self.assertEqual(round(res["firt"], 8), -5.89707436) - self.assertEqual(round(res["firtt"], 8), -2.45187285) - self.assertEqual(round(res["firdt"], 8), -1.13178440) + self.assertEqual(round(ideal.fi, 8), 1.96352717) + self.assertEqual(round(ideal.fid, 9), 0.384253134) + self.assertEqual(round(ideal.fidd, 9), -0.147650471) + self.assertEqual(round(ideal.fit, 8), 9.39259413) + self.assertEqual(round(ideal.fitt, 8), -2.09517144) + self.assertEqual(round(ideal.fidt, 8), 0) + res = fluid.residual.helmholtz(tau, delta) + self.assertEqual(round(res.fi, 8), -3.42291092) + self.assertEqual(round(res.fid, 9), -0.367562780) + self.assertEqual(round(res.fidd, 9), 0.835183806) + self.assertEqual(round(res.fit, 8), -5.89707436) + self.assertEqual(round(res.fitt, 8), -2.45187285) + self.assertEqual(round(res.fidt, 8), -1.13178440) # Table 7, Pag 12, Single phase region st = D2O(T=300, rhom=55.126) @@ -1357,7 +1381,7 @@ def test_D2O(self): P = _D2O_Melting_Pressure(300, "VI") self.assertEqual(round(P, 6), 959.203594) - def test_D2O_Viscosity(self): + def test_D2O_Viscosity(self) -> None: """Table A5 pag 10""" mur = 55.2651e-6 Tr = 643.847 @@ -1390,7 +1414,7 @@ def test_D2O_Viscosity(self): self.assertEqual(round(_D2O_Viscosity(1.2*rhor, 1.2*Tr)/mur, 10), 0.9937870139) self.assertEqual(round(_D2O_Viscosity(1.61*rhor, 1.2*Tr)/mur, 10), 1.2711900131) - def test_D2O_ThCond(self): + def test_D2O_ThCond(self) -> None: """Table B4 pag 17""" lr = 0.742128e-3 Tr = 643.847 @@ -1427,7 +1451,7 @@ def test_D2O_ThCond(self): self.assertEqual(round(_D2O_ThCond(0.95*rhor, 1.27*Tr)/lr, 9), 299.251471210) self.assertEqual(round(_D2O_ThCond(1.37*rhor, 1.27*Tr)/lr, 9), 409.359675394) - def test_D2O_Tension(self): + def test_D2O_Tension(self) -> None: """Selected values from table 1""" self.assertRaises(NotImplementedError, _D2O_Tension, 250) self.assertEqual(round(_D2O_Tension(273.15+3.8)*1000, 2), 74.93) @@ -1451,7 +1475,7 @@ def test_D2O_Tension(self): self.assertEqual(round(_D2O_Tension(623.15)*1000, 2), 3.17) self.assertEqual(round(_D2O_Tension(643.15)*1000, 2), 0.05) - def test_Ice(self): + def test_Ice(self) -> None: """Table 6, pag 12""" ice = _Ice(273.16, 0.000611657) self.assertEqual(round(ice["g"], 12), 0.000611784135) @@ -1514,7 +1538,7 @@ def test_Ice(self): self.assertWarns(Warning, _Ice, *(273, 3)) self.assertWarns(Warning, _Ice, *(272, 1e-4)) - def test_SeaWater(self): + def test_SeaWater(self) -> None: """Table 8, pag 17-19""" # Part a, pag 17 fluid = SeaWater(T=273.15, P=0.101325, S=0.03516504) @@ -1553,6 +1577,7 @@ def test_SeaWater(self): self.assertEqual(round(fluid.rho, 5), 1028.10720) self.assertEqual(round(fluid.cp, 8), 3.98648579) self.assertEqual(round(fluid.w, 5), 1449.00246) + assert(fluid.muw is not None) self.assertEqual(round(fluid.muw, 8), -2.25047137) # Part b, pag 18 @@ -1592,6 +1617,7 @@ def test_SeaWater(self): self.assertEqual(round(fluid.rho, 5), 1029.85888) self.assertEqual(round(fluid.cp, 8), 3.74507355) self.assertEqual(round(fluid.w, 5), 3961.27835) + assert(fluid.muw is not None) self.assertEqual(round(fluid.muw, 7), -54.7200505) # Part c, pag 19 @@ -1631,6 +1657,7 @@ def test_SeaWater(self): self.assertEqual(round(fluid.rho, 5), 1070.92645) self.assertEqual(round(fluid.cp, 8), 3.77190387) self.assertEqual(round(fluid.w, 5), 1621.98998) + assert(fluid.muw is not None) self.assertEqual(round(fluid.muw, 7), 95.3214082) # Custom derivative implementation @@ -1638,7 +1665,7 @@ def test_SeaWater(self): wat = IAPWS95(T=353, P=0.101325) self.assertEqual(round(fluid.derivative("T", "P", "h")*1000-wat.joule, 7), 0) - def test_SeaWater_supp(self): + def test_SeaWater_supp(self) -> None: """Table 6, pag 9""" fluid = SeaWater(T=273.15, P=0.101325, S=0, fast=True) state = fluid._waterSupp(273.15, 0.101325) @@ -1688,7 +1715,7 @@ def test_SeaWater_supp(self): self.assertEqual(round(fluid.cp, 8), 4.17942416) self.assertEqual(round(fluid.w, 5), 1528.91242) - def test_SeaWaterIF97(self): + def test_SeaWaterIF97(self) -> None: """Table A1, pag 19-21""" fluid = SeaWater(T=273.15, P=0.101325, S=0.03516504, IF97=True) state = fluid._waterIF97(273.15, 0.101325) @@ -1725,6 +1752,7 @@ def test_SeaWaterIF97(self): self.assertEqual(round(fluid.s, 12), -0.68447e-7) self.assertEqual(round(fluid.cp, 8), 0.398647132e1) self.assertEqual(round(fluid.w, 5), 0.144907123e4) + assert(fluid.muw is not None) self.assertEqual(round(fluid.muw, 8), -0.225045466e1) fluid = SeaWater(T=353, P=0.101325, S=0.1, IF97=True) @@ -1762,6 +1790,7 @@ def test_SeaWaterIF97(self): self.assertEqual(round(fluid.s, 9), 0.917342513) self.assertEqual(round(fluid.cp, 8), 0.374382192e1) self.assertEqual(round(fluid.w, 5), 0.401505044e4) + assert(fluid.muw is not None) self.assertEqual(round(fluid.muw, 7), -0.547176899e2) # Part c, pag 19 @@ -1800,6 +1829,7 @@ def test_SeaWaterIF97(self): self.assertEqual(round(fluid.s, 10), -0.161227439e-1) self.assertEqual(round(fluid.cp, 8), 0.377237430e1) self.assertEqual(round(fluid.w, 5), 0.162218081e4) + assert(fluid.muw is not None) self.assertEqual(round(fluid.muw, 7), 0.953212423e2) # Table A2 @@ -1919,48 +1949,57 @@ def test_SeaWaterIF97(self): # Osmotic pressure, i have no test to do self.assertEqual(round(_OsmoticPressure(300, 0.1, 0), 5), 0.0) - def test_SeaWater_thcond(self): + def test_SeaWater_thcond(self) -> None: """Table 2, pag 5""" fluid = SeaWater(T=293.15, P=0.1, S=0.035) self.assertEqual(round(_ThCond_SeaWater(T=293.15, P=0.1, S=0.035), 9), -0.004186040) + assert(fluid.k is not None) self.assertEqual(round(fluid.k, 9), 0.593825535) fluid = SeaWater(T=293.15, P=120, S=0.035) self.assertEqual(round(_ThCond_SeaWater(T=293.15, P=120, S=0.035), 9), -0.004317350) + assert(fluid.k is not None) self.assertEqual(round(fluid.k, 9), 0.651692949) fluid = SeaWater(T=333.15, P=0.1, S=0.035) self.assertEqual(round(_ThCond_SeaWater(T=333.15, P=0.1, S=0.035), 9), -0.004124057) + assert(fluid.k is not None) self.assertEqual(round(fluid.k, 9), 0.646875533) fluid = SeaWater(T=333.15, P=120, S=0.035) self.assertEqual(round(_ThCond_SeaWater(T=333.15, P=120, S=0.035), 9), -0.004264405) + assert(fluid.k is not None) self.assertEqual(round(fluid.k, 9), 0.702484548) fluid = SeaWater(T=293.15, P=0.1, S=0.1) self.assertEqual(round(_ThCond_SeaWater(T=293.15, P=0.1, S=0.1), 9), -0.013819821) + assert(fluid.k is not None) self.assertEqual(round(fluid.k, 9), 0.584191754) fluid = SeaWater(T=373.15, P=1, S=0.1) self.assertEqual(round(_ThCond_SeaWater(T=373.15, P=1, S=0.1), 9), -0.013094107) + assert(fluid.k is not None) self.assertEqual(round(fluid.k, 9), 0.664627314) fluid = SeaWater(T=293.15, P=0.1, S=0.12) self.assertEqual(round(_ThCond_SeaWater(T=293.15, P=0.1, S=0.12), 9), -0.017005302) + assert(fluid.k is not None) self.assertEqual(round(fluid.k, 9), 0.581006273) fluid = SeaWater(T=293.15, P=120, S=0.12) self.assertEqual(round(_ThCond_SeaWater(T=293.15, P=120, S=0.12), 9), -0.020194816) + assert(fluid.k is not None) self.assertEqual(round(fluid.k, 9), 0.635815483) fluid = SeaWater(T=333.15, P=120, S=0.12) self.assertEqual(round(_ThCond_SeaWater(T=333.15, P=120, S=0.12), 9), -0.019722469) + assert(fluid.k is not None) self.assertEqual(round(fluid.k, 9), 0.687026483) fluid = SeaWater(T=270, P=1, S=0.12) self.assertRaises(NotImplementedError, _ThCond_SeaWater, *(270, 1, 0)) - def test_SeaWater_tension(self): + def test_SeaWater_tension(self) -> None: """Table 2, pag 4""" self.assertEqual(round(_Tension_SeaWater(253.15, 0.035)*1e3, 9), 79.225179610) self.assertEqual(round(_Tension_SeaWater(298.15, 0.035)*1e3, 9), 73.068674787) @@ -1970,7 +2009,7 @@ def test_SeaWater_tension(self): self.assertEqual(round(_Tension_SeaWater(293.15, 0.120)*1e3, 9), 76.432940211) self.assertEqual(round(_Tension_SeaWater(353.15, 0.120)*1e3, 9), 66.917261258) - def test_na2so4(self): + def test_na2so4(self) -> None: """Selected point from Table 1, pag 5""" self.assertEqual(round(_solNa2SO4(523.15, 0, 0), 2), 3.54) self.assertEqual(round(_solNa2SO4(523.15, 0.25, 0.083), 2), 3.31) @@ -1988,7 +2027,7 @@ def test_na2so4(self): self.assertEqual(round(_solNa2SO4(623.15, 0.75, 2.25), 2), 2.13) self.assertRaises(NotImplementedError, _solNa2SO4, *(500, 0, 0)) - def test_critNaCl(self): + def test_critNaCl(self) -> None: """Table II, page 6""" crit = _critNaCl(0) self.assertEqual(round(crit["Tc"], 6), 647.096000) @@ -2089,7 +2128,8 @@ def test_critNaCl(self): self.assertRaises(NotImplementedError, _critNaCl, 0.2) - def test_Henry(self): + def test_Henry(self) -> None: + """Table 6, Henry constants.""" # Table 6 for Henry constants self.assertRaises(NotImplementedError, _Henry, *(300, "He", "He")) self.assertRaises(NotImplementedError, _Henry, *(300, "SF6", "D2O")) @@ -2266,7 +2306,7 @@ def test_Henry(self): self.assertEqual(round(log(_Kvalue(500, "CH4", "D2O")), 4), 6.9021) self.assertEqual(round(log(_Kvalue(600, "CH4", "D2O")), 4), 3.8126) - def xest_Conductivity(self): + def xest_Conductivity(self) -> None: """Selected values from table II""" self.assertEqual(round(_Conductivity(600, 673.15), 9), 1.57e-6) self.assertEqual(round(_Conductivity(800, 1073.15), 9), 103e-6) @@ -2274,7 +2314,8 @@ def xest_Conductivity(self): self.assertEqual(round(_Conductivity(1100, 273.16), 9), 0.0333e-6) self.assertEqual(round(_Conductivity(1100, 473.15), 9), 22.8e-6) - def test_virial(self): + def test_virial(self) -> None: + """Tables 7 & 8, page 10""" # Table 7, page 10 st = _virial(200) self.assertEqual(round(st["Baa"], 13), -0.392722567e-4) @@ -2315,7 +2356,8 @@ def test_virial(self): self.assertWarns(Warning, _virial, 50) self.assertRaises(NotImplementedError, _fugacity, *(190, 1, 0.1)) - def test_Air(self): + def test_Air(self) -> None: + """Tables A1 & A2, page 363 & 366.""" # Table A1, Pag 363 self.assertEqual(round(Air._bubbleP(59.75), 6), 0.005265) self.assertEqual(round(Air._bubbleP(59.75), 6), 0.005265) @@ -2375,16 +2417,16 @@ def test_Air(self): self.assertEqual(round(f_prho.P-P, 6), 0) self.assertEqual(round(f_prho.T-T, 6), 0) - def test_AirTransport(self): + def test_AirTransport(self) -> None: """Table V, pag 28""" - self.assertEqual(round(Air._visco(0, 100), 11), 7.09559e-6) - self.assertEqual(round(Air._visco(0, 300), 10), 18.523e-6) - self.assertEqual(round(Air._visco(28*28.9586, 100), 9), 107.923e-6) - self.assertEqual(round(Air._visco(10*28.9586, 200), 10), 21.1392e-6) - self.assertEqual(round(Air._visco(5*28.9586, 300), 10), 21.3241e-6) - self.assertEqual(round(Air._visco(10.4*28.9586, 132.64), 10), 17.7623e-6) - st = Air() + self.assertEqual(round(st._visco(0, 100), 11), 7.09559e-6) + self.assertEqual(round(st._visco(0, 300), 10), 18.523e-6) + self.assertEqual(round(st._visco(28*28.9586, 100), 9), 107.923e-6) + self.assertEqual(round(st._visco(10*28.9586, 200), 10), 21.1392e-6) + self.assertEqual(round(st._visco(5*28.9586, 300), 10), 21.3241e-6) + self.assertEqual(round(st._visco(10.4*28.9586, 132.64), 10), 17.7623e-6) + self.assertEqual(round(st._thermo(0, 100), 8), 9.35902e-3) self.assertEqual(round(st._thermo(0, 300), 7), 26.3529e-3) self.assertEqual(round(Air(rho=28*28.9586, T=100).k, 6), 119.222e-3) @@ -2392,27 +2434,27 @@ def test_AirTransport(self): self.assertEqual(round(Air(rho=5*28.9586, T=300).k, 7), 32.6062e-3) # self.assertEqual(round(Air(rho=10.4*28.9586, T=132.64).k, 7), 75.6231e-3) - def test_HumidAir(self): + def test_HumidAir(self) -> None: """Tables 13-15 from page 19""" # Table 13 A = 0.892247719 T = 200 rho = 1.63479657e-5 psy = HumidAir() - fa = psy._fav(T, rho, A) - self.assertEqual(round(fa["fir"], 6), -0.682093392e3) - self.assertEqual(round(fa["fira"], 6), -0.572680404e3) - self.assertEqual(round(fa["firt"], 8), -0.405317966e1) - self.assertEqual(round(fa["fird"], 2), 0.374173101e7) - self.assertEqual(round(fa["firaa"], 6), 0.920967684e3) - self.assertEqual(round(fa["firat"], 8), 0.915653743e1) - self.assertEqual(round(fa["firad"], 2), -0.213442099e7) - self.assertEqual(round(fa["firtt"], 11), -0.394011921e-2) - self.assertEqual(round(fa["firdt"], 4), 0.187087034e5) - self.assertEqual(round(fa["firdd"]*1e-6, 3), -0.228880603e6) - colig = psy._coligative(rho, A, fa) + fav = psy._fav(T, rho, A) + self.assertEqual(round(fav["fir"], 6), -0.682093392e3) + self.assertEqual(round(fav["fira"], 6), -0.572680404e3) + self.assertEqual(round(fav["firt"], 8), -0.405317966e1) + self.assertEqual(round(fav["fird"], 2), 0.374173101e7) + self.assertEqual(round(fav["firaa"], 6), 0.920967684e3) + self.assertEqual(round(fav["firat"], 8), 0.915653743e1) + self.assertEqual(round(fav["firad"], 2), -0.213442099e7) + self.assertEqual(round(fav["firtt"], 11), -0.394011921e-2) + self.assertEqual(round(fav["firdt"], 4), 0.187087034e5) + self.assertEqual(round(fav["firdd"]*1e-6, 3), -0.228880603e6) + colig = psy._coligative(rho, A, fav) self.assertEqual(round(colig["muw"], 6), -0.109950917e3) - prop = psy._prop(T, rho, fa) + prop = psy._prop(T, rho, fav) self.assertEqual(round(prop["P"], 15), 0.999999998e-6) self.assertEqual(round(prop["h"], 6), 0.189712231e3) self.assertEqual(round(prop["g"], 6), -0.620923701e3) @@ -2424,20 +2466,20 @@ def test_HumidAir(self): T = 300 rho = 1.14614216 psy = HumidAir() - fa = psy._fav(T, rho, A) - self.assertEqual(round(fa["fir"], 7), -0.927718178e2) - self.assertEqual(round(fa["fira"], 9), -0.263453864) - self.assertEqual(round(fa["firt"], 9), -0.296711481) - self.assertEqual(round(fa["fird"], 7), 0.761242496e2) - self.assertEqual(round(fa["firaa"], 5), 0.624886233e4) - self.assertEqual(round(fa["firat"], 8), 0.822733446e1) - self.assertEqual(round(fa["firad"], 7), -0.450004399e2) - self.assertEqual(round(fa["firtt"], 11), -0.244742952e-2) - self.assertEqual(round(fa["firdt"], 9), 0.254456302) - self.assertEqual(round(fa["firdd"], 7), -0.664465525e2) - colig = psy._coligative(rho, A, fa) + fav = psy._fav(T, rho, A) + self.assertEqual(round(fav["fir"], 7), -0.927718178e2) + self.assertEqual(round(fav["fira"], 9), -0.263453864) + self.assertEqual(round(fav["firt"], 9), -0.296711481) + self.assertEqual(round(fav["fird"], 7), 0.761242496e2) + self.assertEqual(round(fav["firaa"], 5), 0.624886233e4) + self.assertEqual(round(fav["firat"], 8), 0.822733446e1) + self.assertEqual(round(fav["firad"], 7), -0.450004399e2) + self.assertEqual(round(fav["firtt"], 11), -0.244742952e-2) + self.assertEqual(round(fav["firdt"], 9), 0.254456302) + self.assertEqual(round(fav["firdd"], 7), -0.664465525e2) + colig = psy._coligative(rho, A, fav) self.assertEqual(round(colig["muw"], 8), -0.526505193e1) - prop = psy._prop(T, rho, fa) + prop = psy._prop(T, rho, fav) self.assertEqual(round(prop["P"], 9), 0.1) self.assertEqual(round(prop["h"], 7), 0.834908383e2) self.assertEqual(round(prop["g"], 8), -0.552260595e1) @@ -2449,20 +2491,20 @@ def test_HumidAir(self): T = 400 rho = 0.793354063e1 psy = HumidAir() - fa = psy._fav(T, rho, A) - self.assertEqual(round(fa["fir"], 7), 0.240345570e2) - self.assertEqual(round(fa["fira"], 6), 0.311096733e3) - self.assertEqual(round(fa["firt"], 8), -0.106891931e1) - self.assertEqual(round(fa["fird"], 7), 0.158878781e2) - self.assertEqual(round(fa["firaa"], 5), 0.113786423e4) - self.assertEqual(round(fa["firat"], 8), 0.702631471e1) - self.assertEqual(round(fa["firad"], 8), -0.727972651e1) - self.assertEqual(round(fa["firtt"], 11), -0.222449294e-2) - self.assertEqual(round(fa["firdt"], 10), 0.414350772e-1) - self.assertEqual(round(fa["firdd"], 8), -0.201886184e1) - colig = psy._coligative(rho, A, fa) + fav = psy._fav(T, rho, A) + self.assertEqual(round(fav["fir"], 7), 0.240345570e2) + self.assertEqual(round(fav["fira"], 6), 0.311096733e3) + self.assertEqual(round(fav["firt"], 8), -0.106891931e1) + self.assertEqual(round(fav["fird"], 7), 0.158878781e2) + self.assertEqual(round(fav["firaa"], 5), 0.113786423e4) + self.assertEqual(round(fav["firat"], 8), 0.702631471e1) + self.assertEqual(round(fav["firad"], 8), -0.727972651e1) + self.assertEqual(round(fav["firtt"], 11), -0.222449294e-2) + self.assertEqual(round(fav["firdt"], 10), 0.414350772e-1) + self.assertEqual(round(fav["firdd"], 8), -0.201886184e1) + colig = psy._coligative(rho, A, fav) self.assertEqual(round(colig["muw"], 6), -0.106748981e3) - prop = psy._prop(T, rho, fa) + prop = psy._prop(T, rho, fav) self.assertEqual(round(prop["P"], 8), 1) self.assertEqual(round(prop["h"], 6), 0.577649408e3) self.assertEqual(round(prop["g"], 6), 0.150081684e3) @@ -2478,25 +2520,25 @@ def test_HumidAir(self): rhov = (1-A)*rho air = Air() fa = air._derivDimensional(0, T) - self.assertEqual(round(fa["fir"], 6), 0) + self.assertEqual(round(fa.fi, 6), 0) fa = air._derivDimensional(rhoa, T) self.assertEqual(round(rhoa, 13), 1.45864351e-5) - self.assertEqual(round(fa["fir"], 6), -0.740041144e3) - self.assertEqual(round(fa["firt"], 8), -0.304774177e1) - self.assertEqual(round(fa["fird"], 2), 0.393583654e7) - self.assertEqual(round(fa["firtt"], 11), -0.357677878e-2) - self.assertEqual(round(fa["firdt"], 4), 0.196791837e5) - self.assertEqual(round(fa["firdd"]*1e-6, 3), -0.269828549e6) + self.assertEqual(round(fa.fi, 6), -0.740041144e3) + self.assertEqual(round(fa.fit, 8), -0.304774177e1) + self.assertEqual(round(fa.fid, 2), 0.393583654e7) + self.assertEqual(round(fa.fitt, 11), -0.357677878e-2) + self.assertEqual(round(fa.fidt, 4), 0.196791837e5) + self.assertEqual(round(fa.fidd*1e-6, 3), -0.269828549e6) water = IAPWS95() fv = water._derivDimensional(rhov, T) self.assertEqual(round(rhov, 14), 1.76153059e-6) - self.assertEqual(round(fv["fir"], 6), -0.202254351e3) - self.assertEqual(round(fv["firt"], 7), -0.123787544e2) - self.assertEqual(round(fv["fird"], 1), 0.523995674e8) - self.assertEqual(round(fv["firtt"], 11), -0.694877601e-2) - self.assertEqual(round(fv["firdt"], 3), 0.262001885e6) - self.assertEqual(round(fv["firdd"]*1e-6, 1), -0.297466671e8) + self.assertEqual(round(fv.fi, 6), -0.202254351e3) + self.assertEqual(round(fv.fit, 7), -0.123787544e2) + self.assertEqual(round(fv.fid, 1), 0.523995674e8) + self.assertEqual(round(fv.fitt, 11), -0.694877601e-2) + self.assertEqual(round(fv.fidt, 3), 0.262001885e6) + self.assertEqual(round(fv.fidd*1e-6, 1), -0.297466671e8) A = 0.977605798 T = 300 @@ -2506,21 +2548,21 @@ def test_HumidAir(self): air = Air() fa = air._derivDimensional(rhoa, T) self.assertEqual(round(rhoa, 8), 1.12047522) - self.assertEqual(round(fa["fir"], 7), -0.916103453e2) - self.assertEqual(round(fa["firt"], 9), -0.108476220) - self.assertEqual(round(fa["fird"], 7), 0.768326795e2) - self.assertEqual(round(fa["firtt"], 11), -0.239319940e-2) - self.assertEqual(round(fa["firdt"], 9), 0.256683306) - self.assertEqual(round(fa["firdd"], 7), -0.685917373e2) + self.assertEqual(round(fa.fi, 7), -0.916103453e2) + self.assertEqual(round(fa.fit, 9), -0.108476220) + self.assertEqual(round(fa.fid, 7), 0.768326795e2) + self.assertEqual(round(fa.fitt, 11), -0.239319940e-2) + self.assertEqual(round(fa.fidt, 9), 0.256683306) + self.assertEqual(round(fa.fidd, 7), -0.685917373e2) water = IAPWS95() fv = water._derivDimensional(rhov, T) self.assertEqual(round(rhov, 10), 0.256669391e-1) - self.assertEqual(round(fv["fir"], 6), -0.143157426e3) - self.assertEqual(round(fv["firt"], 8), -0.851598213e1) - self.assertEqual(round(fv["fird"], 5), 0.538480619e4) - self.assertEqual(round(fv["firtt"], 11), -0.480817011e-2) - self.assertEqual(round(fv["firdt"], 7), 0.181489502e2) - self.assertEqual(round(fv["firdd"], 3), -0.210184992e6) + self.assertEqual(round(fv.fi, 6), -0.143157426e3) + self.assertEqual(round(fv.fit, 8), -0.851598213e1) + self.assertEqual(round(fv.fid, 5), 0.538480619e4) + self.assertEqual(round(fv.fitt, 11), -0.480817011e-2) + self.assertEqual(round(fv.fidt, 7), 0.181489502e2) + self.assertEqual(round(fv.fidd, 3), -0.210184992e6) A = 0.825565291 T = 400 @@ -2530,21 +2572,21 @@ def test_HumidAir(self): air = Air() fa = air._derivDimensional(rhoa, T) self.assertEqual(round(rhoa, 8), 0.654965578e1) - self.assertEqual(round(fa["fir"], 7), 0.895561286e2) - self.assertEqual(round(fa["firt"], 9), 0.193271394) - self.assertEqual(round(fa["fird"], 7), 0.175560114e2) - self.assertEqual(round(fa["firtt"], 11), -0.181809877e-2) - self.assertEqual(round(fa["firdt"], 10), 0.442769673e-1) - self.assertEqual(round(fa["firdd"], 8), -0.267635928e1) + self.assertEqual(round(fa.fi, 7), 0.895561286e2) + self.assertEqual(round(fa.fit, 9), 0.193271394) + self.assertEqual(round(fa.fid, 7), 0.175560114e2) + self.assertEqual(round(fa.fitt, 11), -0.181809877e-2) + self.assertEqual(round(fa.fidt, 10), 0.442769673e-1) + self.assertEqual(round(fa.fidd, 8), -0.267635928e1) water = IAPWS95() fv = water._derivDimensional(rhov, T) self.assertEqual(round(rhov, 8), 0.138388485e1) - self.assertEqual(round(fv["fir"], 6), -0.285137534e3) - self.assertEqual(round(fv["firt"], 8), -0.705288048e1) - self.assertEqual(round(fv["fird"], 6), 0.129645039e3) - self.assertEqual(round(fv["firtt"], 11), -0.411710659e-2) - self.assertEqual(round(fv["firdt"], 9), 0.361784086) - self.assertEqual(round(fv["firdd"], 7), -0.965539462e2) + self.assertEqual(round(fv.fi, 6), -0.285137534e3) + self.assertEqual(round(fv.fit, 8), -0.705288048e1) + self.assertEqual(round(fv.fid, 6), 0.129645039e3) + self.assertEqual(round(fv.fitt, 11), -0.411710659e-2) + self.assertEqual(round(fv.fidt, 9), 0.361784086) + self.assertEqual(round(fv.fidd, 7), -0.965539462e2) # Table 15 A = 0.892247719 @@ -2639,13 +2681,14 @@ def test_HumidAir(self): self.assertEqual(round(hum.cp, 8), 1.09387397) self.assertEqual(round(hum.cp-200*hum.derivative("s", "T", "P"), 8), 0) - def test_Ammonia(self): + def test_Ammonia(self) -> None: """Selected point front table of pag 42""" st = NH3(T=-77.65+273.15, x=0.5) self.assertEqual(round(st.P, 5), 0.00609) self.assertEqual(round(st.Liquid.rho, 2), 732.90) self.assertEqual(round(st.Gas.rho, 4), 0.0641) # self.assertEqual(round(st.Liquid.h, 2), -143.14) + assert(st.Hvap is not None) self.assertEqual(round(st.Hvap, 1), 1484.4) self.assertEqual(round(st.Gas.h, 1), 1341.2) self.assertEqual(round(st.Liquid.s, 4), -0.4715) @@ -2657,9 +2700,11 @@ def test_Ammonia(self): self.assertEqual(round(st.Liquid.rho, 2), 638.57) self.assertEqual(round(st.Gas.rho, 4), 3.4567) self.assertEqual(round(st.Liquid.h, 2), 200.00) + assert(st.Hvap is not None) self.assertEqual(round(st.Hvap, 1), 1262.2) self.assertEqual(round(st.Gas.h, 1), 1462.2) self.assertEqual(round(st.Liquid.s, 4), 1.0000) + assert(st.Svap is not None) self.assertEqual(round(st.Svap, 4), 4.6210) self.assertEqual(round(st.Gas.s, 4), 5.6210) @@ -2668,9 +2713,11 @@ def test_Ammonia(self): self.assertEqual(round(st.Liquid.rho, 2), 357.80) self.assertEqual(round(st.Gas.rho, 2), 120.73) self.assertEqual(round(st.Liquid.h, 2), 919.68) + assert(st.Hvap is not None) self.assertEqual(round(st.Hvap, 2), 389.44) self.assertEqual(round(st.Gas.h, 1), 1309.1) self.assertEqual(round(st.Liquid.s, 4), 3.0702) + assert(st.Svap is not None) self.assertEqual(round(st.Svap, 4), 0.9781) self.assertEqual(round(st.Gas.s, 4), 4.0483) @@ -2679,6 +2726,7 @@ def test_Ammonia(self): self.assertEqual(round(st.Liquid.rho, 2), 602.92) # self.assertEqual(round(st.Gas.rho, 4), 7.7821) self.assertEqual(round(st.Liquid.h, 2), 317.16) + assert(st.Hvap is not None) self.assertEqual(round(st.Hvap, 1), 1166.2) self.assertEqual(round(st.Gas.h, 1), 1483.4) self.assertEqual(round(st.Liquid.s, 4), 1.4072) @@ -2688,7 +2736,8 @@ def test_Ammonia(self): if major == 3: self.assertWarns(Warning, st._thermo, *(235, st.Tc, st)) - def test_AmmoniaVisco(self): + def test_AmmoniaVisco(self) -> None: + """Appendix II & III, page 1664 & 1667.""" # Appendix II, pag 1664 st = NH3(T=680, P=0.1) self.assertEqual(round(st.mu*1e6, 2), 24.66) @@ -2719,8 +2768,8 @@ def test_AmmoniaVisco(self): self.assertEqual(round(st.Liquid.rhoM, 4), 19.1642) self.assertEqual(round(st.Liquid.mu*1e6, 2), 39.20) - def test_nh3h2o(self): - + def test_nh3h2o(self) -> None: + """Test outstanding problems in H2ONH3.""" # Range of validity Tt1 = Ttr(0) Tt2 = Ttr(0.5) @@ -2739,6 +2788,9 @@ def test_nh3h2o(self): # self.assertEqual(round(st["u"], 5), 0) # self.assertEqual(round(st["s"], 5), 0) + # Suppress flake8 error until tests are working. + self.assertIsInstance(st["u"], float) + nh3 = NH3(T=NH3.Tt, x=0) st = cl._prop(nh3.rho, NH3.Tt, 1) # self.assertEqual(round(st["u"], 5), 0) @@ -2746,7 +2798,7 @@ def test_nh3h2o(self): # Table 6 x = 0.1 - rhoM = 35 + rhoM = 35.0 M = (1-x)*IAPWS95.M+x*NH3.M rho = rhoM*M st = cl._prop(rho, 600, x) @@ -2758,7 +2810,7 @@ def test_nh3h2o(self): # self.assertEqual(round(st["w"], 6), 883.925596) x = 0.1 - rhoM = 4 + rhoM = 4.0 M = (1-x)*IAPWS95.M+x*NH3.M rho = rhoM*M st = cl._prop(rho, 600, x) @@ -2768,7 +2820,7 @@ def test_nh3h2o(self): # self.assertEqual(round(st["w"], 6), 471.762394) x = 0.5 - rhoM = 32 + rhoM = 32.0 M = (1-x)*IAPWS95.M+x*NH3.M rho = rhoM*M st = cl._prop(rho, 500, x) @@ -2778,7 +2830,7 @@ def test_nh3h2o(self): # self.assertEqual(round(st["w"], 6), 830.295833) x = 0.5 - rhoM = 1 + rhoM = 1.0 M = (1-x)*IAPWS95.M+x*NH3.M rho = rhoM*M st = cl._prop(rho, 500, x) @@ -2788,7 +2840,7 @@ def test_nh3h2o(self): # self.assertEqual(round(st["w"], 6), 510.258362) x = 0.9 - rhoM = 30 + rhoM = 30.0 M = (1-x)*IAPWS95.M+x*NH3.M rho = rhoM*M st = cl._prop(rho, 400, x)