# -*- coding: utf-8 -*-
# BioSTEAM: The Biorefinery Simulation and Techno-Economic Analysis Modules
# Copyright (C) 2020, Yoel Cortes-Pena <yoelcortes@gmail.com>
#
# A significant portion of this module originates from:
# Chemical Engineering Design Library (ChEDL). Utilities for process modeling.
# Copyright (C) 2020 Caleb Bell <Caleb.Andrew.Bell@gmail.com>
#
# This module is under a dual license:
# 1. The UIUC open-source license. See
# github.com/BioSTEAMDevelopmentGroup/biosteam/blob/master/LICENSE.txt
# for license details.
#
# 2. The MIT open-source license. See
# https://github.com/CalebBell/thermo/blob/master/LICENSE.txt for details.
"""
Functional utilities for the calculation of thermodynamic properties.
"""
from cmath import sqrt as csqrt
from flexsolve import njitable
from .base import functor
from ._constants import R
import numpy as np
__all__ = ('isobaric_expansion', 'isothermal_compressibility',
'Cp_minus_Cv', 'speed_of_sound', 'Joule_Thomson',
'phase_identification_parameter', 'phase_identification_parameter_phase',
'isentropic_exponent', 'rho_to_V', 'V_to_rho', 'mu_to_nu',
'Z', 'B_to_Z', 'Z_to_B', 'Z_from_virial_density_form',
'Z_from_virial_pressure_form', 'zs_to_ws', 'ws_to_zs', 'zs_to_Vfs',
'Vfs_to_zs', 'none_and_length_check', 'normalize', 'mixing_simple',
'mixing_logarithmic', 'Parachor', 'SG_to_API', 'API_to_SG', 'SG',
'horner', 'allclose_variable', 'polylog2', 'horner',
)
@functor
def horner_polynomial(T, coeffs):
tot = 0
for c in coeffs: tot = tot * T + c
return tot
horner = horner_polynomial.function
def Pr(Cp, mu, k):
return Cp * mu / k
[docs]def mu_to_nu(mu, rho):
r"""
Return the kinematic viscosity (nu) given the dynamic viscosity (mu) and
density (rho).
.. math::
\nu = \frac{\mu}{\rho}
Examples
--------
>>> mu_to_nu(0.000998, 998.)
1.0e-06
"""
return mu/rho
[docs]def V_to_rho(V, MW):
r'''
Return the density (rho) in kg/m^3 given the molar volume (V) in
m^3/mol and molecular weight (MW) in g/mol.
.. math::
\rho = \frac{MW}{1000\cdot V}
Parameters
----------
V : float
Molar volume, [m^3/mol]
MW : float
Molecular weight, [g/mol]
Returns
-------
rho : float
Density, [kg/m^3]
Examples
--------
>>> rho(0.000132, 86.18)
652.8787878787879
'''
return MW/V/1000.
[docs]def rho_to_V(rho, MW):
r'''
Return the molar volume (V) in m^3/mol given the density (rho) in
kg/m^3 and molecular weight (MW) in g/mol.
.. math::
V = \left(\frac{1000 \rho}{MW}\right)^{-1}
Parameters
----------
rho : float
Density, [kg/m^3]
MW : float
Molecular weight, [g/mol]
Returns
-------
V : float
Molar volume, [m^3/mol]
Examples
--------
>>> V(652.9, 86.18)
0.00013199571144126206
'''
return MW/rho/1000.
[docs]def SG_to_API(SG):
r'''
Return specific gravity of a liquid given its API, as shown in
[1]_.
.. math::
\text{API gravity} = \frac{141.5}{\text{SG}} - 131.5
Parameters
----------
SG : float
Specific gravity of the fluid at 60 degrees Farenheight [-]
Returns
-------
API : float
API of the fluid [-]
Notes
-----
Defined only at 60 degrees Fahrenheit.
Examples
--------
>>> SG_to_API(0.7365)
60.62491513917175
References
----------
.. [1] API Technical Data Book: General Properties & Characterization.
American Petroleum Institute, 7E, 2005.
'''
return 141.5/SG - 131.5
[docs]def API_to_SG(API):
r'''
Calculates API of a liquid given its specific gravity, as shown in
[1]_.
.. math::
\text{SG at}~60^\circ\text{F} = \frac{141.5}{\text{API gravity} +131.5}
Parameters
----------
API : float
API of the fluid [-]
Returns
-------
SG : float
Specific gravity of the fluid at 60 degrees Farenheight [-]
Notes
-----
Defined only at 60 degrees Fahrenheit.
Examples
--------
>>> API_to_SG(60.62)
0.7365188423901728
References
----------
.. [1] API Technical Data Book: General Properties & Characterization.
American Petroleum Institute, 7E, 2005.
'''
return 141.5/(API + 131.5)
[docs]def SG(rho, rho_ref=999.0170824078306):
r'''
Calculates the specific gravity of a substance with respect to another
substance; by default, this is water at 15.6 °C (60 °F). For gases,
normally the reference density is 1.2 kg/m^3, that of dry air. However, in
general specific gravity should always be specified with respect to the
temperature and pressure of its reference fluid. This can vary widely.
.. math::
SG = \frac{\rho}{\rho_{ref}}
Parameters
----------
rho : float
Density of the substance, [kg/m^3]
rho_ref : float, optional
Density of the reference substance, [kg/m^3]
Returns
-------
SG : float
Specific gravity of the substance with respect to the reference
density, [-]
Notes
-----
Another common reference point is water at 4°C (rho_ref=999.9748691393087).
Specific gravity is often used by consumers instead of density.
The reference for solids is normally the same as for liquids - water.
Examples
--------
>>> SG(860)
0.8608461408159591
'''
return rho/rho_ref
[docs]def Parachor(V_l, V_g, sigma):
r'''
Calculate Parachor for a pure species, using its density in the
liquid and gas phases, surface tension, and molecular weight.
.. math::
P = \frac{\sigma^{0.25}}{V_l^{-1} - V_g^{-1}}
Parameters
----------
MW : float
Molecular weight, [g/mol]
V_l : float
Liquid density [mol/m^3]
V_g : float
Gas density [mol/m^3]
sigma : float
Surface tension, [N/m]
Returns
-------
P : float
Parachor, [N^0.25*m^2.75/mol]
Notes
-----
To convert the output of this function to units of [mN^0.25*m^2.75/kmol],
multiply by 5623.4132519.
Values in group contribution tables for Parachor are often listed as
dimensionless, in which they are multiplied by 5623413 and the appropriate
units to make them dimensionless.
Examples
--------
Calculating Parachor from a known surface tension for methyl isobutyl
ketone at 293.15 K
>>> Parachor(100.15888, 800.8088185536124, 4.97865317223119, 0.02672166960656005)
5.088443542210164e-05
Converting to the `dimensionless` form:
>>> 5623413*5.088443542210164e-05
286.14419565030687
Compared to 274.9 according to a group contribution method described in
[3]_.
References
----------
.. [1] Poling, Bruce E. The Properties of Gases and Liquids. 5th edition.
New York: McGraw-Hill Professional, 2000.
.. [2] Green, Don, and Robert Perry. Perry's Chemical Engineers' Handbook,
8E. McGraw-Hill Professional, 2007.
.. [3] Danner, Ronald P, and Design Institute for Physical Property Data.
Manual for Predicting Chemical Process Design Data. New York, N.Y, 1982.
'''
return sigma**0.25 / (1./V_l - 1./V_g) # (N/m)**0.25*g/mol/(g/m^3)
[docs]def isobaric_expansion(V, dV_dT):
r'''
Calculate the isobaric coefficient of a thermal expansion, given its
molar volume at a certain `T` and `P`, and its derivative of molar volume
with respect to `T`.
.. math::
\beta = \frac{1}{V}\left(\frac{\partial V}{\partial T} \right)_P
Parameters
----------
V : float
Molar volume at `T` and `P`, [m^3/mol]
dV_dT : float
Derivative of molar volume with respect to `T`, [m^3/mol/K]
Returns
-------
beta : float
Isobaric coefficient of a thermal expansion, [1/K]
Notes
-----
For an ideal gas, this expression simplified to:
.. math::
\beta = \frac{1}{T}
Examples
--------
Calculated for hexane from the PR EOS at 299 K and 1 MPa (liquid):
>>> isobaric_expansion(0.000130229900873546, 1.58875261849113e-7)
0.0012199599384121608
References
----------
.. [1] Poling, Bruce E. The Properties of Gases and Liquids. 5th edition.
New York: McGraw-Hill Professional, 2000.
'''
return dV_dT/V
[docs]def isothermal_compressibility(V, dV_dP):
r'''Calculate the isothermal coefficient of a compressibility, given its
molar volume at a certain `T` and `P`, and its derivative of molar volume
with respect to `P`.
.. math::
\kappa = -\frac{1}{V}\left(\frac{\partial V}{\partial P} \right)_T
Parameters
----------
V : float
Molar volume at `T` and `P`, [m^3/mol]
dV_dP : float
Derivative of molar volume with respect to `P`, [m^3/mol/Pa]
Returns
-------
kappa : float
Isothermal coefficient of a compressibility, [1/Pa]
Notes
-----
For an ideal gas, this expression simplified to:
.. math::
\kappa = \frac{1}{P}
Examples
--------
Calculated for hexane from the PR EOS at 299 K and 1 MPa (liquid):
>>> isothermal_compressibility(0.000130229900873546, -2.72902118209903e-13)
2.095541165119158e-09
References
----------
.. [1] Poling, Bruce E. The Properties of Gases and Liquids. 5th edition.
New York: McGraw-Hill Professional, 2000.
'''
return -dV_dP/V
[docs]def phase_identification_parameter(V, dP_dT, dP_dV, d2P_dV2, d2P_dVdT):
r'''
Calculate the Phase Identification Parameter developed in [1]_ for
the accurate and efficient determination of whether a fluid is a liquid or
a gas based on the results of an equation of state. For supercritical
conditions, this provides a good method for choosing which property
correlations to use.
.. math::
\Pi = V \left[\frac{\frac{\partial^2 P}{\partial V \partial T}}
{\frac{\partial P }{\partial T}}- \frac{\frac{\partial^2 P}{\partial
V^2}}{\frac{\partial P}{\partial V}} \right]
Parameters
----------
V : float
Molar volume at `T` and `P`, [m^3/mol]
dP_dT : float
Derivative of `P` with respect to `T`, [Pa/K]
dP_dV : float
Derivative of `P` with respect to `V`, [Pa*mol/m^3]
d2P_dV2 : float
Second derivative of `P` with respect to `V`, [Pa*mol^2/m^6]
d2P_dVdT : float
Second derivative of `P` with respect to both `V` and `T`, [Pa*mol/m^3/K]
Returns
-------
PIP : float
Phase Identification Parameter, [-]
Notes
-----
Heuristics were used by process simulators before the invent of this
parameter.
The criteria for liquid is Pi > 1; for vapor, Pi <= 1.
There is also a solid phase mechanism available. For solids, the Solid
Phase Identification Parameter is greater than 1, like liquids; however,
unlike liquids, d2P_dVdT is always >0; it is < 0 for liquids and gases.
Examples
--------
Calculated for hexane from the PR EOS at 299 K and 1 MPa (liquid):
>>> phase_identification_parameter(0.000130229900874, 582169.397484,
... -3.66431747236e+12, 4.48067893805e+17, -20518995218.2)
11.33428990564796
References
----------
.. [1] Venkatarathnam, G., and L. R. Oellrich. "Identification of the Phase
of a Fluid Using Partial Derivatives of Pressure, Volume, and
Temperature without Reference to Saturation Properties: Applications in
Phase Equilibria Calculations." Fluid Phase Equilibria 301, no. 2
(February 25, 2011): 225-33. doi:10.1016/j.fluid.2010.12.001.
.. [2] Jayanti, Pranava Chaitanya, and G. Venkatarathnam. "Identification
of the Phase of a Substance from the Derivatives of Pressure, Volume and
Temperature, without Prior Knowledge of Saturation Properties: Extension
to Solid Phase." Fluid Phase Equilibria 425 (October 15, 2016): 269-277.
doi:10.1016/j.fluid.2016.06.001.
'''
return V*(d2P_dVdT/dP_dT - d2P_dV2/dP_dV)
[docs]def phase_identification_parameter_phase(d2P_dVdT, V=None, dP_dT=None, dP_dV=None, d2P_dV2=None):
r'''
Uses the Phase Identification Parameter concept developed in [1]_ and
[2]_ to determine if a chemical is a solid, liquid, or vapor given the
appropriate thermodynamic conditions.
The criteria for liquid is PIP > 1; for vapor, PIP <= 1.
For solids, PIP(solid) is defined to be d2P_dVdT. If it is larger than 0,
the species is a solid. It is less than 0 for all liquids and gases.
Parameters
----------
d2P_dVdT : float
Second derivative of `P` with respect to both `V` and `T`, [Pa*mol/m^3/K]
V : float, optional
Molar volume at `T` and `P`, [m^3/mol]
dP_dT : float, optional
Derivative of `P` with respect to `T`, [Pa/K]
dP_dV : float, optional
Derivative of `P` with respect to `V`, [Pa*mol/m^3]
d2P_dV2 : float, optionsl
Second derivative of `P` with respect to `V`, [Pa*mol^2/m^6]
Returns
-------
phase : str
Either 's', 'l' or 'g'
Notes
-----
The criteria for being a solid phase is checked first, which only
requires d2P_dVdT. All other inputs are optional for this reason.
However, an exception will be raised if the other inputs become
needed to determine if a species is a liquid or a gas.
Examples
--------
Calculated for hexane from the PR EOS at 299 K and 1 MPa (liquid):
>>> phase_identification_parameter_phase(-20518995218.2, 0.000130229900874,
... 582169.397484, -3.66431747236e+12, 4.48067893805e+17)
'l'
References
----------
.. [1] Venkatarathnam, G., and L. R. Oellrich. "Identification of the Phase
of a Fluid Using Partial Derivatives of Pressure, Volume, and
Temperature without Reference to Saturation Properties: Applications in
Phase Equilibria Calculations." Fluid Phase Equilibria 301, no. 2
(February 25, 2011): 225-33. doi:10.1016/j.fluid.2010.12.001.
.. [2] Jayanti, Pranava Chaitanya, and G. Venkatarathnam. "Identification
of the Phase of a Substance from the Derivatives of Pressure, Volume and
Temperature, without Prior Knowledge of Saturation Properties: Extension
to Solid Phase." Fluid Phase Equilibria 425 (October 15, 2016): 269-277.
doi:10.1016/j.fluid.2016.06.001.
'''
if d2P_dVdT > 0:
return 's'
else:
PIP = phase_identification_parameter(V=V, dP_dT=dP_dT, dP_dV=dP_dV,
d2P_dV2=d2P_dV2, d2P_dVdT=d2P_dVdT)
return 'l' if PIP > 1 else 'g'
[docs]def Cp_minus_Cv(T, dP_dT, dP_dV):
r'''
Calculate the difference between a real gas's constant-pressure heat
capacity and constant-volume heat capacity, as given in [1]_, [2]_, and
[3]_. The required derivatives should be calculated with an equation of
state.
.. math::
C_p - C_v = -T\left(\frac{\partial P}{\partial T}\right)_V^2/
\left(\frac{\partial P}{\partial V}\right)_T
Parameters
----------
T : float
Temperature of fluid [K]
dP_dT : float
Derivative of `P` with respect to `T`, [Pa/K]
dP_dV : float
Derivative of `P` with respect to `V`, [Pa*mol/m^3]
Returns
-------
Cp_minus_Cv : float
Cp - Cv for a real gas, [J/mol/K]
Notes
-----
Equivalent expressions are:
.. math::
C_p - C_v= -T\left(\frac{\partial V}{\partial T}\right)_P^2/\left(
\frac{\partial V}{\partial P}\right)_T
C_p - C_v = T\left(\frac{\partial P}{\partial T}\right)
\left(\frac{\partial V}{\partial T}\right)
Note that these are not second derivatives, only first derivatives, some
of which are squared.
Examples
--------
Calculated for hexane from the PR EOS at 299 K and 1 MPa (liquid):
>>> Cp_minus_Cv(299, 582232.475794113, -3665180614672.253)
27.654681381642394
References
----------
.. [1] Poling, Bruce E. The Properties of Gases and Liquids. 5th edition.
New York: McGraw-Hill Professional, 2000.
.. [2] Walas, Stanley M. Phase Equilibria in Chemical Engineering.
Butterworth-Heinemann, 1985.
.. [3] Gmehling, Jurgen, Barbel Kolbe, Michael Kleiber, and Jurgen Rarey.
Chemical Thermodynamics for Process Simulation. 1st edition. Weinheim:
Wiley-VCH, 2012.
'''
return -T*dP_dT**2/dP_dV
[docs]def speed_of_sound(V, dP_dV, Cp, Cv, MW=None):
r'''
Calculate a real fluid's speed of sound. The required derivatives should
be calculated with an equation of state, and `Cp` and `Cv` are both the
real fluid versions. Expression is given in [1]_ and [2]_; a unit conversion
is further performed to obtain a result in m/s. If MW is not provided the
result is returned in units of m*kg^0.5/s/mol^0.5.
.. math::
w = \left[-V^2 \left(\frac{\partial P}{\partial V}\right)_T \frac{C_p}
{C_v}\right]^{1/2}
Parameters
----------
V : float
Molar volume of fluid, [m^3/mol]
dP_dV : float
Derivative of `P` with respect to `V`, [Pa*mol/m^3]
Cp : float
Real fluid heat capacity at constant pressure, [J/mol/K]
Cv : float
Real fluid heat capacity at constant volume, [J/mol/K]
MW : float, optional
Molecular weight, [g/mol]
Returns
-------
w : float
Speed of sound for a real gas, [m/s or m*kg^0.5/s/mol^0.5 or MW missing]
Notes
-----
An alternate expression based on molar density is as follows:
.. math::
w = \left[\left(\frac{\partial P}{\partial \rho}\right)_T \frac{C_p}
{C_v}\right]^{1/2}
The form with the unit conversion performed inside it is as follows:
.. math::
w = \left[-V^2 \frac{1000}{MW}\left(\frac{\partial P}{\partial V}
\right)_T \frac{C_p}{C_v}\right]^{1/2}
Examples
--------
Example from [2]_:
>>> speed_of_sound(V=0.00229754, dP_dV=-3.5459e+08, Cp=153.235, Cv=132.435, MW=67.152)
179.5868138460819
References
----------
.. [1] Gmehling, Jurgen, Barbel Kolbe, Michael Kleiber, and Jurgen Rarey.
Chemical Thermodynamics for Process Simulation. 1st edition. Weinheim:
Wiley-VCH, 2012.
.. [2] Pratt, R. M. "Thermodynamic Properties Involving Derivatives: Using
the Peng-Robinson Equation of State." Chemical Engineering Education 35,
no. 2 (March 1, 2001): 112-115.
'''
if not MW:
return (-V**2*dP_dV*Cp/Cv)**0.5
else:
return (-V**2*1000./MW*dP_dV*Cp/Cv)**0.5
[docs]def Joule_Thomson(T, V, Cp, dV_dT=None, beta=None):
r'''
Calculate a real fluid's Joule Thomson coefficient. The required
derivative should be calculated with an equation of state, and `Cp` is the
real fluid versions. This can either be calculated with `dV_dT` directly,
or with `beta` if it is already known.
.. math::
\mu_{JT} = \left(\frac{\partial T}{\partial P}\right)_H = \frac{1}{C_p}
\left[T \left(\frac{\partial V}{\partial T}\right)_P - V\right]
= \frac{V}{C_p}\left(\beta T-1\right)
Parameters
----------
T : float
Temperature of fluid, [K]
V : float
Molar volume of fluid, [m^3/mol]
Cp : float
Real fluid heat capacity at constant pressure, [J/mol/K]
dV_dT : float, optional
Derivative of `V` with respect to `T`, [m^3/mol/K]
beta : float, optional
Isobaric coefficient of a thermal expansion, [1/K]
Returns
-------
mu_JT : float
Joule-Thomson coefficient [K/Pa]
Examples
--------
Example from [2]_:
>>> Joule_Thomson(T=390, V=0.00229754, Cp=153.235, dV_dT=1.226396e-05)
1.621956080529905e-05
References
----------
.. [1] Walas, Stanley M. Phase Equilibria in Chemical Engineering.
Butterworth-Heinemann, 1985.
.. [2] Pratt, R. M. "Thermodynamic Properties Involving Derivatives: Using
the Peng-Robinson Equation of State." Chemical Engineering Education 35,
no. 2 (March 1, 2001): 112-115.
'''
if dV_dT:
return (T*dV_dT - V)/Cp
elif beta:
return V/Cp*(beta*T - 1.)
else:
raise Exception('Either dV_dT or beta is needed')
[docs]def isentropic_exponent(Cp, Cv):
r'''
Calculate the isentropic coefficient of a gas, given its constant-
pressure and constant-volume heat capacity.
.. math::
k = \frac{C_p}{C_v}
Parameters
----------
Cp : float
Gas heat capacity at constant pressure, [J/mol/K]
Cv : float
Gas heat capacity at constant volume, [J/mol/K]
Returns
-------
k : float
Isentropic exponent, [-]
Examples
--------
>>> isentropic_exponent(33.6, 25.27)
1.329639889196676
References
----------
.. [1] Poling, Bruce E. The Properties of Gases and Liquids. 5th edition.
New York: McGraw-Hill Professional, 2000.
'''
return Cp/Cv
[docs]def Z(T, P, V):
r'''
Calculates the compressibility factor of a gas, given its
temperature, pressure, and molar volume.
.. math::
Z = \frac{PV}{RT}
Parameters
----------
T : float
Temperature, [K]
P : float
Pressure [Pa]
V : float
Molar volume, [m^3/mol]
Returns
-------
Z : float
Compressibility factor, [-]
Examples
--------
>>> Z(600, P=1E6, V=0.00463)
0.9281019876560912
References
----------
.. [1] Poling, Bruce E. The Properties of Gases and Liquids. 5th edition.
New York: McGraw-Hill Professional, 2000.
'''
return V*P/T/R
[docs]def B_to_Z(B, T, P):
r'''
Calculates the compressibility factor of a gas, given its
second virial coefficient.
.. math::
Z = 1 + \frac{BP}{RT}
Parameters
----------
B : float
Second virial coefficient, [m^3/mol]
T : float
Temperature, [K]
P : float
Pressure [Pa]
Returns
-------
Z : float
Compressibility factor, [-]
Notes
-----
Other forms of the virial coefficient exist.
Examples
--------
>>> B_to_Z(-0.0015, 300, 1E5)
0.9398638020957176
References
----------
.. [1] Poling, Bruce E. The Properties of Gases and Liquids. 5th edition.
New York: McGraw-Hill Professional, 2000.
'''
return 1. + B*P/R/T
[docs]def Z_to_B(Z, T, P):
r'''
Calculates the second virial coefficient of a pure species, given the
compressibility factor of the gas.
.. math::
B = \frac{RT(Z-1)}{P}
Parameters
----------
Z : float
Compressibility factor, [-]
T : float
Temperature, [K]
P : float
Pressure [Pa]
Returns
-------
B : float
Second virial coefficient, [m^3/mol]
Notes
-----
Other forms of the virial coefficient exist.
Examples
--------
>>> B_from_Z(0.94, 300, 1E5)
-0.0014966027640000014
References
----------
.. [1] Poling, Bruce E. The Properties of Gases and Liquids. 5th edition.
New York: McGraw-Hill Professional, 2000.
'''
return (Z - 1)*R*T/P
[docs]def zs_to_ws(zs, MWs):
r'''
Converts a list of mole fractions to mass fractions. Requires molecular
weights for all species.
.. math::
w_i = \frac{z_i MW_i}{MW_{avg}}
MW_{avg} = \sum_i z_i MW_i
Parameters
----------
zs : iterable
Mole fractions [-]
MWs : iterable
Molecular weights [g/mol]
Returns
-------
ws : iterable
Mass fractions [-]
Notes
-----
Does not check that the sums add to one. Does not check that inputs are of
the same length.
Examples
--------
>>> zs_to_ws([0.5, 0.5], [10, 20])
[0.3333333333333333, 0.6666666666666666]
'''
zs = np.asarray(zs)
MWs = np.asarray(MWs)
ws = zs * MWs
ws /= ws.sum()
return ws
[docs]def ws_to_zs(ws, MWs):
r'''
Converts a list of mass fractions to mole fractions. Requires molecular
weights for all species.
.. math::
z_i = \frac{\frac{w_i}{MW_i}}{\sum_i \frac{w_i}{MW_i}}
Parameters
----------
ws : iterable
Mass fractions [-]
MWs : iterable
Molecular weights [g/mol]
Returns
-------
zs : iterable
Mole fractions [-]
Notes
-----
Does not check that the sums add to one. Does not check that inputs are of
the same length.
Examples
--------
>>> ws_to_zs([0.3333333333333333, 0.6666666666666666], [10, 20])
[0.5, 0.5]
'''
ws = np.asarray(ws)
MWs = np.asarray(MWs)
zs = ws/MWs
zs /= zs.sum()
return zs
[docs]def zs_to_Vfs(zs, Vs):
r'''
Converts a list of mole fractions to volume fractions. Requires molar
volumes for all species.
.. math::
\text{Vf}_i = \frac{z_i V_{i}}{\sum_i z_i V_{i}}
Parameters
----------
zs : iterable
Mole fractions [-]
Vs : iterable
Molar volumes of species [m^3/mol]
Returns
-------
Vfs : list
Molar volume fractions [-]
Notes
-----
Does not check that the sums add to one. Does not check that inputs are of
the same length.
Molar volumes are specified in terms of pure components only. Function
works with any phase.
Examples
--------
Acetone and benzene example
>>> zs_to_Vfs([0.637, 0.363], [8.0234e-05, 9.543e-05])
[0.5960229712956298, 0.4039770287043703]
'''
zs = np.asarray(zs)
Vs = np.asarray(Vs)
Vfs = zs * Vs
Vfs /= Vfs.sum()
return Vfs
[docs]def Vfs_to_zs(Vfs, Vs):
r'''
Converts a list of mass fractions to mole fractions. Requires molecular
weights for all species.
.. math::
z_i = \frac{\frac{\text{Vf}_i}{V_{m,i}}}{\sum_i
\frac{\text{Vf}_i}{V_{m,i}}}
Parameters
----------
Vfs : iterable
Molar volume fractions [-]
VMs : iterable
Molar volumes of species [m^3/mol]
Returns
-------
zs : list
Mole fractions [-]
Notes
-----
Does not check that the sums add to one. Does not check that inputs are of
the same length.
Molar volumes are specified in terms of pure components only. Function
works with any phase.
Examples
--------
Acetone and benzene example
>>> Vfs_to_zs([0.596, 0.404], [8.0234e-05, 9.543e-05])
[0.6369779395901142, 0.3630220604098858]
'''
Vs = np.asarray(Vs)
Vfs = np.asarray(Vfs)
zs = Vfs/Vs
zs /= zs.sum()
return zs
[docs]def none_and_length_check(all_inputs, length=None):
r'''
Checks inputs for suitability of use by a mixing rule which requires
all inputs to be of the same length and non-None. A number of variations
were attempted for this function; this was found to be the quickest.
Parameters
----------
all_inputs : array-like of array-like
list of all the lists of inputs, [-]
length : int, optional
Length of the desired inputs, [-]
Returns
-------
False/True : bool
Returns True only if all inputs are the same length (or length `length`)
and none of the inputs contain None [-]
Notes
-----
Does not check for nan values.
Examples
--------
>>> none_and_length_check(([1, 1], [1, 1], [1, 30], [10,0]), length=2)
True
'''
if not length:
length = len(all_inputs[0])
for things in all_inputs:
if None in things or len(things) != length:
return False
return True
[docs]def allclose_variable(a, b, limits, rtols, atols):
'''
Returns True if two arrays are element-wise equal within several
different tolerances. Tolerance values are always positive, usually
very small. Based on numpy's allclose function.
Only atols or rtols needs to be specified; both are used if given.
Parameters
----------
a, b : array_like
Input arrays to compare.
limits : array_like
Fractions of elements allowed to not match to within each tolerance.
rtols : array_like
The relative tolerance parameters.
atols : float
The absolute tolerance parameters.
Returns
-------
allclose : bool
Returns True if the two arrays are equal within the given
tolerances; False otherwise.
Examples
--------
10 random similar variables, all of them matching to within 1E-5, allowing
up to half to match up to 1E-6.
>>> x = [2.7244322249597719e-08, 3.0105683900110473e-10, 2.7244124924802327e-08, 3.0105259397637556e-10, 2.7243929226310193e-08, 3.0104990272770901e-10, 2.7243666849384451e-08, 3.0104101821236015e-10, 2.7243433745917367e-08, 3.0103707421519949e-10]
>>> y = [2.7244328304561904e-08, 3.0105753470546008e-10, 2.724412872417824e-08, 3.0105303055834564e-10, 2.7243914341030203e-08, 3.0104819238021998e-10, 2.7243684057561379e-08, 3.0104299541023674e-10, 2.7243436694839306e-08, 3.010374130526363e-10]
>>> allclose_variable(x, y, limits=[.0, .5], rtols=[1E-5, 1E-6], atols=[0, 0])
True
'''
l = float(len(a))
for atol, rtol, lim in zip(atols, rtols, limits):
matches = np.count_nonzero(np.isclose(a, b, rtol=rtol, atol=atol))
if 1-matches/l > lim:
return False
return True
[docs]def polylog2(x):
r'''
Simple function to calculate PolyLog(2, x) from ranges 0 <= x <= 1,
with relative error guaranteed to be < 1E-7 from 0 to 0.99999. This
is a Pade approximation, with three coefficient sets with splits at 0.7
and 0.99. An exception is raised if x is under 0 or above 1.
Parameters
----------
x : float
Value to evaluate PolyLog(2, x) T
Returns
-------
y : float
Evaluated result
Notes
-----
Efficient (2-4 microseconds). No implementation of this function exists in
SciPy. Derived with mpmath's pade approximation.
Required for the entropy integral of
:obj:`thermo.heat_capacity.Zabransky_quasi_polynomial`.
Examples
--------
>>> polylog2(0.5)
0.5822405264516294
'''
if 0. <= x <= 0.7:
p, q, offset = polylog2._pqoffset0
elif 0.7 < x <= 0.99:
p, q, offset = polylog2._pqoffset1
elif 0.99 < x <= 1.:
p, q, offset = polylog2._pqoffset2
else:
raise ValueError('approximation is valid between 0 and 1 only')
x = x - offset
return horner(x, p)/horner(x, q)
polylog2._pqoffset0 = ((0.06184590404457956, -0.7460693871557973,
2.2435704485433376, -2.1944070385048526,
0.3382265629285811, 0.2791966558569478),
(-0.005308735283483908, 0.1823421262956287,
-1.2364596896290079, 2.9897802200092296,
-2.9365321202088004, 1.0),
0.26)
polylog2._pqoffset1 = ((8.548256176424551e+34, 1.8485781239087334e+35,
-2.1706889553798647e+34, 8.318563643438321e+32,
-1.559802348661511e+31, 1.698939241177209e+29,
-1.180285031647229e+27, 5.531049937687143e+24,
-1.8085903366375877e+22, 4.203276811951035e+19,
-6.98211620300421e+16, 82281997048841.92,
-67157299796.61345, 36084814.54808544,
-11478.108105137717, 1.6370226052761176),
(-1.9763570499484274e+35, 1.4813997374958851e+35,
-1.4773854824041134e+34, 5.38853721252814e+32,
-9.882387315028929e+30, 1.0635231532999732e+29,
-7.334629044071992e+26, 3.420655574477631e+24,
-1.1147787784365177e+22, 2.584530363912858e+19,
-4.285376337404043e+16, 50430830490687.56,
-41115254924.43107, 22072284.971253656,
-7015.799744041691, 1.0),
0.999)
polylog2._pqoffset2 = ((8.548256176424551e+34, 1.8485781239087334e+35,
-2.1706889553798647e+34, 8.318563643438321e+32,
-1.559802348661511e+31, 1.698939241177209e+29,
-1.180285031647229e+27, 5.531049937687143e+24,
-1.8085903366375877e+22, 4.203276811951035e+19,
-6.98211620300421e+16, 82281997048841.92,
-67157299796.61345, 36084814.54808544,
-11478.108105137717, 1.6370226052761176),
(-1.9763570499484274e+35, 1.4813997374958851e+35,
-1.4773854824041134e+34, 5.38853721252814e+32,
-9.882387315028929e+30, 1.0635231532999732e+29,
-7.334629044071992e+26, 3.420655574477631e+24,
-1.1147787784365177e+22, 2.584530363912858e+19,
-4.285376337404043e+16, 50430830490687.56,
-41115254924.43107, 22072284.971253656,
-7015.799744041691, 1.0),
0.999)
[docs]@njitable(cache=True)
def normalize(array, minimum=1e-16):
"""
Return a normalized array to a magnitude of 1.
If magnitude is zero, all fractions will have equal value.
"""
sum_array = array.sum()
if sum_array < minimum:
size = array.size
return np.ones(size)/size
else:
return array/sum_array
[docs]@njitable(cache=True)
def mixing_simple(z, y):
r'''
Return a weighted average of `y` given the weights, `z`.
Examples
--------
>>> import numpy as np
>>> mixing_simple(np.array([0.1, 0.9]), np.array([0.01, 0.02]))
0.019000000000000003
'''
return (z * y).sum()
[docs]@njitable(cache=True)
def mixing_logarithmic(z, y):
r'''
Return the logarithmic weighted average `y` given weights, `z`.
.. math::
y = \sum_i z_i \cdot \log(y_i)
Notes
-----
Does not work on negative values.
Examples
--------
>>> import numpy as np
>>> mixing_logarithmic(np.array([0.1, 0.9]), np.array([0.01, 0.02]))
0.01866065983073615
'''
return np.exp((z*np.log(y)).sum())