'''Chemical Engineering Design Library (ChEDL). Utilities for process modeling.
Copyright (C) 2019, 2020 Caleb Bell <Caleb.Andrew.Bell@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
'''
__all__ = ['GibbsExcessLiquid', 'GibbsExcessSolid']
from math import isinf, isnan
from chemicals.solubility import Henry_constants, d2Henry_constants_dT2, dHenry_constants_dT
from chemicals.utils import phase_identification_parameter
from fluids.constants import R, R_inv
from fluids.numerics import derivative, evaluate_linear_fits, evaluate_linear_fits_d, evaluate_linear_fits_d2, exp, horner_and_der2, log, secant, trunc_exp
from fluids.numerics import numpy as np
from thermo.activity import IdealSolution
from thermo.heat_capacity import HeatCapacityGas, HeatCapacityLiquid
from thermo.nrtl import nrtl_d2taus_dT2 as d2ln_henries_dT2
from thermo.nrtl import nrtl_dtaus_dT as dln_henries_dT
from thermo.nrtl import nrtl_taus as ln_henries
from thermo.phase_change import EnthalpySublimation, EnthalpyVaporization
from thermo.phases.phase import Phase
from thermo.utils import POLY_FIT, TRANSFORM_SECOND_DERIVATIVE_RATIO, TRANSFORM_SECOND_LOG_DERIVATIVE, TRANSFORM_DERIVATIVE_RATIO, TRANSFORM_LOG_DERIVATIVE, TRANSFORM_LOG
from thermo.vapor_pressure import SublimationPressure, VaporPressure
from thermo.volume import VolumeLiquid, VolumeSolid
try:
zeros, array = np.zeros, np.array
except:
pass
[docs]class GibbsExcessLiquid(Phase):
r'''Phase based on combining Raoult's law with a
:obj:`GibbsExcess <thermo.activity.GibbsExcess>` model, optionally
including saturation fugacity coefficient corrections (if the vapor phase
is a cubic equation of state) and Poynting correction factors (if more
accuracy is desired).
The equilibrium equation options (controlled by `equilibrium_basis`)
are as follows:
* 'Psat': :math:`\phi_i = \frac{\gamma_i P_{i}^{sat}}{P}`
* 'Poynting&PhiSat': :math:`\phi_i = \frac{\gamma_i P_{i}^{sat} \phi_i^{sat} \text{Poynting}_i}{P}`
* 'Poynting': :math:`\phi_i = \frac{\gamma_i P_{i}^{sat}\text{Poynting}_i}{P}`
* 'PhiSat': :math:`\phi_i = \frac{\gamma_i P_{i}^{sat} \phi_i^{sat}}{P}`
In all cases, the activity coefficient is derived from the
:obj:`GibbsExcess <thermo.activity.GibbsExcess>` model specified as
input; use the :obj:`IdealSolution <thermo.activity.IdealSolution>`
class as an input to set the activity coefficients to one.
The enthalpy `H` and entropy `S` (and other caloric properties `U`, `G`, `A`)
equation options are similar to the equilibrium ones. If the same option
is selected for `equilibrium_basis` and `caloric_basis`, the phase will be
`thermodynamically consistent`. This is recommended for many reasons.
The full 'Poynting&PhiSat' equations for `H` and `S` are as follows; see
:obj:`GibbsExcessLiquid.H` and :obj:`GibbsExcessLiquid.S` for all of the
other equations:
.. math::
H = H_{\text{excess}} + \sum_i z_i \left[-RT^2\left(
\frac{\frac{\partial \phi_{\text{sat},i}}{\partial T}}{\phi_{\text{sat},i}}
+ \frac{\frac{\partial P_{\text{sat},i}}{\partial T}}{P_{\text{sat},i}}
+ \frac{\frac{\text{Poynting}}{\partial T}}{\text{Poynting}} \right)
+ \int_{T,ref}^T C_{p,ig} dT \right]
.. math::
S = S_{\text{excess}} - R\sum_i z_i\ln z_i - R\ln\left(\frac{P}{P_{ref}}\right)
- \sum_i z_i\left[R\left(
T \frac{\frac{\partial \phi_{\text{sat},i}}{\partial T}}{\phi_{\text{sat},i}}
+ T\frac{\frac{\partial P_{\text{sat},i}}{\partial T}}{P_{\text{sat},i}}
+ T\frac{\frac{\text{Poynting}}{\partial T}}{\text{Poynting}}
+ \ln(P_{\text{sat},i}) + \ln\left(\frac{\text{Poynting}\cdot\phi_{\text{sat},i}}{P}\right)
\right) - \int_{T,ref}^T \frac{C_{p,ig,i}}{T} dT \right]
An additional caloric mode is `Hvap`, which uses enthalpy of vaporization;
this mode can never be thermodynamically consistent, but is still widely
used.
.. math::
H = H_{\text{excess}} + \sum_i z_i\left[-H_{vap,i}
+ \int_{T,ref}^T C_{p,ig} dT \right]
.. math::
S = S_{\text{excess}} - R\sum_i z_i\ln z_i - R\ln\left(\frac{P}{P_{ref}}\right)
- \sum_i z_i\left[R\left(\ln P_{\text{sat},i} + \ln\left(\frac{1}{P}\right)\right)
+ \frac{H_{vap,i}}{T}
- \int_{T,ref}^T \frac{C_{p,ig,i}}{T} dT \right]
.. warning::
Note that above the critical point, there is no definition for what vapor
pressure is. The vapor pressure also tends to reach zero at temperatures
in the 4-20 K range. These aspects mean extrapolation in the supercritical and
very low temperature region is critical to ensure the equations will still
converge. Extrapolation can be performed using either the equation
:math:`P^{\text{sat}} = \exp\left(A - \frac{B}{T}\right)` or
:math:`P^{\text{sat}} = \exp\left(A + \frac{B}{T} + C\cdot \ln T\right)` by
setting `Psat_extrpolation` to either 'AB' or 'ABC' respectively.
The extremely low temperature region's issue is solved by calculating the
logarithm of vapor pressures instead of the actual value. While floating
point values in Python (doubles) can reach a minimum value of around
1e-308, if only the logarithm of that number is computed no issues arise.
Both of these features only work when the vapor pressure correlations are
polynomials.
.. warning::
When using 'PhiSat' as an option, note that the factor cannot be
calculated when a compound is supercritical,
as there is no longer any vapor-liquid pure-component equilibrium
(by definition).
Parameters
----------
VaporPressures : list[:obj:`thermo.vapor_pressure.VaporPressure`]
Objects holding vapor pressure data and methods, [-]
VolumeLiquids : list[:obj:`thermo.volume.VolumeLiquid`], optional
Objects holding liquid volume data and methods; required for Poynting
factors and volumetric properties, [-]
HeatCapacityGases : list[:obj:`thermo.heat_capacity.HeatCapacityGas`], optional
Objects proiding pure-component heat capacity correlations; required
for caloric properties, [-]
GibbsExcessModel : :obj:`GibbsExcess <thermo.activity.GibbsExcess>`, optional
Configured instance for calculating activity coefficients and excess properties;
set to :obj:`IdealSolution <thermo.activity.IdealSolution>` if not provided, [-]
eos_pure_instances : list[:obj:`thermo.eos.GCEOS`], optional
Cubic equation of state object instances for each pure component, [-]
EnthalpyVaporizations : list[:obj:`thermo.phase_change.EnthalpyVaporization`], optional
Objects holding enthalpy of vaporization data and methods; used only
with the 'Hvap' optional, [-]
HeatCapacityLiquids : list[:obj:`thermo.heat_capacity.HeatCapacityLiquid`], optional
Objects holding liquid heat capacity data and methods; not used at
present, [-]
VolumeSupercriticalLiquids : list[:obj:`thermo.volume.VolumeLiquid`], optional
Objects holding liquid volume data and methods but that are used for
supercritical temperatures on a per-component basis only; required for
Poynting factors and volumetric properties at supercritical conditions;
`VolumeLiquids` is used if not provided, [-]
Hfs : list[float], optional
Molar ideal-gas standard heats of formation at 298.15 K and 1 atm,
[J/mol]
Gfs : list[float], optional
Molar ideal-gas standard Gibbs energies of formation at 298.15 K and
1 atm, [J/mol]
T : float, optional
Temperature, [K]
P : float, optional
Pressure, [Pa]
zs : list[float], optional
Mole fractions of each component, [-]
equilibrium_basis : str, optional
Which set of equilibrium equations to use when calculating fugacities
and related properties; valid options are 'Psat', 'Poynting&PhiSat',
'Poynting', 'PhiSat', [-]
caloric_basis : str, optional
Which set of caloric equations to use when calculating fugacities
and related properties; valid options are 'Psat', 'Poynting&PhiSat',
'Poynting', 'PhiSat', 'Hvap' [-]
Psat_extrpolation : str, optional
One of 'AB' or 'ABC'; configures extrapolation for vapor pressure, [-]
henry_abcdef : tuple[list[list[float]], 6], optional
Contains the parameters used for henry's law constant, [-]
henry_as : list[list[float]], optional
`a` parameters used in calculating henry's law constant, [-]
henry_bs : list[list[float]], optional
`b` parameters used in calculating henry's law constant, [K]
henry_cs : list[list[float]], optional
`c` parameters used in calculating henry's law constant, [-]
henry_ds : list[list[float]], optional
`d` paraemeters used in calculating henry's law constant, [1/K]
henry_es : list[list[float]], optional
`e` parameters used in calculating henry's law constant, [K^2]
henry_fs : list[list[float]], optional
`f` parameters used in calculating henry's law constant, [1/K^2]
henry_mode : str
The setting for henry's law. 'solvents' to consider all
components set not to be henry's law components a solvent (if
any parameters are missing this will not make sense at all);
'solvents_with_parameters' to consider only the solvents with
parameters (vapor pressures will be used if a component has
no solvents whatsoever)
use_Hvap_caloric : bool, optional
If True, enthalpy and entropy will be calculated using ideal-gas
heat capacity and the heat of vaporization of the fluid only. This
forces enthalpy to be pressure-independent. This supersedes other
options which would otherwise impact these properties. The molar volume
of the fluid has no impact on enthalpy or entropy if this option is
True. This option is not thermodynamically consistent, but is still
often an assumption that is made.
'''
PIP_INCALCULABLE_VALUE = 2
force_phase = 'l'
phase = 'l'
is_gas = False
is_liquid = True
P_DEPENDENT_H_LIQ = True
PHI_SAT_IDEAL_TR = 0.1
_Psats_data = None
_Vms_sat_data = None
_Hvap_data = None
use_IG_Cp = True # Deprecated! Remove with S_old and H_old
ideal_gas_basis = True
supercritical_volumes = False
Cpls_poly_fit = False
_Cpls_data = None
_Tait_B_data = None
_Tait_C_data = None
pure_references = ('HeatCapacityGases', 'VolumeLiquids', 'VaporPressures', 'HeatCapacityLiquids',
'EnthalpyVaporizations')
pure_reference_types = (HeatCapacityGas, VolumeLiquid, VaporPressure, HeatCapacityLiquid,
EnthalpyVaporization)
model_attributes = ('Hfs', 'Gfs', 'Sfs', 'GibbsExcessModel',
'eos_pure_instances', 'use_Poynting', 'use_phis_sat',
'use_Tait', 'use_eos_volume', 'henry_components',
'henry_as', 'henry_bs', 'henry_cs', 'henry_ds', 'henry_es', 'henry_fs','henry_mode',
'Psat_extrpolation') + pure_references
obj_references = ('GibbsExcessModel', 'eos_pure_instances', 'result', 'constants', 'correlations',
'HeatCapacityGases', 'VolumeLiquids', 'VaporPressures', 'HeatCapacityLiquids',
'EnthalpyVaporizations')
# Not sure how this will progress
__slots__ = ('VaporPressures', 'Psats_poly_fit', 'Psat_extrpolation', 'N', 'HeatCapacityGases', 'Cpgs_poly_fit',
'_Cpgs_data', 'HeatCapacityLiquids', 'use_eos_volume', 'VolumeLiquids', 'Vms_sat_poly_fit', 'VolumeSupercriticalLiquids',
'Vms_supercritical_poly_fit', 'incompressible', 'use_Tait', 'EnthalpyVaporizations', 'Hvap_poly_fit', 'GibbsExcessModel',
'eos_pure_instances', 'equilibrium_basis', 'caloric_basis', 'use_phis_sat', 'use_Poynting', 'use_phis_sat_caloric',
'use_Poynting_caloric', 'use_Hvap_caloric', 'has_henry_components', 'henry_components',
'henry_as', 'henry_bs', 'henry_cs', 'henry_ds', 'henry_es', 'henry_fs','henry_mode',
'composition_independent',
'Hfs', 'Gfs', 'Sfs', 'model_id', 'T', 'P', 'zs', '_model_hash_ignore_phase', '_model_hash')
def __repr__(self):
r'''Method to create a string representation of the phase object, with
the goal of making it easy to obtain standalone code which reproduces
the current state of the phase. This is extremely helpful in creating
new test cases.
Returns
-------
recreation : str
String which is valid Python and recreates the current state of
the object if ran, [-]
'''
pure_strs = ''
for k in self.pure_references:
v = getattr(self, k)
if v is not None:
try:
s = '[' + ', '.join(str(o) for o in v) + ']'
except:
continue
pure_strs += f'{k}={s}, '
base = f"""{self.__class__.__name__}(GibbsExcessModel={self.GibbsExcessModel},
equilibrium_basis={self.equilibrium_basis}, caloric_basis={self.caloric_basis},
eos_pure_instances={self.eos_pure_instances}, {pure_strs}"""
for s in ('Hfs', 'Gfs', 'Sfs', 'T', 'P', 'zs'):
if hasattr(self, s) and getattr(self, s) is not None:
base += f'{s}={getattr(self, s)}, '
if base[-2:] == ', ':
base = base[:-2]
base += ')'
return base
def __init__(self, VaporPressures, VolumeLiquids=None,
HeatCapacityGases=None,
GibbsExcessModel=None,
eos_pure_instances=None,
EnthalpyVaporizations=None,
HeatCapacityLiquids=None,
VolumeSupercriticalLiquids=None,
use_Hvap_caloric=False,
use_Poynting=False,
use_phis_sat=False,
use_Tait=False,
use_eos_volume=False,
Hfs=None, Gfs=None, Sfs=None,
henry_components=None,
henry_abcdef=None,
henry_as=None, henry_bs=None,
henry_cs=None, henry_ds=None,
henry_es=None, henry_fs=None,
henry_mode='solvents_with_parameters',
T=Phase.T_DEFAULT, P=Phase.P_DEFAULT, zs=None,
Psat_extrpolation='AB',
equilibrium_basis=None,
caloric_basis=None,
):
'''It is quite possible to introduce a PVT relation ship for liquid
density and remain thermodynamically consistent. However, must be
applied on a per-component basis! This class cannot have an
equation-of-state or VolumeLiquidMixture for a liquid MIXTURE!
(it might still be nice to generalize the handling; maybe even allow)
pure EOSs to be used too, and as a form/template for which functions to
use).
In conclusion, you have
1) The standard H/S model
2) The H/S model with all pressure correction happening at P
3) The inconsistent model which has no pressure dependence whatsover in H/S
This model is required due to its popularity, not its consistency (but still volume dependency)
All mixture volumetric properties have to be averages of the pure
components properties and derivatives. A Multiphase will be needed to
allow flashes with different properties from different phases.
'''
self.N = N = len(VaporPressures)
if zs is None:
zs = [1.0/N]*N
if henry_components is None:
henry_components = [False]*self.N
self.has_henry_components = any(henry_components)
self.henry_components = henry_components
self.VaporPressures = VaporPressures
self.Psats_poly_fit = (all(i.method == POLY_FIT for i in VaporPressures) and not self.has_henry_components) if VaporPressures is not None else False
self.Psat_extrpolation = Psat_extrpolation
if self.Psats_poly_fit:
Psats_data = [[i.poly_fit_Tmin for i in VaporPressures],
[i.poly_fit_Tmin_slope for i in VaporPressures],
[i.poly_fit_Tmin_value for i in VaporPressures],
[i.poly_fit_Tmax for i in VaporPressures],
[i.poly_fit_Tmax_slope for i in VaporPressures],
[i.poly_fit_Tmax_value for i in VaporPressures],
[i.poly_fit_coeffs for i in VaporPressures],
[i.poly_fit_d_coeffs for i in VaporPressures],
[i.poly_fit_d2_coeffs for i in VaporPressures],
[i.DIPPR101_ABC for i in VaporPressures]]
if Psat_extrpolation == 'AB':
Psats_data.append([i.poly_fit_AB_high_ABC_compat + [0.0] for i in VaporPressures])
elif Psat_extrpolation == 'ABC':
Psats_data.append([i.DIPPR101_ABC_high for i in VaporPressures])
# Other option: raise?
self._Psats_data = Psats_data
if self.vectorized:
zero_coeffs = zeros((N, N))
else:
zero_coeffs = [[0.0]*N for _ in range(N)]
self.HeatCapacityGases = HeatCapacityGases
self.Cpgs_poly_fit, self._Cpgs_data = self._setup_Cpigs(HeatCapacityGases)
self.HeatCapacityLiquids = HeatCapacityLiquids
if HeatCapacityLiquids is not None:
self.Cpls_poly_fit, self._Cpls_data = self._setup_Cpigs(HeatCapacityLiquids)
T_REF_IG = self.T_REF_IG
T_REF_IG_INV = 1.0/T_REF_IG
self.Hvaps_T_ref = [obj(T_REF_IG) for obj in EnthalpyVaporizations]
self.dSvaps_T_ref = [T_REF_IG_INV*dH for dH in self.Hvaps_T_ref]
self.use_eos_volume = use_eos_volume
self.VolumeLiquids = VolumeLiquids
self.Vms_sat_poly_fit = ((not use_eos_volume and all(i.method == POLY_FIT for i in VolumeLiquids)) if VolumeLiquids is not None else False)
if self.Vms_sat_poly_fit:
self._Vms_sat_data = [[i.poly_fit_Tmin for i in VolumeLiquids],
[i.poly_fit_Tmin_slope for i in VolumeLiquids],
[i.poly_fit_Tmin_value for i in VolumeLiquids],
[i.poly_fit_Tmax for i in VolumeLiquids],
[i.poly_fit_Tmax_slope for i in VolumeLiquids],
[i.poly_fit_Tmax_value for i in VolumeLiquids],
[i.poly_fit_coeffs for i in VolumeLiquids],
[i.poly_fit_d_coeffs for i in VolumeLiquids],
[i.poly_fit_d2_coeffs for i in VolumeLiquids],
[i.poly_fit_Tmin_quadratic for i in VolumeLiquids],
]
# low_fits = self._Vms_sat_data[9]
# for i in range(self.N):
# low_fits[i][0] = max(0, low_fits[i][0])
self.VolumeSupercriticalLiquids = VolumeSupercriticalLiquids
self.Vms_supercritical_poly_fit = all(i.method == POLY_FIT for i in VolumeSupercriticalLiquids) if VolumeSupercriticalLiquids is not None else False
if self.Vms_supercritical_poly_fit:
self.Vms_supercritical_data = [[i.poly_fit_Tmin for i in VolumeSupercriticalLiquids],
[i.poly_fit_Tmin_slope for i in VolumeSupercriticalLiquids],
[i.poly_fit_Tmin_value for i in VolumeSupercriticalLiquids],
[i.poly_fit_Tmax for i in VolumeSupercriticalLiquids],
[i.poly_fit_Tmax_slope for i in VolumeSupercriticalLiquids],
[i.poly_fit_Tmax_value for i in VolumeSupercriticalLiquids],
[i.poly_fit_coeffs for i in VolumeSupercriticalLiquids],
[i.poly_fit_d_coeffs for i in VolumeSupercriticalLiquids],
[i.poly_fit_d2_coeffs for i in VolumeSupercriticalLiquids],
[i.poly_fit_Tmin_quadratic for i in VolumeSupercriticalLiquids],
]
self.incompressible = not use_Tait
self.use_Tait = use_Tait
if self.use_Tait:
Tait_B_data, Tait_C_data = [[] for i in range(9)], [[] for i in range(9)]
for v in VolumeLiquids:
for (d, store) in zip(v.Tait_data(), [Tait_B_data, Tait_C_data]):
for i in range(len(d)):
store[i].append(d[i])
self._Tait_B_data = Tait_B_data
self._Tait_C_data = Tait_C_data
self.EnthalpyVaporizations = EnthalpyVaporizations
self.Hvap_poly_fit = all(i.method == POLY_FIT for i in EnthalpyVaporizations) if EnthalpyVaporizations is not None else False
if self.Hvap_poly_fit:
self._Hvap_data = [[i.poly_fit_Tmin for i in EnthalpyVaporizations],
[i.poly_fit_Tmax for i in EnthalpyVaporizations],
[i.poly_fit_Tc for i in EnthalpyVaporizations],
[1.0/i.poly_fit_Tc for i in EnthalpyVaporizations],
[i.poly_fit_coeffs for i in EnthalpyVaporizations]]
if GibbsExcessModel is None:
GibbsExcessModel = IdealSolution(T=T, xs=zs)
self.GibbsExcessModel = GibbsExcessModel
self.eos_pure_instances = eos_pure_instances
self.equilibrium_basis = equilibrium_basis
self.caloric_basis = caloric_basis
if equilibrium_basis is not None:
if equilibrium_basis == 'Poynting':
self.use_Poynting = True
self.use_phis_sat = False
elif equilibrium_basis == 'Poynting&PhiSat':
self.use_Poynting = True
self.use_phis_sat = True
elif equilibrium_basis == 'PhiSat':
self.use_phis_sat = True
self.use_Poynting = False
elif equilibrium_basis == 'Psat':
self.use_phis_sat = False
self.use_Poynting = False
else:
self.use_Poynting = use_Poynting
self.use_phis_sat = use_phis_sat
if caloric_basis is not None:
if caloric_basis == 'Poynting':
self.use_Poynting_caloric = True
self.use_phis_sat_caloric = False
self.use_Hvap_caloric = False
elif caloric_basis == 'Poynting&PhiSat':
self.use_Poynting_caloric = True
self.use_phis_sat_caloric = True
self.use_Hvap_caloric = False
elif caloric_basis == 'PhiSat':
self.use_phis_sat_caloric = True
self.use_Poynting_caloric = False
self.use_Hvap_caloric = False
elif caloric_basis == 'Psat':
self.use_phis_sat_caloric = False
self.use_Poynting_caloric = False
self.use_Hvap_caloric = False
elif caloric_basis == 'Hvap':
self.use_phis_sat_caloric = False
self.use_Poynting_caloric = False
self.use_Hvap_caloric = True
else:
self.use_Poynting_caloric = use_Poynting
self.use_phis_sat_caloric = use_phis_sat
self.use_Hvap_caloric = use_Hvap_caloric
if henry_mode not in ('solvents', 'solvents_with_parameters'):
raise ValueError("Henry's law model setting not recognized")
self.henry_mode = henry_mode
multiple_henry_inputs = (henry_as, henry_bs, henry_cs, henry_ds, henry_es, henry_fs)
input_count_henry = (henry_abcdef is not None) + (any(i is not None for i in multiple_henry_inputs))
if input_count_henry > 1:
raise ValueError("Input only one of henry_abcdef, or (henry_as...henry_fs)")
if henry_abcdef is not None:
if self.vectorized:
self.henry_as = array(henry_abcdef[:,:,0], order='C', copy=True)
self.henry_bs = array(henry_abcdef[:,:,1], order='C', copy=True)
self.henry_cs = array(henry_abcdef[:,:,2], order='C', copy=True)
self.henry_ds = array(henry_abcdef[:,:,3], order='C', copy=True)
self.henry_es = array(henry_abcdef[:,:,4], order='C', copy=True)
self.henry_fs = array(henry_abcdef[:,:,5], order='C', copy=True)
else:
self.henry_as = [[i[0] for i in l] for l in henry_abcdef]
self.henry_bs = [[i[1] for i in l] for l in henry_abcdef]
self.henry_cs = [[i[2] for i in l] for l in henry_abcdef]
self.henry_ds = [[i[3] for i in l] for l in henry_abcdef]
self.henry_es = [[i[4] for i in l] for l in henry_abcdef]
self.henry_fs = [[i[5] for i in l] for l in henry_abcdef]
else:
if henry_abcdef is None:
henry_abcdef = multiple_henry_inputs
henry_abcdef_len = 0 if henry_abcdef is None else len(henry_abcdef)
if not henry_abcdef_len or henry_abcdef[0] is None:
self.henry_as = zero_coeffs
else:
self.henry_as = henry_abcdef[0]
if not henry_abcdef_len or henry_abcdef[1] is None:
self.henry_bs = zero_coeffs
else:
self.henry_bs = henry_abcdef[1]
if not henry_abcdef_len or henry_abcdef[2] is None:
self.henry_cs = zero_coeffs
else:
self.henry_cs = henry_abcdef[2]
if not henry_abcdef_len or henry_abcdef[3] is None:
self.henry_ds = zero_coeffs
else:
self.henry_ds = henry_abcdef[3]
if not henry_abcdef_len or henry_abcdef[4] is None:
self.henry_es = zero_coeffs
else:
self.henry_es = henry_abcdef[4]
if not henry_abcdef_len or henry_abcdef[5] is None:
self.henry_fs = zero_coeffs
else:
self.henry_fs = henry_abcdef[5]
self.composition_independent = isinstance(GibbsExcessModel, IdealSolution) and not self.has_henry_components
self.Hfs = Hfs
self.Gfs = Gfs
self.Sfs = Sfs
self.model_id = 20000 + GibbsExcessModel.model_id
if T is not None and P is not None and zs is not None:
self.T = T
self.P = P
self.zs = zs
def to_TP_zs(self, T, P, zs):
T_equal = hasattr(self, 'T') and T == self.T
new = self.__class__.__new__(self.__class__)
new.T = T
new.P = P
new.zs = zs
new.N = self.N
self.transfer_data(new, zs, T, T_equal)
return new
def to(self, zs, T=None, P=None, V=None):
try:
T_equal = T == self.T
except:
T_equal = False
new = self.__class__.__new__(self.__class__)
new.zs = zs
new.N = self.N
if T is not None:
if P is not None:
new.T = T
new.P = P
elif V is not None:
def to_solve(P):
return self.to_TP_zs(T, P, zs).V() - V
P = secant(to_solve, 0.0002, xtol=1e-8, ytol=1e-10)
new.P = P
elif P is not None and V is not None:
def to_solve(T):
return self.to_TP_zs(T, P, zs).V() - V
T = secant(to_solve, 300, xtol=1e-9, ytol=1e-5)
new.T = T
else:
raise ValueError("Two of T, P, or V are needed")
self.transfer_data(new, zs, T, T_equal)
return new
def transfer_data(self, new, zs, T, T_equal):
new.VaporPressures = self.VaporPressures
new.VolumeLiquids = self.VolumeLiquids
new.eos_pure_instances = self.eos_pure_instances
new.HeatCapacityGases = self.HeatCapacityGases
new.EnthalpyVaporizations = self.EnthalpyVaporizations
new.HeatCapacityLiquids = self.HeatCapacityLiquids
new.Psats_poly_fit = self.Psats_poly_fit
new._Psats_data = self._Psats_data
new.Psat_extrpolation = self.Psat_extrpolation
new.Cpgs_poly_fit = self.Cpgs_poly_fit
new._Cpgs_data = self._Cpgs_data
new.Cpls_poly_fit = self.Cpls_poly_fit
new._Cpls_data = self._Cpls_data
new.Vms_sat_poly_fit = self.Vms_sat_poly_fit
new._Vms_sat_data = self._Vms_sat_data
new._Hvap_data = self._Hvap_data
new.Hvap_poly_fit = self.Hvap_poly_fit
new.incompressible = self.incompressible
new.equilibrium_basis = self.equilibrium_basis
new.caloric_basis = self.caloric_basis
new.use_phis_sat = self.use_phis_sat
new.use_Poynting = self.use_Poynting
new.P_DEPENDENT_H_LIQ = self.P_DEPENDENT_H_LIQ
new.use_eos_volume = self.use_eos_volume
new.use_Hvap_caloric = self.use_Hvap_caloric
new.Hfs = self.Hfs
new.Gfs = self.Gfs
new.Sfs = self.Sfs
new.henry_as = self.henry_as
new.henry_bs = self.henry_bs
new.henry_cs = self.henry_cs
new.henry_ds = self.henry_ds
new.henry_es = self.henry_es
new.henry_fs = self.henry_fs
new.henry_components = self.henry_components
new.has_henry_components = self.has_henry_components
new.henry_mode = self.henry_mode
new.composition_independent = self.composition_independent
new.model_id = self.model_id
new.use_Tait = self.use_Tait
new._Tait_B_data = self._Tait_B_data
new._Tait_C_data = self._Tait_C_data
if T_equal and (self.composition_independent or self.zs is zs):
# Allow the composition inconsistency as it is harmless
new.GibbsExcessModel = self.GibbsExcessModel
else:
new.GibbsExcessModel = self.GibbsExcessModel.to_T_xs(T=T, xs=zs)
try:
if T_equal:
if not self.has_henry_components:
try:
new._Psats = self._Psats
new._dPsats_dT = self._dPsats_dT
new._d2Psats_dT2 = self._d2Psats_dT2
except:
pass
try:
new._lnPsats = self._lnPsats
new._dPsats_dT_over_Psats = self._dPsats_dT_over_Psats
except:
pass
else:
try:
new._lnHenry_matrix = self._lnHenry_matrix
new._dlnHenry_matrix_dT = self._dlnHenry_matrix_dT
new._d2lnHenry_matrix_dT2 = self._d2lnHenry_matrix_dT2
except:
pass
try:
new._Vms_sat = self._Vms_sat
new._Vms_sat_dT = self._Vms_sat_dT
new._d2Vms_sat_dT2 = self._d2Vms_sat_dT2
except:
pass
try:
new._Cpigs = self._Cpigs
except:
pass
try:
new._Cpig_integrals_over_T_pure = self._Cpig_integrals_over_T_pure
except:
pass
try:
new._Cpig_integrals_pure = self._Cpig_integrals_pure
except:
pass
except:
pass
return new
supports_lnphis_args = False
def lnphis_args(self):
try:
return self._lnphis_args
except:
pass
lnPsats = self.lnPsats()
Poyntings = self.Poyntings()
phis_sat = self.phis_sat()
activity_args = self.GibbsExcessModel.gammas_args()
lnphis = zeros(self.N) if self.vectorized else [0.0]*self.N
self._lnphis_args = (self.model_id, self.T, self.P, self.N, lnPsats, Poyntings, phis_sat) + activity_args +(lnphis,)
return self._lnphis_args
[docs] def lnHenry_matrix(self):
r'''Method to calculate and return the matrix of log Henry's law constants
as required by the traditional mixing rule, [-].
.. math::
\ln \text{H}_{i,j} = A_{ij}+\frac{B_{ij}}{T}+C_{ij}\ln T + D_{ij}T
+ \frac{E_{ij}}{T^2} + F_{ij}{T^2}
Returns
-------
lnHenry_matrix : list[list[float]]
Henry's law interaction parameters, [log(Pa)]
Notes
-----
Solvent/solvent and gas/gas values are all 0.
'''
try:
return self._lnHenry_matrix
except:
pass
N = self.N
if self.vectorized:
lnHenry_matrix = zeros((N, N))
else:
lnHenry_matrix = [[0.0]*N for _ in range(N)]
lnHenry_matrix = ln_henries(self.T, N, self.henry_as, self.henry_bs, self.henry_cs, self.henry_ds, self.henry_es, self.henry_fs, lnHenry_matrix)
self._lnHenry_matrix = lnHenry_matrix
return lnHenry_matrix
[docs] def dlnHenry_matrix_dT(self):
r'''Method to calculate and return the first temperature derivative
of the matrix of log Henry's law constants
as required by the traditional mixing rule, [-].
Returns
-------
dlnHenry_matrix_dT : list[list[float]]
First temperature derivative of Henry's law interaction
parameters, [log(Pa)/K]
Notes
-----
'''
try:
return self._dlnHenry_matrix_dT
except:
pass
N = self.N
if self.vectorized:
dlnHenry_matrix_dT = zeros((N, N))
else:
dlnHenry_matrix_dT = [[0.0]*N for _ in range(N)]
dlnHenry_matrix_dT = dln_henries_dT(self.T, N, self.henry_bs, self.henry_cs, self.henry_ds, self.henry_es, self.henry_fs, dlnHenry_matrix_dT)
self._dlnHenry_matrix_dT = dlnHenry_matrix_dT
return dlnHenry_matrix_dT
[docs] def d2lnHenry_matrix_dT2(self):
r'''Method to calculate and return the second temperature derivative
of the matrix of log Henry's law constants
as required by the traditional mixing rule, [-].
Returns
-------
d2lnHenry_matrix_dT2 : list[list[float]]
Second temperature derivative of Henry's law interaction
parameters, [log(Pa)/K]
Notes
-----
'''
try:
return self._d2lnHenry_matrix_dT2
except:
pass
N = self.N
if self.vectorized:
d2lnHenry_matrix_dT2 = zeros((N, N))
else:
d2lnHenry_matrix_dT2 = [[0.0]*N for _ in range(N)]
d2lnHenry_matrix_dT2 = d2ln_henries_dT2(self.T, N, self.henry_bs, self.henry_cs, self.henry_es, self.henry_fs, d2lnHenry_matrix_dT2)
self._d2lnHenry_matrix_dT2 = d2lnHenry_matrix_dT2
return d2lnHenry_matrix_dT2
def Henry_constants(self):
zs, vectorized, N, henry_components, henry_mode = self.zs, self.vectorized, self.N, self.henry_components, self.henry_mode
solvents_with_parameters = henry_mode == 'solvents_with_parameters'
lnHenry_matrix = self.lnHenry_matrix()
Hs = zeros(N) if vectorized else [0.0]*N
Henry_constants(lnHenry_matrix, zs, henry_components, solvents_with_parameters, Hs)
return Hs
dHenry_constants_dT
def dHenry_constants_dT(self):
zs, vectorized, N, henry_components, henry_mode = self.zs, self.vectorized, self.N, self.henry_components, self.henry_mode
solvents_with_parameters = henry_mode == 'solvents_with_parameters'
lnHenry_matrix = self.lnHenry_matrix()
dlnHenry_matrix_dT = self.dlnHenry_matrix_dT()
dHs = zeros(N) if vectorized else [0.0]*N
dHenry_constants_dT(lnHenry_matrix, dlnHenry_matrix_dT, zs, henry_components, solvents_with_parameters, dHs)
return dHs
def d2Henry_constants_dT2(self):
zs, vectorized, N, henry_components, henry_mode = self.zs, self.vectorized, self.N, self.henry_components, self.henry_mode
solvents_with_parameters = henry_mode == 'solvents_with_parameters'
lnHenry_matrix = self.lnHenry_matrix()
dlnHenry_matrix_dT = self.dlnHenry_matrix_dT()
d2lnHenry_matrix_dT2 = self.d2lnHenry_matrix_dT2()
d2Hs = zeros(N) if vectorized else [0.0]*N
d2Henry_constants_dT2(lnHenry_matrix, dlnHenry_matrix_dT, d2lnHenry_matrix_dT2, zs, henry_components, solvents_with_parameters, d2Hs)
return d2Hs
def Psats_T_ref(self):
try:
return self._Psats_T_ref
except AttributeError:
pass
VaporPressures, N = self.VaporPressures, self.N
T_REF_IG = self.T_REF_IG
if self.has_henry_components:
self._Psats_T_ref = self.to(T=T_REF_IG, P=self.P, zs=self.zs).Psats()
else:
self._Psats_T_ref = [VaporPressures[i](T_REF_IG) for i in range(N)]
return self._Psats_T_ref
def Psats_at(self, T):
if self.Psats_poly_fit:
return self._Psats_at_poly_fit(T, self._Psats_data, range(self.N))
if self.has_henry_components:
return self.to(T=T, P=self.P, zs=self.zs).Psats()
VaporPressures = self.VaporPressures
return [VaporPressures[i](T) for i in range(self.N)]
@staticmethod
def _Psats_at_poly_fit(T, Psats_data, cmps):
Psats = []
T_inv = 1.0/T
logT = log(T)
Tmins, Tmaxes, coeffs = Psats_data[0], Psats_data[3], Psats_data[6]
for i in cmps:
if T < Tmins[i]:
A, B, C = Psats_data[9][i]
Psat = (A + B*T_inv + C*logT)
# A, B = _Psats_data[9][i]
# Psat = (A - B*T_inv)
# Psat = (T - Tmins[i])*_Psats_data[1][i] + _Psats_data[2][i]
elif T > Tmaxes[i]:
A, B, C = Psats_data[10][i]
Psat = (A + B*T_inv + C*logT)
# A, B = _Psats_data[10][i]
# Psat = (A - B*T_inv)
# Psat = (T - Tmaxes[i])*_Psats_data[4][i] + _Psats_data[5][i]
else:
Psat = 0.0
for c in coeffs[i]:
Psat = Psat*T + c
try:
Psats.append(exp(Psat))
except:
Psats.append(1.6549840276802644e+300)
return Psats
def Psats(self):
try:
return self._Psats
except AttributeError:
pass
N = self.N
T, cmps = self.T, range(N)
if self.Psats_poly_fit:
self._Psats = Psats = self._Psats_at_poly_fit(T, self._Psats_data, cmps)
# _Psats_data = self._Psats_data
# Tmins, Tmaxes, coeffs = _Psats_data[0], _Psats_data[3], _Psats_data[6]
# for i in cmps:
# if T < Tmins[i]:
# A, B, C = _Psats_data[9][i]
# Psat = (A + B*T_inv + C*logT)
## A, B = _Psats_data[9][i]
## Psat = (A - B*T_inv)
## Psat = (T - Tmins[i])*_Psats_data[1][i] + _Psats_data[2][i]
# elif T > Tmaxes[i]:
# Psat = (T - Tmaxes[i])*_Psats_data[4][i] + _Psats_data[5][i]
# else:
# Psat = 0.0
# for c in coeffs[i]:
# Psat = Psat*T + c
# Psats.append(exp(Psat))
else:
self._Psats = Psats = []
for i in self.VaporPressures:
Psats.append(i.T_dependent_property(T))
if self.has_henry_components:
Hs, henry_components = self.Henry_constants(), self.henry_components
for i in range(N):
if henry_components[i]:
Psats[i] = Hs[i]
return Psats
def PIP(self):
dP_dT = self.dP_dT()
if dP_dT == 0:
return self.PIP_INCALCULABLE_VALUE
return phase_identification_parameter(self.V(), self.dP_dT(), self.dP_dV(),
self.d2P_dV2(), self.d2P_dTdV())
@staticmethod
def _dPsats_dT_at_poly_fit(T, Psats_data, cmps, Psats):
T_inv = 1.0/T
Tinv2 = T_inv*T_inv
dPsats_dT = []
Tmins, Tmaxes, dcoeffs, coeffs_low, coeffs_high = Psats_data[0], Psats_data[3], Psats_data[7], Psats_data[9], Psats_data[10]
for i in cmps:
if T < Tmins[i]:
# A, B = _Psats_data[9][i]
# dPsat_dT = B*Tinv2*Psats[i]
dPsat_dT = Psats[i]*(-coeffs_low[i][1]*Tinv2 + coeffs_low[i][2]*T_inv)
# dPsat_dT = _Psats_data[1][i]*Psats[i]#*exp((T - Tmins[i])*_Psats_data[1][i]
# + _Psats_data[2][i])
elif T > Tmaxes[i]:
dPsat_dT = Psats[i]*(-coeffs_high[i][1]*Tinv2 + coeffs_high[i][2]*T_inv)
# dPsat_dT = _Psats_data[4][i]*Psats[i]#*exp((T - Tmaxes[i])
# #*_Psats_data[4][i]
# #+ _Psats_data[5][i])
else:
dPsat_dT = 0.0
for c in dcoeffs[i]:
dPsat_dT = dPsat_dT*T + c
# v, der = horner_and_der(coeffs[i], T)
dPsat_dT *= Psats[i]
dPsats_dT.append(dPsat_dT)
return dPsats_dT
def dPsats_dT_at(self, T, Psats=None):
if Psats is None:
Psats = self.Psats_at(T)
if self.Psats_poly_fit:
return self._dPsats_dT_at_poly_fit(T, self._Psats_data, range(self.N), Psats)
if self.has_henry_components:
return self.to(T=T, P=self.P, zs=self.zs).dPsats_dT()
return [VaporPressure.T_dependent_property_derivative(T=T)
for VaporPressure in self.VaporPressures]
def dPsats_dT(self):
try:
return self._dPsats_dTT_dependent_property_derivative
except:
pass
T, N = self.T, self.N
# Need to reset the method because for the T bounded solver,
# will normally get a different than prefered method as it starts
# at the boundaries
if self.Psats_poly_fit:
try:
Psats = self._Psats
except AttributeError:
Psats = self.Psats()
self._dPsats_dT = dPsats_dT = self._dPsats_dT_at_poly_fit(T, self._Psats_data, range(N), Psats)
return dPsats_dT
self._dPsats_dT = dPsats_dT = [VaporPressure.T_dependent_property_derivative(T=T)
for VaporPressure in self.VaporPressures]
if self.has_henry_components:
dHs, henry_components = self.dHenry_constants_dT(), self.henry_components
for i in range(N):
if henry_components[i]:
dPsats_dT[i] = dHs[i]
return dPsats_dT
def d2Psats_dT2(self):
try:
return self._d2Psats_dT2
except:
pass
try:
Psats = self._Psats
except AttributeError:
Psats = self.Psats()
try:
dPsats_dT = self._dPsats_dT
except AttributeError:
dPsats_dT = self.dPsats_dT()
T, N = self.T, self.N
T_inv = 1.0/T
T_inv2 = T_inv*T_inv
# Tinv3 = T_inv*T_inv*T_inv
self._d2Psats_dT2 = d2Psats_dT2 = []
if self.Psats_poly_fit:
Psats_data = self._Psats_data
Tmins, Tmaxes, d2coeffs = Psats_data[0], Psats_data[3], Psats_data[8]
for i in range(N):
if T < Tmins[i]:
# A, B = _Psats_data[9][i]
# d2Psat_dT2 = B*Psats[i]*(B*T_inv - 2.0)*Tinv3
A, B, C = Psats_data[9][i]
x0 = (B*T_inv - C)
d2Psat_dT2 = Psats[i]*(2.0*B*T_inv - C + x0*x0)*T_inv2
# d2Psat_dT2 = _Psats_data[1][i]*dPsats_dT[i]
elif T > Tmaxes[i]:
A, B, C = Psats_data[10][i]
x0 = (B*T_inv - C)
d2Psat_dT2 = Psats[i]*(2.0*B*T_inv - C + x0*x0)*T_inv2
# d2Psat_dT2 = _Psats_data[4][i]*dPsats_dT[i]
else:
d2Psat_dT2 = 0.0
for c in d2coeffs[i]:
d2Psat_dT2 = d2Psat_dT2*T + c
d2Psat_dT2 = (dPsats_dT[i]*dPsats_dT[i]/Psats[i] + Psats[i]*d2Psat_dT2)
d2Psats_dT2.append(d2Psat_dT2)
return d2Psats_dT2
self._d2Psats_dT2 = d2Psats_dT2 = [VaporPressure.T_dependent_property_derivative(T=T, order=2)
for VaporPressure in self.VaporPressures]
if self.has_henry_components:
d2Hs, henry_components = self.d2Henry_constants_dT2(), self.henry_components
for i in range(N):
if henry_components[i]:
d2Psats_dT2[i] = d2Hs[i]
return d2Psats_dT2
def lnPsats(self):
try:
return self._lnPsats
except AttributeError:
pass
T, N = self.T, self.N
T_inv = 1.0/T
logT = log(T)
lnPsats = []
if self.Psats_poly_fit:
Psats_data = self._Psats_data
Tmins, Tmaxes, coeffs = Psats_data[0], Psats_data[3], Psats_data[6]
for i in range(N):
if T < Tmins[i]:
A, B, C = Psats_data[9][i]
Psat = (A + B*T_inv + C*logT)
elif T > Tmaxes[i]:
A, B, C = Psats_data[10][i]
Psat = (A + B*T_inv + C*logT)
# Psat = (T - Tmaxes[i])*_Psats_data[4][i] + _Psats_data[5][i]
else:
Psat = 0.0
for c in coeffs[i]:
Psat = Psat*T + c
lnPsats.append(Psat)
self._lnPsats = lnPsats
return lnPsats
self._lnPsats = lnPsats = [VaporPressure.T_dependent_property_transform(T, TRANSFORM_LOG)
for VaporPressure in self.VaporPressures]
if self.has_henry_components:
Hs, henry_components = self.Henry_constants(), self.henry_components
for i in range(N):
if henry_components[i]:
lnPsats[i] = log(Hs[i])
return lnPsats
def dlnPsats_dT(self):
T, N = self.T, self.N
T_inv = 1.0/T
Tinv2 = T_inv*T_inv
if self.Psats_poly_fit:
Psats_data = self._Psats_data
Tmins, Tmaxes, dcoeffs = Psats_data[0], Psats_data[3], Psats_data[7]
dlnPsats_dT = []
for i in range(N):
if T < Tmins[i]:
A, B, C = Psats_data[9][i]
dPsat_dT = (-B*Tinv2 + C*T_inv)
elif T > Tmaxes[i]:
A, B, C = Psats_data[10][i]
dPsat_dT = (-B*Tinv2 + C*T_inv)
# dPsat_dT = _Psats_data[4][i]
else:
dPsat_dT = 0.0
for c in dcoeffs[i]:
dPsat_dT = dPsat_dT*T + c
dlnPsats_dT.append(dPsat_dT)
return dlnPsats_dT
dlnPsats_dT = [VaporPressure.T_dependent_property_transform(T, TRANSFORM_LOG_DERIVATIVE) for VaporPressure in self.VaporPressures]
if self.has_henry_components:
Hs, dHs, henry_components = self.Henry_constants(), self.dHenry_constants_dT(), self.henry_components
for i in range(N):
if henry_components[i]:
dlnPsats_dT[i] = dHs[i]/Hs[i]
return dlnPsats_dT
def d2lnPsats_dT2(self):
T, N = self.T, self.N
T_inv = 1.0/T
T_inv2 = T_inv*T_inv
# Tinv3 = T_inv*T_inv*T_inv
if self.Psats_poly_fit:
Psats_data = self._Psats_data
Tmins, Tmaxes, d2coeffs = Psats_data[0], Psats_data[3], Psats_data[8]
d2lnPsats_dT2 = []
for i in range(N):
if T < Tmins[i]:
A, B, C = Psats_data[9][i]
d2lnPsat_dT2 = (2.0*B*T_inv - C)*T_inv2
elif T > Tmaxes[i]:
A, B, C = Psats_data[10][i]
d2lnPsat_dT2 = (2.0*B*T_inv - C)*T_inv2
# d2lnPsat_dT2 = 0.0
else:
d2lnPsat_dT2 = 0.0
for c in d2coeffs[i]:
d2lnPsat_dT2 = d2lnPsat_dT2*T + c
d2lnPsats_dT2.append(d2lnPsat_dT2)
return d2lnPsats_dT2
d2lnPsats_dT2 = [VaporPressure.T_dependent_property_transform(T, TRANSFORM_SECOND_LOG_DERIVATIVE) for VaporPressure in self.VaporPressures]
if self.has_henry_components:
Hs, dHs, d2Hs, henry_components = self.Henry_constants(), self.dHenry_constants_dT(), self.d2Henry_constants_dT2(), self.henry_components
for i in range(N):
if henry_components[i]:
d2lnPsats_dT2[i] = (d2Hs[i] - dHs[i]*dHs[i]/Hs[i])/Hs[i]
return d2lnPsats_dT2
def dPsats_dT_over_Psats(self):
try:
return self._dPsats_dT_over_Psats
except AttributeError:
pass
T, N = self.T, self.N
T_inv = 1.0/T
Tinv2 = T_inv*T_inv
if self.Psats_poly_fit:
dPsat_dT_over_Psats = []
Psats_data = self._Psats_data
Tmins, Tmaxes, dcoeffs, low_coeffs, high_coeffs = Psats_data[0], Psats_data[3], Psats_data[7], Psats_data[9], Psats_data[10]
for i in range(N):
if T < Tmins[i]:
dPsat_dT_over_Psat = (-low_coeffs[i][1]*Tinv2 + low_coeffs[i][2]*T_inv)
elif T > Tmaxes[i]:
dPsat_dT_over_Psat = (-high_coeffs[i][1]*Tinv2 + high_coeffs[i][2]*T_inv)
# dPsat_dT_over_Psat = _Psats_data[4][i]
else:
dPsat_dT_over_Psat = 0.0
for c in dcoeffs[i]:
dPsat_dT_over_Psat = dPsat_dT_over_Psat*T + c
dPsat_dT_over_Psats.append(dPsat_dT_over_Psat)
self._dPsats_dT_over_Psats = dPsat_dT_over_Psats
return dPsat_dT_over_Psats
# dPsat_dT_over_Psats = [i/j for i, j in zip(self.dPsats_dT(), self.Psats())]
dPsat_dT_over_Psats = [VaporPressure.T_dependent_property_transform(T, TRANSFORM_DERIVATIVE_RATIO) for VaporPressure in self.VaporPressures]
if self.has_henry_components:
Hs, dHenry_constants_dT, henry_components = self.Henry_constants(), self.dHenry_constants_dT(), self.henry_components
for i in range(N):
if henry_components[i]:
dPsat_dT_over_Psats[i] = dHenry_constants_dT[i]/Hs[i]
self._dPsats_dT_over_Psats = dPsat_dT_over_Psats
return dPsat_dT_over_Psats
def d2Psats_dT2_over_Psats(self):
try:
return self._d2Psats_dT2_over_Psats
except AttributeError:
pass
T, N = self.T, self.N
T_inv = 1.0/T
Tinv2 = T_inv*T_inv
Tinv4 = Tinv2*Tinv2
c0 = (T + T)*Tinv4
if self.Psats_poly_fit:
d2Psat_dT2_over_Psats = []
Psats_data = self._Psats_data
Tmins, Tmaxes, dcoeffs, low_coeffs, high_coeffs = Psats_data[0], Psats_data[3], Psats_data[7], Psats_data[9], Psats_data[10]
for i in range(N):
if T < Tmins[i]:
B, C = low_coeffs[i][1], low_coeffs[i][2]
x0 = (B - C*T)
d2Psat_dT2_over_Psat = c0*B - C*Tinv2 + x0*x0*Tinv4
# d2Psat_dT2_over_Psat = (2*B*T - C*T**2 + (B - C*T)**2)/T**4
elif T > Tmaxes[i]:
B, C = high_coeffs[i][1], high_coeffs[i][2]
x0 = (B - C*T)
d2Psat_dT2_over_Psat = c0*B - C*Tinv2 + x0*x0*Tinv4
else:
dPsat_dT = 0.0
d2Psat_dT2 = 0.0
for a in dcoeffs[i]:
d2Psat_dT2 = T*d2Psat_dT2 + dPsat_dT
dPsat_dT = T*dPsat_dT + a
d2Psat_dT2_over_Psat = dPsat_dT*dPsat_dT + d2Psat_dT2
d2Psat_dT2_over_Psats.append(d2Psat_dT2_over_Psat)
self._d2Psats_dT2_over_Psats = d2Psat_dT2_over_Psats
return d2Psat_dT2_over_Psats
# d2Psat_dT2_over_Psats = [i/j for i, j in zip(self.d2Psats_dT2(), self.Psats())]
d2Psat_dT2_over_Psats = [VaporPressure.T_dependent_property_transform(T, TRANSFORM_SECOND_DERIVATIVE_RATIO) for VaporPressure in self.VaporPressures]
if self.has_henry_components:
Hs, d2Henry_constants_dT2, henry_components = self.Henry_constants(), self.d2Henry_constants_dT2(), self.henry_components
for i in range(N):
if henry_components[i]:
d2Psat_dT2_over_Psats[i] = d2Henry_constants_dT2[i]/Hs[i]
self._d2Psats_dT2_over_Psats = d2Psat_dT2_over_Psats
return d2Psat_dT2_over_Psats
@staticmethod
def _Vms_sat_at(T, Vms_sat_data, cmps):
Tmins, Tmaxes, coeffs, coeffs_Tmin = Vms_sat_data[0], Vms_sat_data[3], Vms_sat_data[6], Vms_sat_data[9]
Vms_sat = []
for i in cmps:
if T < Tmins[i]:
Vm = 0.0
for c in coeffs_Tmin[i]:
Vm = Vm*T + c
# Vm = (T - Tmins[i])*Vms_sat_data[1][i] + Vms_sat_data[2][i]
elif T > Tmaxes[i]:
Vm = (T - Tmaxes[i])*Vms_sat_data[4][i] + Vms_sat_data[5][i]
else:
Vm = 0.0
for c in coeffs[i]:
Vm = Vm*T + c
Vms_sat.append(Vm)
return Vms_sat
def Vms_sat_at(self, T):
if self.Vms_sat_poly_fit:
return self._Vms_sat_at(T, self._Vms_sat_data, range(self.N))
VolumeLiquids = self.VolumeLiquids
return [VolumeLiquids[i].T_dependent_property(T) for i in range(self.N)]
def Vms_sat(self):
try:
return self._Vms_sat
except AttributeError:
pass
T = self.T
if self.Vms_sat_poly_fit:
# self._Vms_sat = evaluate_linear_fits(self._Vms_sat_data, T)
# return self._Vms_sat
self._Vms_sat = Vms_sat = self._Vms_sat_at(T, self._Vms_sat_data, range(self.N))
return Vms_sat
elif self.use_eos_volume:
Vms = []
eoss = self.eos_pure_instances
Psats = self.Psats()
for i, e in enumerate(eoss):
if T < e.Tc:
Vms.append(e.V_l_sat(T))
else:
e = e.to(T=T, P=Psats[i])
try:
Vms.append(e.V_l)
except:
Vms.append(e.V_g)
self._Vms_sat = Vms
return Vms
VolumeLiquids = self.VolumeLiquids
# Psats = self.Psats()
# self._Vms_sat = [VolumeLiquids[i](T, Psats[i]) for i in range(self.N)]
self._Vms_sat = [VolumeLiquids[i].T_dependent_property(T) for i in range(self.N)]
return self._Vms_sat
@staticmethod
def _dVms_sat_dT_at(T, Vms_sat_data, cmps):
Vms_sat_dT = []
Tmins, Tmaxes, dcoeffs = Vms_sat_data[0], Vms_sat_data[3], Vms_sat_data[7]
for i in cmps:
if T < Tmins[i]:
dVm = horner_and_der2(Vms_sat_data[9][i], T)[1]
elif T > Tmaxes[i]:
dVm = Vms_sat_data[4][i]
else:
dVm = 0.0
for c in dcoeffs[i]:
dVm = dVm*T + c
Vms_sat_dT.append(dVm)
return Vms_sat_dT
def dVms_sat_dT_at(self, T):
if self.Vms_sat_poly_fit:
return self._dVms_sat_dT_at(T, self._Vms_sat_data, range(self.N))
return [obj.T_dependent_property_derivative(T=T) for obj in self.VolumeLiquids]
def dVms_sat_dT(self):
try:
return self._Vms_sat_dT
except:
pass
T = self.T
if self.Vms_sat_poly_fit:
# self._Vms_sat_dT = evaluate_linear_fits_d(self._Vms_sat_data, T)
self._Vms_sat_dT = self._dVms_sat_dT_at(T, self._Vms_sat_data, range(self.N))
return self._Vms_sat_dT
VolumeLiquids = self.VolumeLiquids
self._Vms_sat_dT = Vms_sat_dT = [obj.T_dependent_property_derivative(T=T) for obj in VolumeLiquids]
return Vms_sat_dT
def d2Vms_sat_dT2(self):
try:
return self._d2Vms_sat_dT2
except:
pass
T = self.T
if self.Vms_sat_poly_fit:
# self._d2Vms_sat_dT2 = evaluate_linear_fits_d2(self._Vms_sat_data, T)
# return self._d2Vms_sat_dT2
d2Vms_sat_dT2 = self._d2Vms_sat_dT2 = []
Vms_sat_data = self._Vms_sat_data
Tmins, Tmaxes, d2coeffs = Vms_sat_data[0], Vms_sat_data[3], Vms_sat_data[8]
for i in range(self.N):
d2Vm = 0.0
if Tmins[i] < T < Tmaxes[i]:
for c in d2coeffs[i]:
d2Vm = d2Vm*T + c
elif T < Tmins[i]:
d2Vm = horner_and_der2(Vms_sat_data[9][i], T)[2]
d2Vms_sat_dT2.append(d2Vm)
return d2Vms_sat_dT2
VolumeLiquids = self.VolumeLiquids
self._d2Vms_sat_dT2 = [obj.T_dependent_property_derivative(T=T, order=2) for obj in VolumeLiquids]
return self._d2Vms_sat_dT2
def Vms_sat_T_ref(self):
try:
return self._Vms_sat_T_ref
except AttributeError:
pass
T_REF_IG = self.T_REF_IG
if self.Vms_sat_poly_fit:
self._Vms_sat_T_ref = evaluate_linear_fits(self._Vms_sat_data, T_REF_IG)
else:
VolumeLiquids, cmps = self.VolumeLiquids, range(self.N)
self._Vms_sat_T_ref = [VolumeLiquids[i].T_dependent_property(T_REF_IG) for i in cmps]
return self._Vms_sat_T_ref
def dVms_sat_dT_T_ref(self):
try:
return self._dVms_sat_dT_T_ref
except AttributeError:
pass
T_REF_IG = self.T_REF_IG
if self.Vms_sat_poly_fit:
self._dVms_sat_dT_T_ref = evaluate_linear_fits_d(self._Vms_sat_data, self.T)
else:
VolumeLiquids, cmps = self.VolumeLiquids, range(self.N)
self._dVms_sat_dT_T_ref = [VolumeLiquids[i].T_dependent_property_derivative(T_REF_IG) for i in cmps]
return self._dVms_sat_dT_T_ref
def Vms(self):
# Fill in tait/eos function to be called instead of Vms_sat
return self.Vms_sat()
def dVms_dT(self):
return self.dVms_sat_dT()
def d2Vms_dT2(self):
return self.d2Vms_sat_dT2()
def dVms_dP(self):
return [0.0]*self.N
def d2Vms_dP2(self):
return [0.0]*self.N
def d2Vms_dPdT(self):
return [0.0]*self.N
def Hvaps(self):
try:
return self._Hvaps
except AttributeError:
pass
T, EnthalpyVaporizations, cmps = self.T, self.EnthalpyVaporizations, range(self.N)
self._Hvaps = Hvaps = []
if self.Hvap_poly_fit:
Hvap_data = self._Hvap_data
Tcs, Tcs_inv, coeffs = Hvap_data[2], Hvap_data[3], Hvap_data[4]
for i in cmps:
Hvap = 0.0
if T < Tcs[i]:
x = log(1.0 - T*Tcs_inv[i])
for c in coeffs[i]:
Hvap = Hvap*x + c
# Vm = horner(coeffs[i], log(1.0 - T*Tcs_inv[i])
Hvaps.append(Hvap)
return Hvaps
self._Hvaps = Hvaps = [EnthalpyVaporizations[i](T) for i in cmps]
for i in cmps:
if Hvaps[i] is None:
Hvaps[i] = 0.0
return Hvaps
def dHvaps_dT(self):
try:
return self._dHvaps_dT
except AttributeError:
pass
T, EnthalpyVaporizations, cmps = self.T, self.EnthalpyVaporizations, range(self.N)
self._dHvaps_dT = dHvaps_dT = []
if self.Hvap_poly_fit:
Hvap_data = self._Hvap_data
Tcs, Tcs_inv, coeffs = Hvap_data[2], Hvap_data[3], Hvap_data[4]
for i in cmps:
dHvap_dT = 0.0
if T < Tcs[i]:
p = log((Tcs[i] - T)*Tcs_inv[i])
x = 1.0
a = 1.0
for c in coeffs[i][-2::-1]:
dHvap_dT += a*c*x
x *= p
a += 1.0
dHvap_dT /= T - Tcs[i]
dHvaps_dT.append(dHvap_dT)
return dHvaps_dT
self._dHvaps_dT = dHvaps_dT = [EnthalpyVaporizations[i].T_dependent_property_derivative(T) for i in cmps]
for i in cmps:
if dHvaps_dT[i] is None:
dHvaps_dT[i] = 0.0
return dHvaps_dT
def Hvaps_T_ref(self):
try:
return self._Hvaps_T_ref
except AttributeError:
pass
EnthalpyVaporizations, cmps = self.EnthalpyVaporizations, range(self.N)
T_REF_IG = self.T_REF_IG
self._Hvaps_T_ref = [EnthalpyVaporizations[i](T_REF_IG) for i in cmps]
return self._Hvaps_T_ref
def Poyntings_at(self, T, P, Psats=None, Vms=None):
if not self.use_Poynting:
return [1.0]*self.N
cmps = range(self.N)
if Psats is None:
Psats = self.Psats_at(T)
if Vms is None:
Vms = self.Vms_sat_at(T)
RT_inv = 1.0/(R*T)
return [exp(Vms[i]*(P-Psats[i])*RT_inv) for i in cmps]
[docs] def Poyntings(self):
r'''Method to calculate and return the Poynting pressure correction
factors of the phase, [-].
.. math::
\text{Poynting}_i = \exp\left(\frac{V_{m,i}(P-P_{sat})}{RT}\right)
Returns
-------
Poyntings : list[float]
Poynting pressure correction factors, [-]
Notes
-----
The above formula is correct for pressure-independent molar volumes.
When the volume does depend on pressure, the full expression is:
.. math::
\text{Poynting} = \exp\left[\frac{\int_{P_i^{sat}}^P V_i^l dP}{RT}\right]
When a specified model e.g. the Tait equation is used, an analytical
integral of this term is normally available.
'''
try:
return self._Poyntings
except AttributeError:
pass
if not self.use_Poynting:
self._Poyntings = [1.0]*self.N
return self._Poyntings
T, P = self.T, self.P
try:
Psats = self._Psats
except AttributeError:
Psats = self.Psats()
try:
Vms_sat = self._Vms_sat
except AttributeError:
Vms_sat = self.Vms_sat()
RT_inv = 1.0/(R*T)
self._Poyntings = [trunc_exp(Vml*(P-Psat)*RT_inv) for Psat, Vml in zip(Psats, Vms_sat)]
return self._Poyntings
def dPoyntings_dT(self):
try:
return self._dPoyntings_dT
except AttributeError:
pass
if not self.use_Poynting:
self._dPoyntings_dT = [0.0]*self.N
return self._dPoyntings_dT
T, P = self.T, self.P
Psats = self.Psats()
dPsats_dT = self.dPsats_dT()
Vms = self.Vms_sat()
dVms_sat_dT = self.dVms_sat_dT()
x0 = 1.0/R
x1 = 1.0/T
RT_inv = x0*x1
self._dPoyntings_dT = dPoyntings_dT = []
for i in range(self.N):
x2 = Vms[i]
x3 = Psats[i]
x4 = P - x3
x5 = x1*x2*x4
dPoyntings_dTi = -RT_inv*(x2*dPsats_dT[i] - x4*dVms_sat_dT[i] + x5)*trunc_exp(x0*x5)
dPoyntings_dT.append(dPoyntings_dTi)
return dPoyntings_dT
def dPoyntings_dT_at(self, T, P, Psats=None, Vms=None, dPsats_dT=None, dVms_sat_dT=None):
if not self.use_Poynting:
return [0.0]*self.N
if Psats is None:
Psats = self.Psats_at(T)
if dPsats_dT is None:
dPsats_dT = self.dPsats_dT_at(T, Psats)
if Vms is None:
Vms = self.Vms_sat_at(T)
if dVms_sat_dT is None:
dVms_sat_dT = self.dVms_sat_dT_at(T)
x0 = 1.0/R
x1 = 1.0/T
dPoyntings_dT = []
for i in range(self.N):
x2 = Vms[i]
x4 = P - Psats[i]
x5 = x1*x2*x4
dPoyntings_dTi = -x0*x1*(x2*dPsats_dT[i] - x4*dVms_sat_dT[i] + x5)*exp(x0*x5)
dPoyntings_dT.append(dPoyntings_dTi)
return dPoyntings_dT
def d2Poyntings_dT2(self):
try:
return self._d2Poyntings_dT2
except AttributeError:
pass
if not self.use_Poynting:
self._d2Poyntings_dT2 = [0.0]*self.N
return self._d2Poyntings_dT2
T, P = self.T, self.P
Psats = self.Psats()
dPsats_dT = self.dPsats_dT()
d2Psats_dT2 = self.d2Psats_dT2()
Vms = self.Vms_sat()
dVms_sat_dT = self.dVms_sat_dT()
d2Vms_sat_dT2 = self.d2Vms_sat_dT2()
x6 = 1.0/T
x7 = x6 + x6
x11 = 1.0/R
x12 = x11*x6
c0 = 2.0*x6*x6
self._d2Poyntings_dT2 = d2Poyntings_dT2 = []
"""
from sympy import *
R, T, P = symbols('R, T, P')
Vml, Psat = symbols('Vml, Psat', cls=Function)
RT_inv = 1/(R*T)
Poy = exp(Vml(T)*(P-Psat(T))*RT_inv)
cse(diff(Poy, T, 2), optimizations='basic')
"""
for i in range(self.N):
x0 = Vms[i]
x1 = Psats[i]
x2 = P - x1
x3 = x0*x2
x4 = dPsats_dT[i]
x5 = x0*x4
x8 = dVms_sat_dT[i]
x9 = x2*x8
x10 = x3*x6
x50 = (x10 + x5 - x9)
d2Poyntings_dT2i = (x12*(-x0*d2Psats_dT2[i] + x12*x50*x50
+ x2*d2Vms_sat_dT2[i] - 2.0*x4*x8 + x5*x7
- x7*x9 + x3*c0)*exp(x10*x11))
d2Poyntings_dT2.append(d2Poyntings_dT2i)
return d2Poyntings_dT2
def dPoyntings_dP(self):
'''from sympy import *
R, T, P, zi = symbols('R, T, P, zi')
Vml = symbols('Vml', cls=Function)
cse(diff(exp(Vml(T)*(P - Psati(T))/(R*T)), P), optimizations='basic')
'''
try:
return self._dPoyntings_dP
except AttributeError:
pass
if not self.use_Poynting:
self._dPoyntings_dP = [0.0]*self.N
return self._dPoyntings_dP
T, P = self.T, self.P
Psats = self.Psats()
Vms = self.Vms_sat()
self._dPoyntings_dP = dPoyntings_dPs = []
for i in range(self.N):
x0 = Vms[i]/(R*T)
dPoyntings_dPs.append(x0*exp(x0*(P - Psats[i])))
return dPoyntings_dPs
def d2Poyntings_dPdT(self):
'''
from sympy import *
R, T, P = symbols('R, T, P')
Vml, Psat = symbols('Vml, Psat', cls=Function)
RT_inv = 1/(R*T)
Poy = exp(Vml(T)*(P-Psat(T))*RT_inv)
Poyf = symbols('Poyf')
cse(diff(Poy, T, P).subs(Poy, Poyf), optimizations='basic')
'''
try:
return self._d2Poyntings_dPdT
except AttributeError:
pass
if not self.use_Poynting:
self._d2Poyntings_dPdT = [0.0]*self.N
return self._d2Poyntings_dPdT
try:
Psats = self._Psats
except AttributeError:
Psats = self.Psats()
try:
dPsats_dT = self._dPsats_dT
except AttributeError:
dPsats_dT = self.dPsats_dT()
try:
Vms = self._Vms_sat
except AttributeError:
Vms = self.Vms_sat()
try:
dVms_sat_dT = self._dVms_sat_dT
except AttributeError:
dVms_sat_dT = self.dVms_sat_dT()
try:
Poyntings = self._Poyntings
except AttributeError:
Poyntings = self.Poyntings()
x0 = R_inv
x1 = 1.0/self.T
P = self.P
nRT_inv = -x0*x1
self._d2Poyntings_dPdT = d2Poyntings_dPdT = []
for i in range(self.N):
x2 = Vms[i]
x3 = x1*x2
x4 = dVms_sat_dT[i]
x5 = Psats[i]
x6 = P - x5
v = Poyntings[i]*nRT_inv*(x0*x3*(x2*dPsats_dT[i] + x3*x6 - x4*x6) + x3 - x4)
d2Poyntings_dPdT.append(v)
return d2Poyntings_dPdT
d2Poyntings_dTdP = d2Poyntings_dPdT
def phis_sat_at(self, T):
if not self.use_phis_sat:
return [1.0]*self.N
phis_sat = []
for i in self.eos_pure_instances:
try:
phis_sat.append(i.phi_sat(min(T, i.Tc), polish=True))
except Exception as e:
if T < self.PHI_SAT_IDEAL_TR*i.Tc:
phis_sat.append(1.0)
else:
raise e
return phis_sat
[docs] def phis_sat(self):
r'''Method to calculate and return the saturation fugacity coefficient
correction factors of the phase, [-].
These are calculated from the
provided pure-component equations of state. This term should only be
used with a consistent vapor-phase cubic equation of state.
Returns
-------
phis_sat : list[float]
Saturation fugacity coefficient correction factors, [-]
Notes
-----
.. warning::
This factor cannot be calculated when a compound is supercritical,
as there is no longer any vapor-liquid pure-component equilibrium
(by definition).
'''
try:
return self._phis_sat
except AttributeError:
pass
if not self.use_phis_sat:
self._phis_sat = [1.0]*self.N
return self._phis_sat
T = self.T
self._phis_sat = phis_sat = []
for i in self.eos_pure_instances:
try:
phis_sat.append(i.phi_sat(min(T, i.Tc), polish=True))
except Exception as e:
if T < self.PHI_SAT_IDEAL_TR*i.Tc:
phis_sat.append(1.0)
else:
raise e
return phis_sat
def dphis_sat_dT_at(self, T):
if not self.use_phis_sat:
return [0.0]*self.N
dphis_sat_dT = []
for i in self.eos_pure_instances:
try:
dphis_sat_dT.append(i.dphi_sat_dT(min(T, i.Tc)))
except Exception as e:
if T < self.PHI_SAT_IDEAL_TR*i.Tc:
dphis_sat_dT.append(0.0)
else:
raise e
return dphis_sat_dT
def dphis_sat_dT(self):
try:
return self._dphis_sat_dT
except AttributeError:
pass
if not self.use_phis_sat:
self._dphis_sat_dT = [0.0]*self.N
return self._dphis_sat_dT
T = self.T
self._dphis_sat_dT = dphis_sat_dT = []
for i in self.eos_pure_instances:
try:
dphis_sat_dT.append(i.dphi_sat_dT(min(T, i.Tc)))
except Exception as e:
if T < self.PHI_SAT_IDEAL_TR*i.Tc:
dphis_sat_dT.append(0.0)
else:
raise e
return dphis_sat_dT
def d2phis_sat_dT2(self):
# Numerically implemented
try:
return self._d2phis_sat_dT2
except AttributeError:
pass
if not self.use_phis_sat:
self._d2phis_sat_dT2 = [0.0]*self.N
return self._d2phis_sat_dT2
T = self.T
self._d2phis_sat_dT2 = d2phis_sat_dT2 = []
for i in self.eos_pure_instances:
try:
d2phis_sat_dT2.append(i.d2phi_sat_dT2(min(T, i.Tc)))
except Exception as e:
if T < self.PHI_SAT_IDEAL_TR*i.Tc:
d2phis_sat_dT2.append(0.0)
else:
raise e
return d2phis_sat_dT2
def phis_at(self, T, P, zs, Psats=None, gammas=None, phis_sat=None, Poyntings=None):
P_inv = 1.0/P
if Psats is None:
Psats = self.Psats_at(T)
if gammas is None:
gammas = self.gammas_at(T, zs)
if phis_sat is None:
phis_sat = self.phis_sat_at(T)
if Poyntings is None:
Poyntings = self.Poyntings_at(T, P, Psats=Psats)
return [gammas[i]*Psats[i]*Poyntings[i]*phis_sat[i]*P_inv
for i in range(self.N)]
def phis(self):
r'''Method to calculate the fugacity coefficients of the
GibbsExcessLiquid phase. Depending on the settings of the phase, can
include the effects of activity coefficients `gammas`, pressure
correction terms `Poyntings`, and pure component saturation fugacities
`phis_sat` as well as the pure component vapor pressures.
.. math::
\phi_i = \frac{\gamma_i P_{i}^{sat} \phi_i^{sat} \text{Poynting}_i}
{P}
Returns
-------
phis : list[float]
Fugacity coefficients of all components in the phase, [-]
Notes
-----
Poyntings, gammas, and pure component saturation phis default to 1.
'''
try:
return self._phis
except AttributeError:
pass
P = self.P
try:
gammas = self._gammas
except AttributeError:
gammas = self.gammas()
try:
Psats = self._Psats
except AttributeError:
Psats = self.Psats()
try:
phis_sat = self._phis_sat
except AttributeError:
phis_sat = self.phis_sat()
try:
Poyntings = self._Poyntings
except AttributeError:
Poyntings = self.Poyntings()
P_inv = 1.0/P
self._phis = [gammas[i]*Psats[i]*Poyntings[i]*phis_sat[i]*P_inv
for i in range(self.N)]
return self._phis
def lnphis(self):
try:
return self._lnphis
except AttributeError:
pass
try:
self._lnphis = [log(i) for i in self.phis()]
except:
# Zero Psats - must compute them inline
P = self.P
try:
gammas = self._gammas
except AttributeError:
gammas = self.gammas()
try:
lnPsats = self._lnPsats
except AttributeError:
lnPsats = self.lnPsats()
try:
phis_sat = self._phis_sat
except AttributeError:
phis_sat = self.phis_sat()
try:
Poyntings = self._Poyntings
except AttributeError:
Poyntings = self.Poyntings()
P_inv = 1.0/P
self._lnphis = [log(gammas[i]*Poyntings[i]*phis_sat[i]*P_inv) + lnPsats[i]
for i in range(self.N)]
return self._lnphis
lnphis_G_min = lnphis
lnphis_lowest_Gibbs = lnphis
# def fugacities(self, T, P, zs):
# # DO NOT EDIT _ CORRECT
# gammas = self.gammas(T, zs)
# Psats = self._Psats(T=T)
# if self.use_phis_sat:
# phis = self.phis(T=T, zs=zs)
# else:
# phis = [1.0]*self.N
#
# if self.use_Poynting:
# Poyntings = self.Poyntings(T=T, P=P, Psats=Psats)
# else:
# Poyntings = [1.0]*self.N
# return [zs[i]*gammas[i]*Psats[i]*Poyntings[i]*phis[i]
# for i in range(self.N)]
#
# def dphis_dxs(self):
# if
def dphis_dT(self):
try:
return self._dphis_dT
except AttributeError:
pass
P = self.P
Psats = self.Psats()
gammas = self.gammas()
if self.use_Poynting:
# Evidence suggests poynting derivatives are not worth calculating
dPoyntings_dT = self.dPoyntings_dT() #[0.0]*self.N
Poyntings = self.Poyntings()
else:
dPoyntings_dT = [0.0]*self.N
Poyntings = [1.0]*self.N
dPsats_dT = self.dPsats_dT()
dgammas_dT = self.GibbsExcessModel.dgammas_dT()
if self.use_phis_sat:
dphis_sat_dT = self.dphis_sat_dT()
phis_sat = self.phis_sat()
else:
dphis_sat_dT = [0.0]*self.N
phis_sat = [1.0]*self.N
# print(gammas, phis_sat, Psats, Poyntings, dgammas_dT, dPoyntings_dT, dPsats_dT)
self._dphis_dT = dphis_dTl = []
for i in range(self.N):
x0 = gammas[i]
x1 = phis_sat[i]
x2 = Psats[i]
x3 = Poyntings[i]
x4 = x2*x3
x5 = x0*x1
v = (x0*x4*dphis_sat_dT[i] + x1*x4*dgammas_dT[i] + x2*x5*dPoyntings_dT[i] + x3*x5*dPsats_dT[i])/P
dphis_dTl.append(v)
return dphis_dTl
def dphis_dT_at(self, T, P, zs, phis_also=False):
Psats = self.Psats_at(T)
dPsats_dT = self.dPsats_dT_at(T, Psats)
Vms = self.Vms_sat_at(T)
dVms_sat_dT = self.dVms_sat_dT_at(T)
gammas = self.gammas_at(T, zs)
dgammas_dT = self.dgammas_dT_at(T, zs)
if self.use_Poynting:
Poyntings = self.Poyntings_at(T, P, Psats, Vms)
dPoyntings_dT = self.dPoyntings_dT_at(T, P, Psats=Psats, Vms=Vms, dPsats_dT=dPsats_dT, dVms_sat_dT=dVms_sat_dT)
else:
Poyntings = [1.0]*self.N
dPoyntings_dT = [0.0]*self.N
if self.use_phis_sat:
dphis_sat_dT = self.dphis_sat_dT_at(T)
phis_sat = self.phis_sat_at(T)
else:
dphis_sat_dT = [0.0]*self.N
phis_sat = [1.0]*self.N
dphis_dT = []
for i in range(self.N):
x0 = gammas[i]
x1 = phis_sat[i]
x2 = Psats[i]
x3 = Poyntings[i]
x4 = x2*x3
x5 = x0*x1
v = (x0*x4*dphis_sat_dT[i] + x1*x4*dgammas_dT[i] + x2*x5*dPoyntings_dT[i] + x3*x5*dPsats_dT[i])/P
dphis_dT.append(v)
if phis_also:
P_inv = 1.0/P
phis = [gammas[i]*Psats[i]*Poyntings[i]*phis_sat[i]*P_inv for i in range(self.N)]
return dphis_dT, phis
return dphis_dT
def dlnphis_dT(self):
try:
return self._dlnphis_dT
except AttributeError:
pass
dphis_dT = self.dphis_dT()
phis = self.phis()
self._dlnphis_dT = [i/j for i, j in zip(dphis_dT, phis)]
return self._dlnphis_dT
def dlnphis_dP(self):
r'''Method to calculate the pressure derivative of log fugacity
coefficients of the phase. Depending on the settings of the phase, can
include the effects of activity coefficients `gammas`, pressure
correction terms `Poyntings`, and pure component saturation fugacities
`phis_sat` as well as the pure component vapor pressures.
.. math::
\frac{\partial \ln \phi_i}{\partial P} =
\frac{\frac{\partial \text{Poynting}_i}{\partial P}}
{\text{Poynting}_i} - \frac{1}{P}
Returns
-------
dlnphis_dP : list[float]
Pressure derivative of log fugacity coefficients of all components
in the phase, [1/Pa]
Notes
-----
Poyntings, gammas, and pure component saturation phis default to 1. For
that case, :math:`\frac{\partial \ln \phi_i}{\partial P}=\frac{1}{P}`.
'''
try:
return self._dlnphis_dP
except AttributeError:
pass
try:
Poyntings = self._Poyntings
except AttributeError:
Poyntings = self.Poyntings()
try:
dPoyntings_dP = self._dPoyntings_dP
except AttributeError:
dPoyntings_dP = self.dPoyntings_dP()
P_inv = 1.0/self.P
self._dlnphis_dP = [dPoyntings_dP[i]/Poyntings[i] - P_inv for i in range(self.N)]
return self._dlnphis_dP
def gammas_at(self, T, zs):
if self.composition_independent:
return [1.0]*self.N
return self.GibbsExcessModel.to_T_xs(T, zs).gammas()
def dgammas_dT_at(self, T, zs):
if self.composition_independent:
return [0.0]*self.N
return self.GibbsExcessModel.to_T_xs(T, zs).dgammas_dT()
[docs] def gammas(self):
r'''Method to calculate and return the activity coefficients of the
phase, [-]. This is a direct call to
:obj:`GibbsExcess.gammas <thermo.activity.GibbsExcess.gammas>`.
Returns
-------
gammas : list[float]
Activity coefficients, [-]
'''
try:
return self.GibbsExcessModel._gammas
except AttributeError:
return self.GibbsExcessModel.gammas()
def dgammas_dT(self):
r'''Method to calculate and return the temperature derivative of
activity coefficients of the phase, [-].
This is a direct call to
:obj:`GibbsExcess.dgammas_dT <thermo.activity.GibbsExcess.dgammas_dT>`.
Returns
-------
dgammas_dT : list[float]
First temperature derivative of the activity coefficients, [1/K]
'''
return self.GibbsExcessModel.dgammas_dT()
def H_old(self):
# try:
# return self._H
# except AttributeError:
# pass
# Untested
T = self.T
RT = R*T
P = self.P
zs, cmps = self.zs, range(self.N)
T_REF_IG = self.T_REF_IG
P_DEPENDENT_H_LIQ = self.P_DEPENDENT_H_LIQ
try:
Cpig_integrals_pure = self._Cpig_integrals_pure
except AttributeError:
Cpig_integrals_pure = self.Cpig_integrals_pure()
H = 0.0
if P_DEPENDENT_H_LIQ:
# Page 650 Chemical Thermodynamics for Process Simulation
# Confirmed with CoolProp via analytical integrals
# Not actually checked numerically until Hvap is implemented though
"""
from scipy.integrate import *
from CoolProp.CoolProp import PropsSI
fluid = 'decane'
T = 400
Psat = PropsSI('P', 'T', T, 'Q', 0, fluid)
P2 = Psat*100
dP = P2 - Psat
Vm = 1/PropsSI('DMOLAR', 'T', T, 'Q', 0, fluid)
Vm2 = 1/PropsSI('DMOLAR', 'T', T, 'P', P2, fluid)
dH = PropsSI('HMOLAR', 'T', T, 'P', P2, fluid) - PropsSI('HMOLAR', 'T', T, 'Q', 0, fluid)
def to_int(P):
Vm = 1/PropsSI('DMOLAR', 'T', T, 'P', P, fluid)
alpha = PropsSI('ISOBARIC_EXPANSION_COEFFICIENT', 'T', T, 'P', P, fluid)
return Vm -alpha*T*Vm
quad(to_int, Psat, P2, epsabs=1.49e-14, epsrel=1.49e-14)[0]/dH
"""
if self.use_IG_Cp:
try:
Psats = self._Psats
except AttributeError:
Psats = self.Psats()
try:
dPsats_dT = self._dPsats_dT
except AttributeError:
dPsats_dT = self.dPsats_dT()
try:
Vms_sat = self._Vms_sat
except AttributeError:
Vms_sat = self.Vms_sat()
try:
dVms_sat_dT = self._Vms_sat_dT
except AttributeError:
dVms_sat_dT = self.dVms_sat_dT()
failed_dPsat_dT = False
try:
H = 0.0
for i in cmps:
dV_vap = R*T/Psats[i] - Vms_sat[i]
# print( R*T/Psats[i] , Vms_sat[i])
# ratio of der to value might be easier?
dS_vap = dPsats_dT[i]*dV_vap
# print(dPsats_dT[i]*dV_vap)
Hvap = T*dS_vap
H += zs[i]*(Cpig_integrals_pure[i] - Hvap)
except ZeroDivisionError:
failed_dPsat_dT = True
if failed_dPsat_dT or isinf(H):
# Handle the case where vapor pressure reaches zero - needs special implementations
dPsats_dT_over_Psats = self.dPsats_dT_over_Psats()
H = 0.0
for i in cmps:
# dV_vap = R*T/Psats[i] - Vms_sat[i]
# dS_vap = dPsats_dT[i]*dV_vap
Hvap = T*dPsats_dT_over_Psats[i]*RT
H += zs[i]*(Cpig_integrals_pure[i] - Hvap)
if self.use_Tait:
dH_dP_integrals_Tait = self.dH_dP_integrals_Tait()
for i in cmps:
H += zs[i]*dH_dP_integrals_Tait[i]
elif self.use_Poynting:
for i in cmps:
# This bit is the differential with respect to pressure
# dP = max(0.0, P - Psats[i]) # Breaks thermodynamic consistency
dP = P - Psats[i]
H += zs[i]*dP*(Vms_sat[i] - T*dVms_sat_dT[i])
else:
Psats = self.Psats()
Vms_sat = self.Vms_sat()
dVms_sat_dT = self.dVms_sat_dT()
dPsats_dT = self.dPsats_dT()
Hvaps_T_ref = self.Hvaps_T_ref()
Cpl_integrals_pure = self._Cpl_integrals_pure()
dVms_sat_dT_T_ref = self.dVms_sat_dT_T_ref()
Vms_sat_T_ref = self.Vms_sat_T_ref()
Psats_T_ref = self.Psats_T_ref()
Hvaps = self.Hvaps()
H = 0.0
for i in range(self.N):
H += zs[i]*(Cpl_integrals_pure[i] - Hvaps_T_ref[i])
# If we can use the liquid heat capacity and prove its consistency
# This bit is the differential with respect to pressure
dP = P - Psats_T_ref[i]
H += zs[i]*dP*(Vms_sat_T_ref[i] - T_REF_IG*dVms_sat_dT_T_ref[i])
else:
Hvaps = self.Hvaps()
for i in range(self.N):
H += zs[i]*(Cpig_integrals_pure[i] - Hvaps[i])
H += self.GibbsExcessModel.HE()
# self._H = H
return H
del H_old
[docs] def H(self):
r'''Method to calculate the enthalpy of the
:obj:`GibbsExcessLiquid` phase. Depending on the settings of the phase, this can
include the effects of activity coefficients
:obj:`gammas <GibbsExcessLiquid.gammas>`, pressure correction terms
:obj:`Poyntings <GibbsExcessLiquid.Poyntings>`, and pure component
saturation fugacities :obj:`phis_sat <GibbsExcessLiquid.phis_sat>`
as well as the pure component vapor pressures.
When `caloric_basis` is 'Poynting&PhiSat':
.. math::
H = H_{\text{excess}} + \sum_i z_i \left[-RT^2\left(
\frac{\frac{\partial \phi_{\text{sat},i}}{\partial T}}{\phi_{\text{sat},i}}
+ \frac{\frac{\partial P_{\text{sat},i}}{\partial T}}{P_{\text{sat},i}}
+ \frac{\frac{\text{Poynting}}{\partial T}}{\text{Poynting}} \right)
+ \int_{T,ref}^T C_{p,ig} dT \right]
When `caloric_basis` is 'PhiSat':
.. math::
H = H_{\text{excess}} + \sum_i z_i \left[-RT^2\left(
\frac{\frac{\partial \phi_{\text{sat},i}}{\partial T}}{\phi_{\text{sat},i}}
+ \frac{\frac{\partial P_{\text{sat},i}}{\partial T}}{P_{\text{sat},i}}
\right)
+ \int_{T,ref}^T C_{p,ig} dT \right]
When `caloric_basis` is 'Poynting':
.. math::
H = H_{\text{excess}} + \sum_i z_i \left[-RT^2\left(
+ \frac{\frac{\partial P_{\text{sat},i}}{\partial T}}{P_{\text{sat},i}}
+ \frac{\frac{\text{Poynting}}{\partial T}}{\text{Poynting}} \right)
+ \int_{T,ref}^T C_{p,ig} dT \right]
When `caloric_basis` is 'Psat':
.. math::
H = H_{\text{excess}} + \sum_i z_i \left[-RT^2\left(
+ \frac{\frac{\partial P_{\text{sat},i}}{\partial T}}{P_{\text{sat},i}}
\right)
+ \int_{T,ref}^T C_{p,ig} dT \right]
When `caloric_basis` is 'Hvap':
.. math::
H = H_{\text{excess}} + \sum_i z_i\left[-H_{vap,i}
+ \int_{T,ref}^T C_{p,ig} dT \right]
Returns
-------
H : float
Enthalpy of the phase, [J/(mol)]
Notes
-----
'''
try:
return self._H
except AttributeError:
pass
H = 0.0
T = self.T
nRT2 = -R*T*T
zs, cmps = self.zs, range(self.N)
try:
Cpig_integrals_pure = self._Cpig_integrals_pure
except AttributeError:
Cpig_integrals_pure = self.Cpig_integrals_pure()
if self.use_Hvap_caloric:
Hvaps = self.Hvaps()
for i in range(self.N):
H += zs[i]*(Cpig_integrals_pure[i] - Hvaps[i])
else:
dPsats_dT_over_Psats = self.dPsats_dT_over_Psats()
use_Poynting, use_phis_sat = self.use_Poynting, self.use_phis_sat
if use_Poynting:
try:
Poyntings = self._Poyntings
except AttributeError:
Poyntings = self.Poyntings()
try:
dPoyntings_dT = self._dPoyntings_dT
except AttributeError:
dPoyntings_dT = self.dPoyntings_dT()
if use_phis_sat:
try:
dphis_sat_dT = self._dphis_sat_dT
except AttributeError:
dphis_sat_dT = self.dphis_sat_dT()
try:
phis_sat = self._phis_sat
except AttributeError:
phis_sat = self.phis_sat()
if use_Poynting and use_phis_sat:
for i in cmps:
H += zs[i]*(nRT2*(dphis_sat_dT[i]/phis_sat[i] + dPsats_dT_over_Psats[i] + dPoyntings_dT[i]/Poyntings[i])
+ Cpig_integrals_pure[i])
elif use_Poynting:
for i in cmps:
H += zs[i]*(nRT2*(dPsats_dT_over_Psats[i] + dPoyntings_dT[i]/Poyntings[i]) + Cpig_integrals_pure[i])
elif use_phis_sat:
for i in cmps:
H += zs[i]*(nRT2*(dPsats_dT_over_Psats[i] + dphis_sat_dT[i]/phis_sat[i]) + Cpig_integrals_pure[i])
else:
for i in cmps:
H += zs[i]*(nRT2*dPsats_dT_over_Psats[i] + Cpig_integrals_pure[i])
if not self.composition_independent:
H += self.GibbsExcessModel.HE()
self._H = H
return H
def S_old(self):
# try:
# return self._S
# except AttributeError:
# pass
# Untested
# Page 650 Chemical Thermodynamics for Process Simulation
'''
from scipy.integrate import *
from CoolProp.CoolProp import PropsSI
fluid = 'decane'
T = 400
Psat = PropsSI('P', 'T', T, 'Q', 0, fluid)
P2 = Psat*100
dP = P2 - Psat
Vm = 1/PropsSI('DMOLAR', 'T', T, 'Q', 0, fluid)
Vm2 = 1/PropsSI('DMOLAR', 'T', T, 'P', P2, fluid)
dH = PropsSI('HMOLAR', 'T', T, 'P', P2, fluid) - PropsSI('HMOLAR', 'T', T, 'Q', 0, fluid)
dS = PropsSI('SMOLAR', 'T', T, 'P', P2, fluid) - PropsSI('SMOLAR', 'T', T, 'Q', 0, fluid)
def to_int2(P):
Vm = 1/PropsSI('DMOLAR', 'T', T, 'P', P, fluid)
alpha = PropsSI('ISOBARIC_EXPANSION_COEFFICIENT', 'T', T, 'P', P, fluid)
return -alpha*Vm
quad(to_int2, Psat, P2, epsabs=1.49e-14, epsrel=1.49e-14)[0]/dS
'''
S = 0.0
T, P, zs, cmps = self.T, self.P, self.zs, range(self.N)
log_zs = self.log_zs()
for i in cmps:
S -= zs[i]*log_zs[i]
S *= R
S_base = S
T_inv = 1.0/T
RT = R*T
P_REF_IG_INV = self.P_REF_IG_INV
try:
Cpig_integrals_over_T_pure = self._Cpig_integrals_over_T_pure
except AttributeError:
Cpig_integrals_over_T_pure = self.Cpig_integrals_over_T_pure()
try:
Psats = self._Psats
except AttributeError:
Psats = self.Psats()
try:
dPsats_dT = self._dPsats_dT
except AttributeError:
dPsats_dT = self.dPsats_dT()
try:
Vms_sat = self._Vms_sat
except AttributeError:
Vms_sat = self.Vms_sat()
try:
dVms_sat_dT = self._Vms_sat_dT
except AttributeError:
dVms_sat_dT = self.dVms_sat_dT()
if self.P_DEPENDENT_H_LIQ:
if self.use_IG_Cp:
failed_dPsat_dT = False
try:
for i in range(self.N):
dSi = Cpig_integrals_over_T_pure[i]
dVsat = R*T/Psats[i] - Vms_sat[i]
dSvap = dPsats_dT[i]*dVsat
# dSvap = Hvaps[i]/T # Confirmed - this line breaks everything - do not use
dSi -= dSvap
# dSi = Cpig_integrals_over_T_pure[i] - Hvaps[i]*T_inv # Do the transition at the temperature of the liquid
# Take each component to its reference state change - saturation pressure
# dSi -= R*log(P*P_REF_IG_INV)
dSi -= R*log(Psats[i]*P_REF_IG_INV)
# dSi -= R*log(P/101325.0)
# Only include the
dP = P - Psats[i]
# dP = max(0.0, P - Psats[i])
# if dP > 0.0:
# I believe should include effect of pressure on all components, regardless of phase
dSi -= dP*dVms_sat_dT[i]
S += dSi*zs[i]
except (ZeroDivisionError, ValueError):
# Handle the zero division on Psat or the log getting two small
failed_dPsat_dT = True
if failed_dPsat_dT or isinf(S):
S = S_base
# Handle the case where vapor pressure reaches zero - needs special implementations
dPsats_dT_over_Psats = self.dPsats_dT_over_Psats()
lnPsats = self.lnPsats()
LOG_P_REF_IG = self.LOG_P_REF_IG
for i in cmps:
dSi = Cpig_integrals_over_T_pure[i]
dSvap = RT*dPsats_dT_over_Psats[i]
dSi -= dSvap
dSi -= R*(lnPsats[i] - LOG_P_REF_IG)# trunc_log(Psats[i]*P_REF_IG_INV)
dSi -= P*dVms_sat_dT[i]
S += dSi*zs[i]
if self.use_Tait:
pass
elif self.use_Poynting:
pass
# for i in cmps:
else:
# mine
Hvaps_T_ref = self.Hvaps_T_ref()
Psats_T_ref = self.Psats_T_ref()
Cpl_integrals_over_T_pure = self._Cpl_integrals_over_T_pure()
T_REF_IG_INV = self.T_REF_IG_INV
dVms_sat_dT_T_ref = self.dVms_sat_dT_T_ref()
# Vms_sat_T_ref = self.Vms_sat_T_ref()
for i in range(self.N):
dSi = Cpl_integrals_over_T_pure[i]
dSi -= Hvaps_T_ref[i]*T_REF_IG_INV
# Take each component to its reference state change - saturation pressure
dSi -= R*log(Psats_T_ref[i]*P_REF_IG_INV)
# I believe should include effect of pressure on all components, regardless of phase
dP = P - Psats_T_ref[i]
dSi -= dP*dVms_sat_dT_T_ref[i]
S += dSi*zs[i]
# else:
# # COCO
# Hvaps = self.Hvaps()
# Psats_T_ref = self.Psats_T_ref()
# _Cpl_integrals_over_T_pure = self._Cpl_integrals_over_T_pure()
# T_REF_IG_INV = self.T_REF_IG_INV
#
# for i in range(self.N):
# dSi = -_Cpl_integrals_over_T_pure[i]
# dSi -= Hvaps[i]/T
# # Take each component to its reference state change - saturation pressure
# dSi -= R*log(Psats[i]*P_REF_IG_INV)
#
# dP = P - Psats[i]
# # I believe should include effect of pressure on all components, regardless of phase
# dSi -= dP*dVms_sat_dT[i]
# S += dSi*zs[i]
else:
Hvaps = self.Hvaps()
for i in cmps:
Sg298_to_T = Cpig_integrals_over_T_pure[i]
Svap = -Hvaps[i]*T_inv # Do the transition at the temperature of the liquid
S += zs[i]*(Sg298_to_T + Svap - R*log(P*P_REF_IG_INV))
# self._S =
S = S + self.GibbsExcessModel.SE()
return S
[docs] def S(self):
r'''Method to calculate the entropy of the
:obj:`GibbsExcessLiquid` phase. Depending on the settings of the phase, this can
include the effects of activity coefficients
:obj:`gammas <GibbsExcessLiquid.gammas>`, pressure correction terms
:obj:`Poyntings <GibbsExcessLiquid.Poyntings>`, and pure component
saturation fugacities :obj:`phis_sat <GibbsExcessLiquid.phis_sat>`
as well as the pure component vapor pressures.
When `caloric_basis` is 'Poynting&PhiSat':
.. math::
S = S_{\text{excess}} - R\sum_i z_i\ln z_i - R\ln\left(\frac{P}{P_{ref}}\right)
- \sum_i z_i\left[R\left(
T \frac{\frac{\partial \phi_{\text{sat},i}}{\partial T}}{\phi_{\text{sat},i}}
+ T\frac{\frac{\partial P_{\text{sat},i}}{\partial T}}{P_{\text{sat},i}}
+ T\frac{\frac{\text{Poynting}}{\partial T}}{\text{Poynting}}
+ \ln(P_{\text{sat},i}) + \ln\left(\frac{\text{Poynting}\cdot\phi_{\text{sat},i}}{P}\right)
\right) - \int_{T,ref}^T \frac{C_{p,ig,i}}{T} dT \right]
When `caloric_basis` is 'PhiSat':
.. math::
S = S_{\text{excess}} - R\sum_i z_i\ln z_i - R\ln\left(\frac{P}{P_{ref}}\right)
- \sum_i z_i\left[R\left(
T \frac{\frac{\partial \phi_{\text{sat},i}}{\partial T}}{\phi_{\text{sat},i}}
+ T\frac{\frac{\partial P_{\text{sat},i}}{\partial T}}{P_{\text{sat},i}}
+ \ln(P_{\text{sat},i}) + \ln\left(\frac{\phi_{\text{sat},i}}{P}\right)
\right) - \int_{T,ref}^T \frac{C_{p,ig,i}}{T} dT \right]
When `caloric_basis` is 'Poynting':
.. math::
S = S_{\text{excess}} - R\sum_i z_i\ln z_i - R\ln\left(\frac{P}{P_{ref}}\right)
- \sum_i z_i\left[R\left(
T\frac{\frac{\partial P_{\text{sat},i}}{\partial T}}{P_{\text{sat},i}}
+ T\frac{\frac{\text{Poynting}}{\partial T}}{\text{Poynting}}
+ \ln(P_{\text{sat},i}) + \ln\left(\frac{\text{Poynting}}{P}\right)
\right) - \int_{T,ref}^T \frac{C_{p,ig,i}}{T} dT \right]
When `caloric_basis` is 'Psat':
.. math::
S = S_{\text{excess}} - R\sum_i z_i\ln z_i - R\ln\left(\frac{P}{P_{ref}}\right)
- \sum_i z_i\left[R\left(
T\frac{\frac{\partial P_{\text{sat},i}}{\partial T}}{P_{\text{sat},i}}
+ \ln(P_{\text{sat},i}) + \ln\left(\frac{1}{P}\right)
\right) - \int_{T,ref}^T \frac{C_{p,ig,i}}{T} dT \right]
When `caloric_basis` is 'Hvap':
.. math::
S = S_{\text{excess}} - R\sum_i z_i\ln z_i - R\ln\left(\frac{P}{P_{ref}}\right)
- \sum_i z_i\left[R\left(\ln P_{\text{sat},i} + \ln\left(\frac{1}{P}\right)\right)
+ \frac{H_{vap,i}}{T}
- \int_{T,ref}^T \frac{C_{p,ig,i}}{T} dT \right]
Returns
-------
S : float
Entropy of the phase, [J/(mol*K)]
Notes
-----
'''
try:
return self._S
except AttributeError:
pass
T, P = self.T, self.P
P_inv = 1.0/P
zs, cmps = self.zs, range(self.N)
log_zs = self.log_zs()
S_comp = 0.0
for i in cmps:
S_comp -= zs[i]*log_zs[i]
S = S_comp - log(P*self.P_REF_IG_INV)
S *= R
try:
Cpig_integrals_over_T_pure = self._Cpig_integrals_over_T_pure
except AttributeError:
Cpig_integrals_over_T_pure = self.Cpig_integrals_over_T_pure()
try:
lnPsats = self._lnPsats
except AttributeError:
lnPsats = self.lnPsats()
use_Poynting, use_phis_sat, use_Hvap_caloric = self.use_Poynting, self.use_phis_sat, self.use_Hvap_caloric
if use_Hvap_caloric:
Hvaps = self.Hvaps()
T_inv = 1.0/T
logP_inv = log(P_inv)
# Almost the same as no Poynting
for i in cmps:
S -= zs[i]*(R*(lnPsats[i] + logP_inv)
- Cpig_integrals_over_T_pure[i] + Hvaps[i]*T_inv)
else:
dPsats_dT_over_Psats = self.dPsats_dT_over_Psats()
if use_Poynting:
try:
Poyntings = self._Poyntings
except AttributeError:
Poyntings = self.Poyntings()
try:
dPoyntings_dT = self._dPoyntings_dT
except AttributeError:
dPoyntings_dT = self.dPoyntings_dT()
if use_phis_sat:
try:
dphis_sat_dT = self._dphis_sat_dT
except AttributeError:
dphis_sat_dT = self.dphis_sat_dT()
try:
phis_sat = self._phis_sat
except AttributeError:
phis_sat = self.phis_sat()
if use_Poynting and use_phis_sat:
for i in cmps:
S -= zs[i]*(R*(T*(dphis_sat_dT[i]/phis_sat[i] + dPsats_dT_over_Psats[i] + dPoyntings_dT[i]/Poyntings[i])
+ lnPsats[i] + log(Poyntings[i]*phis_sat[i]*P_inv)) - Cpig_integrals_over_T_pure[i])
elif use_Poynting:
for i in cmps:
S -= zs[i]*(R*(T*(dPsats_dT_over_Psats[i] + dPoyntings_dT[i]/Poyntings[i])
+ lnPsats[i] + log(Poyntings[i]*P_inv)) - Cpig_integrals_over_T_pure[i])
elif use_phis_sat:
for i in cmps:
S -= zs[i]*(R*(T*(dphis_sat_dT[i]/phis_sat[i] + dPsats_dT_over_Psats[i])
+ lnPsats[i] + log(phis_sat[i]*P_inv)) - Cpig_integrals_over_T_pure[i])
else:
logP_inv = log(P_inv)
for i in cmps:
S -= zs[i]*(R*(T*dPsats_dT_over_Psats[i] + lnPsats[i] + logP_inv)
- Cpig_integrals_over_T_pure[i])
if not self.composition_independent:
S += self.GibbsExcessModel.SE()
self._S = S
return S
def Cp_old(self):
try:
return self._Cp
except AttributeError:
pass
# Needs testing
T, P, P_DEPENDENT_H_LIQ = self.T, self.P, self.P_DEPENDENT_H_LIQ
Cp, zs = 0.0, self.zs
Cpigs_pure = self.Cpigs_pure()
if P_DEPENDENT_H_LIQ:
try:
Psats = self._Psats
except AttributeError:
Psats = self.Psats()
try:
dPsats_dT = self._dPsats_dT
except AttributeError:
dPsats_dT = self.dPsats_dT()
try:
d2Psats_dT2 = self._d2Psats_dT2
except AttributeError:
d2Psats_dT2 = self.d2Psats_dT2()
try:
Vms_sat = self._Vms_sat
except AttributeError:
Vms_sat = self.Vms_sat()
try:
dVms_sat_dT = self._Vms_sat_dT
except AttributeError:
dVms_sat_dT = self.dVms_sat_dT()
try:
d2Vms_sat_dT2 = self._d2Vms_sat_dT2
except AttributeError:
d2Vms_sat_dT2 = self.d2Vms_sat_dT2()
failed_dPsat_dT = False
try:
for i in range(self.N):
x0 = Psats[i]
Psat_inv = 1.0/x0
x1 = Vms_sat[i]
x2 = dPsats_dT[i]
x3 = R*Psat_inv
x4 = T*x3
x5 = -x1
x6 = dVms_sat_dT[i]
x7 = T*x2
# print(#-T*(P - x0)*d2Vms_sat_dT2[i],
# - T*(x4 + x5)*d2Psats_dT2[i], T, x4, x5, d2Psats_dT2[i],
#x2*(x1 - x4) + x2*(T*x6 + x5) - x7*(-R*x7*Psat_inv*Psat_inv + x3 - x6),
#Cpigs_pure[i]
# )
Cp += zs[i]*(-T*(P - x0)*d2Vms_sat_dT2[i] - T*(x4 + x5)*d2Psats_dT2[i]
+ x2*(x1 - x4) + x2*(T*x6 + x5) - x7*(-R*x7*Psat_inv*Psat_inv + x3 - x6) + Cpigs_pure[i])
# The second derivative of volume is zero when extrapolating, which causes zero issues, discontinuous derivative
"""
from sympy import *
T, P, R, zi = symbols('T, P, R, zi')
Psat, Cpig_int, Vmsat = symbols('Psat, Cpig_int, Vmsat', cls=Function)
dVmsatdT = diff(Vmsat(T), T)
dPsatdT = diff(Psat(T), T)
dV_vap = R*T/Psat(T) - Vmsat(T)
dS_vap = dPsatdT*dV_vap
Hvap = T*dS_vap
H = zi*(Cpig_int(T) - Hvap)
dP = P - Psat(T)
H += zi*dP*(Vmsat(T) - T*dVmsatdT)
(cse(diff(H, T), optimizations='basic'))
"""
except (ZeroDivisionError, ValueError):
# Handle the zero division on Psat or the log getting two small
failed_dPsat_dT = True
if failed_dPsat_dT or isinf(Cp) or isnan(Cp):
dlnPsats_dT = self.dlnPsats_dT()
d2lnPsats_dT2 = self.d2lnPsats_dT2()
Cp = 0.0
for i in range(self.N):
Cp += zs[i]*(Cpigs_pure[i] - P*T*d2Vms_sat_dT2[i] - R*T*T*d2lnPsats_dT2[i]
- 2.0*R*T*dlnPsats_dT[i])
"""
from sympy import *
T, P, R, zi = symbols('T, P, R, zi')
lnPsat, Cpig_T_int, Vmsat = symbols('lnPsat, Cpig_T_int, Vmsat', cls=Function)
dVmsatdT = diff(Vmsat(T), T)
dPsatdT = diff(exp(lnPsat(T)), T)
dV_vap = R*T/exp(lnPsat(T)) - Vmsat(T)
dS_vap = dPsatdT*dV_vap
Hvap = T*dS_vap
H = zi*(Cpig_int(T) - Hvap)
dP = P
H += zi*dP*(Vmsat(T) - T*dVmsatdT)
print(simplify(expand(diff(H, T)).subs(exp(lnPsat(T)), 0)/zi))
"""
# Cp += zs[i]*(Cpigs_pure[i] - dHvaps_dT[i])
# Cp += zs[i]*(-T*(P - Psats[i])*d2Vms_sat_dT2[i] + (T*dVms_sat_dT[i] - Vms_sat[i])*dPsats_dT[i])
else:
dHvaps_dT = self.dHvaps_dT()
for i in range(self.N):
Cp += zs[i]*(Cpigs_pure[i] - dHvaps_dT[i])
Cp += self.GibbsExcessModel.CpE()
return Cp
[docs] def Cp(self):
try:
return self._Cp
except AttributeError:
pass
T, zs, cmps = self.T, self.zs, range(self.N)
Cpigs_pure = self.Cpigs_pure()
use_Poynting, use_phis_sat = self.use_Poynting, self.use_phis_sat
if use_Poynting:
try:
d2Poyntings_dT2 = self._d2Poyntings_dT2
except AttributeError:
d2Poyntings_dT2 = self.d2Poyntings_dT2()
try:
dPoyntings_dT = self._dPoyntings_dT
except AttributeError:
dPoyntings_dT = self.dPoyntings_dT()
try:
Poyntings = self._Poyntings
except AttributeError:
Poyntings = self.Poyntings()
if use_phis_sat:
try:
d2phis_sat_dT2 = self._d2phis_sat_dT2
except AttributeError:
d2phis_sat_dT2 = self.d2phis_sat_dT2()
try:
dphis_sat_dT = self._dphis_sat_dT
except AttributeError:
dphis_sat_dT = self.dphis_sat_dT()
try:
phis_sat = self._phis_sat
except AttributeError:
phis_sat = self.phis_sat()
dPsats_dT_over_Psats = self.dPsats_dT_over_Psats()
d2Psats_dT2_over_Psats = self.d2Psats_dT2_over_Psats()
RT = R*T
RT2 = RT*T
RT2_2 = RT + RT
Cp = 0.0
if use_Poynting and use_phis_sat:
for i in cmps:
Poy_inv = 1.0/Poyntings[i]
phi_inv = 1.0/phis_sat[i]
dPoy_ratio = dPoyntings_dT[i]*Poy_inv
dphi_ratio = dphis_sat_dT[i]*phi_inv
a = (d2phis_sat_dT2[i]*phi_inv - dphi_ratio*dphi_ratio
+ d2Psats_dT2_over_Psats[i] - dPsats_dT_over_Psats[i]*dPsats_dT_over_Psats[i]
+ d2Poyntings_dT2[i]*Poy_inv - dPoy_ratio*dPoy_ratio)
b = dphi_ratio + dPsats_dT_over_Psats[i] + dPoy_ratio
Cp -= zs[i]*(RT2*a + RT2_2*b - Cpigs_pure[i])
elif use_Poynting:
for i in cmps:
Poy_inv = 1.0/Poyntings[i]
dPoy_ratio = dPoyntings_dT[i]*Poy_inv
a = (d2Psats_dT2_over_Psats[i] - dPsats_dT_over_Psats[i]*dPsats_dT_over_Psats[i]
+ d2Poyntings_dT2[i]*Poy_inv - dPoy_ratio*dPoy_ratio)
b = dPsats_dT_over_Psats[i] + dPoy_ratio
Cp -= zs[i]*(RT2*a + RT2_2*b - Cpigs_pure[i])
elif use_phis_sat:
for i in cmps:
phi_inv = 1.0/phis_sat[i]
dphi_ratio = dphis_sat_dT[i]*phi_inv
a = (d2phis_sat_dT2[i]*phi_inv - dphi_ratio*dphi_ratio
+ d2Psats_dT2_over_Psats[i] - dPsats_dT_over_Psats[i]*dPsats_dT_over_Psats[i])
b = dphi_ratio + dPsats_dT_over_Psats[i]
Cp -= zs[i]*(RT2*a + RT2_2*b - Cpigs_pure[i])
else:
for i in cmps:
a = (d2Psats_dT2_over_Psats[i] - dPsats_dT_over_Psats[i]*dPsats_dT_over_Psats[i])
b = dPsats_dT_over_Psats[i]
Cp -= zs[i]*(RT2*a + RT2_2*b - Cpigs_pure[i])
if not self.composition_independent:
Cp += self.GibbsExcessModel.CpE()
self._Cp = Cp
return Cp
dH_dT = Cp
def dS_dT_old(self):
# Needs testing
T, P, P_DEPENDENT_H_LIQ = self.T, self.P, self.P_DEPENDENT_H_LIQ
RT = R*T
zs = self.zs
Cpigs_pure = self.Cpigs_pure()
dS_dT = 0.0
T_inv = 1.0/T
if P_DEPENDENT_H_LIQ:
d2Vms_sat_dT2 = self.d2Vms_sat_dT2()
dVms_sat_dT = self.dVms_sat_dT()
Vms_sat = self.Vms_sat()
Psats = self.Psats()
dPsats_dT = self.dPsats_dT()
d2Psats_dT2 = self.d2Psats_dT2()
failed_dPsat_dT = False
for Psat in Psats:
if Psat < 1e-40:
failed_dPsat_dT = True
if not failed_dPsat_dT:
try:
"""
from sympy import *
T, P, R, zi, P_REF_IG = symbols('T, P, R, zi, P_REF_IG')
Psat, Cpig_T_int, Vmsat = symbols('Psat, Cpig_T_int, Vmsat', cls=Function)
dVmsatdT = diff(Vmsat(T), T)
dPsatdT = diff(Psat(T), T)
S = 0
dSi = Cpig_T_int(T)
dVsat = R*T/Psat(T) - Vmsat(T)
dSvap = dPsatdT*dVsat
dSi -= dSvap
dSi -= R*log(Psat(T)/P_REF_IG)
dP = P - Psat(T)
dSi -= dP*dVmsatdT
S += dSi*zi
# cse(diff(S, T), optimizations='basic')
"""
for i in range(self.N):
x0 = Psats[i]
x1 = dPsats_dT[i]
x2 = R/x0
x3 = Vms_sat[i]
x4 = dVms_sat_dT[i]
dS_dT -= zs[i]*(x1*x2 - x1*x4 - x1*(RT*x1/x0**2 - x2 + x4) + (P - x0)*d2Vms_sat_dT2[i]
+ (T*x2 - x3)*d2Psats_dT2[i] - Cpigs_pure[i]*T_inv)
except (ZeroDivisionError, ValueError):
# Handle the zero division on Psat or the log getting two small
failed_dPsat_dT = True
if failed_dPsat_dT:
# lnPsats = self.lnPsats()
dlnPsats_dT = self.dlnPsats_dT()
d2lnPsats_dT2 = self.d2lnPsats_dT2()
# P*Derivative(Vmsat(T), (T, 2))
# R*T*Derivative(lnPsat(T), (T, 2))
# 2*R*Derivative(lnPsat(T), T) + Derivative(Cpig_T_int(T), T)
"""
from sympy import *
T, P, R, zi, P_REF_IG = symbols('T, P, R, zi, P_REF_IG')
lnPsat, Cpig_T_int, Vmsat = symbols('lnPsat, Cpig_T_int, Vmsat', cls=Function)
# Psat, Cpig_T_int, Vmsat = symbols('Psat, Cpig_T_int, Vmsat', cls=Function)
dVmsatdT = diff(Vmsat(T), T)
dPsatdT = diff(exp(lnPsat(T)), T)
S = 0
dSi = Cpig_T_int(T)
dVsat = R*T/exp(lnPsat(T)) - Vmsat(T)
dSvap = dPsatdT*dVsat
dSi -= dSvap
# dSi -= R*log(Psat(T)/P_REF_IG)
dSi -= R*(lnPsat(T) - log(P_REF_IG))
dP = P - exp(lnPsat(T))
dSi -= dP*dVmsatdT
S += dSi*zi
# cse(diff(S, T), optimizations='basic')
print(simplify(expand(diff(S, T)).subs(exp(lnPsat(T)), 0)/zi))
"""
dS_dT = 0.0
for i in range(self.N):
dS_dT -= zs[i]*(P*d2Vms_sat_dT2[i] + RT*d2lnPsats_dT2[i]
+ 2.0*R*dlnPsats_dT[i]- Cpigs_pure[i]*T_inv)
dS_dT += self.GibbsExcessModel.dSE_dT()
return dS_dT
def dS_dT(self):
try:
return self._dS_dT
except AttributeError:
pass
T, zs, cmps = self.T, self.zs, range(self.N)
use_Poynting, use_phis_sat = self.use_Poynting, self.use_phis_sat
if use_Poynting:
try:
d2Poyntings_dT2 = self._d2Poyntings_dT2
except AttributeError:
d2Poyntings_dT2 = self.d2Poyntings_dT2()
try:
dPoyntings_dT = self._dPoyntings_dT
except AttributeError:
dPoyntings_dT = self.dPoyntings_dT()
try:
Poyntings = self._Poyntings
except AttributeError:
Poyntings = self.Poyntings()
if use_phis_sat:
try:
d2phis_sat_dT2 = self._d2phis_sat_dT2
except AttributeError:
d2phis_sat_dT2 = self.d2phis_sat_dT2()
try:
dphis_sat_dT = self._dphis_sat_dT
except AttributeError:
dphis_sat_dT = self.dphis_sat_dT()
try:
phis_sat = self._phis_sat
except AttributeError:
phis_sat = self.phis_sat()
dPsats_dT_over_Psats = self.dPsats_dT_over_Psats()
d2Psats_dT2_over_Psats = self.d2Psats_dT2_over_Psats()
Cpigs_pure = self.Cpigs_pure()
T_inv = 1.0/T
RT = R*T
R_2 = R + R
dS_dT = 0.0
if use_Poynting and use_phis_sat:
for i in cmps:
Poy_inv = 1.0/Poyntings[i]
phi_inv = 1.0/phis_sat[i]
dPoy_ratio = dPoyntings_dT[i]*Poy_inv
dphi_ratio = dphis_sat_dT[i]*phi_inv
a = (d2phis_sat_dT2[i]*phi_inv - dphi_ratio*dphi_ratio
+ d2Psats_dT2_over_Psats[i] - dPsats_dT_over_Psats[i]*dPsats_dT_over_Psats[i]
+ d2Poyntings_dT2[i]*Poy_inv - dPoy_ratio*dPoy_ratio)
b = dphi_ratio + dPsats_dT_over_Psats[i] + dPoy_ratio
dS_dT -= zs[i]*((RT*a + b*R_2) - Cpigs_pure[i]*T_inv)
elif use_Poynting:
for i in cmps:
Poy_inv = 1.0/Poyntings[i]
dPoy_ratio = dPoyntings_dT[i]*Poy_inv
a = (d2Psats_dT2_over_Psats[i] - dPsats_dT_over_Psats[i]*dPsats_dT_over_Psats[i]
+ d2Poyntings_dT2[i]*Poy_inv - dPoy_ratio*dPoy_ratio)
b = dPsats_dT_over_Psats[i] + dPoy_ratio
dS_dT -= zs[i]*((RT*a + b*R_2) - Cpigs_pure[i]*T_inv)
elif use_phis_sat:
for i in cmps:
phi_inv = 1.0/phis_sat[i]
dphi_ratio = dphis_sat_dT[i]*phi_inv
a = (d2phis_sat_dT2[i]*phi_inv - dphi_ratio*dphi_ratio
+ d2Psats_dT2_over_Psats[i] - dPsats_dT_over_Psats[i]*dPsats_dT_over_Psats[i])
b = dphi_ratio + dPsats_dT_over_Psats[i]
dS_dT -= zs[i]*((RT*a + b*R_2) - Cpigs_pure[i]*T_inv)
else:
for i in cmps:
a = (d2Psats_dT2_over_Psats[i] - dPsats_dT_over_Psats[i]*dPsats_dT_over_Psats[i])
b = dPsats_dT_over_Psats[i]
dS_dT -= zs[i]*((RT*a + b*R_2) - Cpigs_pure[i]*T_inv)
if not self.composition_independent:
dS_dT += self.GibbsExcessModel.dSE_dT()
self._dS_dT = dS_dT
return dS_dT
def dH_dP(self):
try:
return self._dH_dP
except AttributeError:
pass
T = self.T
zs = self.zs
dH_dP = 0.0
if self.use_Poynting:
nRT2 = -R*T*T
Poyntings = self.Poyntings()
dPoyntings_dP = self.dPoyntings_dP()
dPoyntings_dT = self.dPoyntings_dT()
d2Poyntings_dPdT = self.d2Poyntings_dPdT()
for i in range(self.N):
Poy_inv = 1.0/Poyntings[i]
dH_dP += nRT2*zs[i]*Poy_inv*(d2Poyntings_dPdT[i] - dPoyntings_dP[i]*dPoyntings_dT[i]*Poy_inv)
# if self.P_DEPENDENT_H_LIQ:
# if self.use_IG_Cp:
# Vms_sat = self.Vms_sat()
# dVms_sat_dT = self.dVms_sat_dT()
# Psats = self.Psats()
# for i in range(self.N):
# if P > Psats[i]:
# dH_dP += zs[i]*(-T*dVms_sat_dT[i] + Vms_sat[i])
self._dH_dP = dH_dP
return dH_dP
def dS_dP(self):
try:
return self._dS_dP
except AttributeError:
pass
T = self.T
P = self.P
P_inv = 1.0/P
zs = self.zs
if self.use_Poynting:
dS_dP = -R*P_inv
Poyntings = self.Poyntings()
dPoyntings_dP = self.dPoyntings_dP()
dPoyntings_dT = self.dPoyntings_dT()
d2Poyntings_dPdT = self.d2Poyntings_dPdT()
for i in range(self.N):
Poy_inv = 1.0/Poyntings[i]
dS_dP -= zs[i]*R*Poy_inv*(dPoyntings_dP[i] - Poyntings[i]*P_inv
+T*(d2Poyntings_dPdT[i] - dPoyntings_dP[i]*dPoyntings_dT[i]*Poy_inv))
else:
dS_dP = 0.0
# if self.P_DEPENDENT_H_LIQ:
# if self.use_IG_Cp:
# dVms_sat_dT = self.dVms_sat_dT()
# Psats = self.Psats()
# for i in range(self.N):
# if P > Psats[i]:
# dS_dP -= zs[i]*(dVms_sat_dT[i])
self._dS_dP = dS_dP
return dS_dP
def H_dep(self):
return self.H() - self.H_ideal_gas()
def S_dep(self):
return self.S() - self.S_ideal_gas()
def Cp_dep(self):
return self.Cp() - self.Cp_ideal_gas()
### Volumetric properties
def V(self):
try:
return self._V
except AttributeError:
pass
zs = self.zs
Vms = self.Vms()
"""To make a fugacity-volume identity consistent, cannot use pressure
correction unless the Poynting factor is calculated with quadrature/
integration.
"""
V = 0.0
for i in range(self.N):
V += zs[i]*Vms[i]
self._V = V
return V
def dV_dT(self):
try:
return self._dV_dT
except AttributeError:
pass
zs = self.zs
dVms_sat_dT = self.dVms_sat_dT()
dV_dT = 0.0
for i in range(self.N):
dV_dT += zs[i]*dVms_sat_dT[i]
self._dV_dT = dV_dT
return dV_dT
def d2V_dT2(self):
try:
return self._d2V_dT2
except AttributeError:
pass
zs = self.zs
d2Vms_sat_dT2 = self.d2Vms_sat_dT2()
d2V_dT2 = 0.0
for i in range(self.N):
d2V_dT2 += zs[i]*d2Vms_sat_dT2[i]
self._d2V_dT2 = d2V_dT2
return d2V_dT2
# Main needed volume derivatives
def dP_dV(self):
try:
return self._dP_dV
except AttributeError:
pass
if self.incompressible:
self._dP_dV = self.INCOMPRESSIBLE_CONST #1.0/self.VolumeLiquidMixture.property_derivative_P(self.T, self.P, self.zs, order=1)
return self._dP_dV
def d2P_dV2(self):
try:
return self._d2P_dV2
except AttributeError:
pass
if self.incompressible:
self._d2P_dV2 = self.INCOMPRESSIBLE_CONST #self.d2V_dP2()/-(self.dP_dV())**-3
return self._d2P_dV2
def dP_dT(self):
try:
return self._dP_dT
except AttributeError:
pass
self._dP_dT = self.dV_dT()/-self.dP_dV()
return self._dP_dT
def d2P_dTdV(self):
try:
return self._d2P_dTdV
except AttributeError:
pass
if self.incompressible:
self._d2P_dTdV = 0.0
else:
P = self.P
def dP_dV_for_diff(T):
return 1.0/self.VolumeLiquidMixture.property_derivative_P(T, P, self.zs, order=1)
self._d2P_dTdV = derivative(dP_dV_for_diff, self.T)
return self._d2P_dTdV
def d2P_dT2(self):
try:
return self._d2P_dT2
except AttributeError:
pass
if self.incompressible:
self._d2P_dT2 = -self.d2V_dT2()/self.INCOMPRESSIBLE_CONST
else:
P, zs = self.P, self.zs
def dP_dT_for_diff(T):
dV_dT = self.VolumeLiquidMixture.property_derivative_T(T, P, zs, order=1)
dP_dV = 1.0/self.VolumeLiquidMixture.property_derivative_P(T, P, zs, order=1)
dP_dT = dV_dT/-dP_dV
return dP_dT
self._d2P_dT2 = derivative(dP_dT_for_diff, self.T)
return self._d2P_dT2
# Volume derivatives which needed to be implemented for the main ones
def d2V_dP2(self):
try:
return self._d2V_dP2
except AttributeError:
pass
if self.incompressible:
self._d2V_dP2 = 0.0
return self._d2V_dP2
def Tait_Bs(self):
try:
return self._Tait_Bs
except:
pass
self._Tait_Bs = evaluate_linear_fits(self._Tait_B_data, self.T)
return self._Tait_Bs
def dTait_B_dTs(self):
try:
return self._dTait_B_dTs
except:
pass
self._dTait_B_dTs = evaluate_linear_fits_d(self._Tait_B_data, self.T)
return self._dTait_B_dTs
def d2Tait_B_dT2s(self):
try:
return self._d2Tait_B_dT2s
except:
pass
self._d2Tait_B_dT2s = evaluate_linear_fits_d2(self._Tait_B_data, self.T)
return self._d2Tait_B_dT2s
def Tait_Cs(self):
try:
return self._Tait_Cs
except:
pass
self._Tait_Cs = evaluate_linear_fits(self._Tait_C_data, self.T)
return self._Tait_Cs
def dTait_C_dTs(self):
try:
return self._dTait_C_dTs
except:
pass
self._dTait_C_dTs = evaluate_linear_fits_d(self._Tait_C_data, self.T)
return self._dTait_C_dTs
def d2Tait_C_dT2s(self):
try:
return self._d2Tait_C_dT2s
except:
pass
self._d2Tait_C_dT2s = evaluate_linear_fits_d2(self._Tait_C_data, self.T)
return self._d2Tait_C_dT2s
def Tait_Vs(self):
Vms_sat = self.Vms_sat()
Psats = self.Psats()
Tait_Bs = self.Tait_Bs()
Tait_Cs = self.Tait_Cs()
P = self.P
return [Vms_sat[i]*(1.0 - Tait_Cs[i]*log((Tait_Bs[i] + P)/(Tait_Bs[i] + Psats[i]) ))
for i in range(self.N)]
def dH_dP_integrals_Tait(self):
try:
return self._dH_dP_integrals_Tait
except AttributeError:
pass
Psats = self.Psats()
Vms_sat = self.Vms_sat()
dVms_sat_dT = self.dVms_sat_dT()
dPsats_dT = self.dPsats_dT()
Tait_Bs = self.Tait_Bs()
Tait_Cs = self.Tait_Cs()
dTait_C_dTs = self.dTait_C_dTs()
dTait_B_dTs = self.dTait_B_dTs()
T, P = self.T, self.P
self._dH_dP_integrals_Tait = dH_dP_integrals_Tait = []
# def to_int(P, i):
# l = self.to_TP_zs(T, P, zs)
## def to_diff(T):
## return self.to_TP_zs(T, P, zs).Tait_Vs()[i]
## dV_dT = derivative(to_diff, T, dx=1e-5*T, order=11)
#
# x0 = l.Vms_sat()[i]
# x1 = l.Tait_Cs()[i]
# x2 = l.Tait_Bs()[i]
# x3 = P + x2
# x4 = l.Psats()[i]
# x5 = x3/(x2 + x4)
# x6 = log(x5)
# x7 = l.dTait_B_dTs()[i]
# dV_dT = (-x0*(x1*(-x5*(x7 +l.dPsats_dT()[i]) + x7)/x3
# + x6*l.dTait_C_dTs()[i])
# - (x1*x6 - 1.0)*l.dVms_sat_dT()[i])
#
## print(dV_dT, dV_dT2, dV_dT/dV_dT2, T, P)
#
# V = l.Tait_Vs()[i]
# return V - T*dV_dT
# from scipy.integrate import quad
# _dH_dP_integrals_Tait = [quad(to_int, Psats[i], P, args=i)[0]
# for i in range(self.N)]
## return self._dH_dP_integrals_Tait
# print(_dH_dP_integrals_Tait)
# self._dH_dP_integrals_Tait2 = _dH_dP_integrals_Tait
# return self._dH_dP_integrals_Tait2
# dH_dP_integrals_Tait = []
for i in range(self.N):
# Very wrong according to numerical integration. Is it an issue with
# the translation to code, one of the derivatives, what was integrated,
# or sympy's integration?
x0 = Tait_Bs[i]
x1 = P + x0
x2 = Psats[i]
x3 = x0 + x2
x4 = 1.0/x3
x5 = Tait_Cs[i]
x6 = Vms_sat[i]
x7 = x5*x6
x8 = T*dVms_sat_dT[i]
x9 = x5*x8
x10 = T*dTait_C_dTs[i]
x11 = x0*x6
x12 = T*x7
x13 = -x0*x7 + x0*x9 + x10*x11 + x12*dTait_B_dTs[i]
x14 = x2*x6
x15 = x4*(x0*x8 + x10*x14 - x11 + x12*dPsats_dT[i] + x13 - x14 - x2*x7 + x2*x8 + x2*x9)
val = -P*x15 + P*(x10*x6 - x7 + x9)*log(x1*x4) + x13*log(x1) - x13*log(x3) + x15*x2
dH_dP_integrals_Tait.append(val)
# print(dH_dP_integrals_Tait, self._dH_dP_integrals_Tait2)
return dH_dP_integrals_Tait
def mu(self):
try:
return self._mu
except AttributeError:
pass
mu = self._mu = self.correlations.ViscosityLiquidMixture.mixture_property(self.T, self.P, self.zs, self.ws())
return mu
def k(self):
try:
return self._k
except AttributeError:
pass
self._k = k = self.correlations.ThermalConductivityLiquidMixture.mixture_property(self.T, self.P, self.zs, self.ws())
return k
class GibbsExcessSolid(GibbsExcessLiquid):
ideal_gas_basis = True
force_phase = 's'
phase = 's'
is_gas = False
is_liquid = False
is_solid = True
pure_references = ('HeatCapacityGases','SublimationPressures', 'VolumeSolids', 'EnthalpySublimations')
pure_reference_types = (HeatCapacityGas, SublimationPressure, VolumeSolid, EnthalpySublimation)
model_attributes = ('Hfs', 'Gfs', 'Sfs','GibbsExcessModel',
'eos_pure_instances', 'use_Poynting', 'use_phis_sat',
'use_eos_volume', 'henry_components',
'henry_as', 'henry_bs', 'henry_cs', 'henry_ds', 'henry_es', 'henry_fs',
'Psat_extrpolation') + pure_references
def __init__(self, SublimationPressures, VolumeSolids=None,
GibbsExcessModel=IdealSolution,
eos_pure_instances=None,
VolumeLiquidMixture=None,
HeatCapacityGases=None,
EnthalpySublimations=None,
use_Poynting=False,
use_phis_sat=False,
Hfs=None, Gfs=None, Sfs=None,
T=Phase.T_DEFAULT, P=Phase.P_DEFAULT, zs=None,
):
super().__init__(VaporPressures=SublimationPressures, VolumeLiquids=VolumeSolids,
HeatCapacityGases=HeatCapacityGases, EnthalpyVaporizations=EnthalpySublimations,
use_Poynting=use_Poynting,
Hfs=Hfs, Gfs=Gfs, Sfs=Sfs, T=T, P=P, zs=zs)