'''Chemical Engineering Design Library (ChEDL). Utilities for process modeling.
Copyright (C) 2016, 2017, 2018, 2019 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__ = ['Chemical', 'reference_states']
from chemicals.acentric import Stiel_polar_factor, omega, omega_methods
from chemicals.combustion import HHV_stoichiometry, LHV_from_HHV, combustion_stoichiometry
from chemicals.critical import Pc, Pc_methods, Tc, Tc_methods, Vc, Vc_methods
from chemicals.dipole import dipole_moment as dipole
from chemicals.dipole import dipole_moment_methods
from chemicals.elements import (
atom_fractions,
atoms_to_Hill,
charge_from_formula,
homonuclear_elements,
mass_fractions,
molecular_weight,
periodic_table,
similarity_variable,
simple_formula_parser,
)
from chemicals.environment import GWP, ODP, GWP_methods, ODP_methods, logP, logP_methods
from chemicals.identifiers import search_chemical
from chemicals.lennard_jones import Stockmayer, Stockmayer_methods, molecular_diameter, molecular_diameter_methods
from chemicals.phase_change import Hfus, Hfus_methods, Tb, Tb_methods, Tm, Tm_methods
from chemicals.reaction import Gibbs_formation, Hf_basis_converter, Hfg, Hfg_methods, Hfl, Hfl_methods, Hfs, Hfs_methods, S0g, S0g_methods
from chemicals.refractivity import RI, RI_methods
from chemicals.safety import (
LFL,
STEL,
TWA,
UFL,
Carcinogen,
Ceiling,
Ceiling_methods,
LFL_methods,
Skin,
Skin_methods,
STEL_methods,
T_autoignition,
T_autoignition_methods,
T_flash,
T_flash_methods,
TWA_methods,
UFL_methods,
)
from chemicals.solubility import solubility_parameter
from chemicals.triple import Pt, Pt_methods, Tt, Tt_methods
from chemicals.utils import SG, Joule_Thomson, Parachor, R, SG_to_API, Vm_to_rho, Z, isentropic_exponent, isobaric_expansion, property_molar_to_mass
from chemicals.virial import B_from_Z
from chemicals.volume import ideal_gas
from fluids.constants import epsilon_0
from fluids.core import Bond, Capillary, Grashof, Jakob, Peclet_heat, Prandtl, Reynolds, Weber, nu_mu_converter, thermal_diffusivity
from fluids.numerics import exp, log, secant
from thermo import functional_groups
from thermo.electrochem import conductivity, conductivity_methods
from thermo.eos import IG, PR
from thermo.functional_groups import group_names
from thermo.heat_capacity import HeatCapacityGas, HeatCapacityLiquid, HeatCapacitySolid
from thermo.interface import SurfaceTension
from thermo.law import economic_status, legal_status
from thermo.permittivity import PermittivityLiquid
from thermo.phase_change import EnthalpySublimation, EnthalpyVaporization
from thermo.thermal_conductivity import ThermalConductivityGas, ThermalConductivityLiquid, ThermalConductivitySolid
from thermo.unifac import UNIFAC_RQ, UNIFAC_group_assignment_DDBST, Van_der_Waals_area, Van_der_Waals_volume
from thermo.utils import identify_phase, phase_select_property
from thermo.vapor_pressure import SublimationPressure, VaporPressure
from thermo.viscosity import ViscosityGas, ViscosityLiquid
from thermo.volume import VolumeGas, VolumeLiquid, VolumeSolid
caching = True
# Format: (T, P, phase, H, S, molar=True)
IAPWS = (273.16, 611.655, 'l', 0.00922, 0, True) # Water; had to convert Href from mass to molar
ASHRAE = (233.15, 'Psat', 'l', 0, 0, True) # As described in REFPROP
IIR = (273.15, 'Psat', 'l', 200E3, 1000, False) # 200 kj/kg reference, as described in REFPROP
REFPROP = ('Tb', 101325, 'l', 0, 0, True)
CHEMSEP = (298., 101325, 'g', 0, 0, True) # It has an option to add Hf to the reference
PRO_II = (298.15, 101325, 'gas', 0, 0, True)
HYSYS = (298.15, 101325, 'calc', 'Hf', 0, True)
UNISIM = HYSYS
SUPERPRO = (298.15, 101325, 'calc', 0, 0, True) # No support for entropy found, 0 assumed
# note soecifying a phase works for chemicals but not mixtures.
reference_states = [IAPWS, ASHRAE, IIR, REFPROP, CHEMSEP, PRO_II, HYSYS,
UNISIM, SUPERPRO]
class ChemicalConstants:
__slots__ = ('CAS', 'Tc', 'Pc', 'Vc', 'omega', 'Tb', 'Tm', 'Tt', 'Pt',
'Hfus', 'Hsub', 'Hfg', 'dipole',
'HeatCapacityGas', 'HeatCapacityLiquid', 'HeatCapacitySolid',
'ThermalConductivityLiquid', 'ThermalConductivityGas',
'ViscosityLiquid', 'ViscosityGas',
'EnthalpyVaporization', 'VaporPressure', 'VolumeLiquid',
'EnthalpySublimation', 'SublimationPressure', 'SurfaceTension',
'VolumeSolid',
'VolumeSupercriticalLiquid', 'PermittivityLiquid', 'ThermalConductivitySolid',
)
# Or can I store the actual objects without doing the searches?
def __init__(self, CAS, Tc=None, Pc=None, Vc=None, omega=None, Tb=None,
Tm=None, Tt=None, Pt=None, Hfus=None, Hsub=None, Hfg=None,
dipole=None,
HeatCapacityGas=(), HeatCapacityLiquid=(),
HeatCapacitySolid=(),
ThermalConductivityLiquid=(), ThermalConductivityGas=(),
ViscosityLiquid=(), ViscosityGas=(),
EnthalpyVaporization=(), VaporPressure=(), VolumeLiquid=(),
SublimationPressure=(), EnthalpySublimation=(),
SurfaceTension=(), VolumeSolid=(), VolumeSupercriticalLiquid=(),
PermittivityLiquid=(),
):
self.CAS = CAS
self.Tc = Tc
self.Pc = Pc
self.Vc = Vc
self.omega = omega
self.Tb = Tb
self.Tm = Tm
self.Tt = Tt
self.Pt = Pt
self.Hfus = Hfus
self.Hsub = Hsub
self.Hfg = Hfg
self.dipole = dipole
self.HeatCapacityGas = HeatCapacityGas
self.HeatCapacityLiquid = HeatCapacityLiquid
self.HeatCapacitySolid = HeatCapacitySolid
self.ThermalConductivityLiquid = ThermalConductivityLiquid
self.ThermalConductivityGas = ThermalConductivityGas
self.ViscosityLiquid = ViscosityLiquid
self.ViscosityGas = ViscosityGas
self.EnthalpyVaporization = EnthalpyVaporization
self.EnthalpySublimation = EnthalpySublimation
self.VaporPressure = VaporPressure
self.SublimationPressure = SublimationPressure
self.VolumeLiquid = VolumeLiquid
self.SurfaceTension = SurfaceTension
self.VolumeSolid = VolumeSolid
self.VolumeSupercriticalLiquid = VolumeSupercriticalLiquid
self.PermittivityLiquid = PermittivityLiquid
self.ThermalConductivitySolid = tuple()
empty_chemical_constants = ChemicalConstants(None)
property_lock = False
def lock_properties(status):
global property_lock
if property_lock == status:
return True
else:
property_lock = status
return True
user_prop_to_default_poly = {'SurfaceTension': 'exp_poly_fit_ln_tau',
'EnthalpyVaporization': 'poly_fit_ln_tau',
'VolumeLiquid': 'poly_fit',
'VolumeSolid': 'poly_fit',
'HeatCapacityGas': 'poly_fit',
'HeatCapacitySolid': 'poly_fit',
'HeatCapacityLiquid': 'poly_fit',
'EnthalpySublimation': 'poly_fit',
'ViscosityGas': 'poly_fit',
'ThermalConductivityLiquid': 'poly_fit',
'ThermalConductivityGas': 'poly_fit',
'PermittivityLiquid': 'poly_fit',
'VaporPressure': 'exp_poly_fit',
'SublimationPressure': 'exp_poly_fit',
'ViscosityLiquid': 'exp_poly_fit'}
def user_chemical_property_lookup(CAS, key):
if loaded_user_dbs:
for db in loaded_user_dbs:
try:
return db[CAS][key]
except KeyError:
continue
return {}
# legacy method
if not property_lock:
return {}
from thermo.database import loaded_chemicals
try:
vs = getattr(loaded_chemicals[CAS], key)
if all(i is not None for i in vs):
return {user_prop_to_default_poly[key]: vs}
return {}
except KeyError:
return {}
loaded_user_dbs = []
loaded_user_db_paths = []
def set_user_chemical_property_databases(paths):
loaded_user_dbs.clear()
loaded_user_db_paths.clear()
import json
for path in paths:
string = open(path)
regression_data = json.load(string)
string.close()
loaded_user_dbs.append(regression_data)
loaded_user_db_paths.append(path)
class UserDatabaseContext:
def __init__(self, paths):
self.redundant_call = paths == loaded_user_db_paths
if not self.redundant_call:
self.old_loaded_user_dbs = loaded_user_dbs.copy()
self.old_loaded_user_db_paths = loaded_user_db_paths.copy()
set_user_chemical_property_databases(paths)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
global loaded_user_dbs, loaded_user_db_paths
if not self.redundant_call:
loaded_user_dbs = self.old_loaded_user_dbs
loaded_user_db_paths = self.old_loaded_user_db_paths
return exc_type is None
[docs]class Chemical: # pragma: no cover
'''Creates a Chemical object which contains basic information such as
molecular weight and the structure of the species, as well as thermodynamic
and transport properties as a function of temperature and pressure.
Parameters
----------
ID : str
One of the following [-]:
* Name, in IUPAC form or common form or a synonym registered in PubChem
* InChI name, prefixed by 'InChI=1S/' or 'InChI=1/'
* InChI key, prefixed by 'InChIKey='
* PubChem CID, prefixed by 'PubChem='
* SMILES (prefix with 'SMILES=' to ensure smiles parsing)
* CAS number
T : float, optional
Temperature of the chemical (default 298.15 K), [K]
P : float, optional
Pressure of the chemical (default 101325 Pa) [Pa]
Examples
--------
Creating chemical objects:
>>> Chemical('hexane')
<Chemical [hexane], T=298.15 K, P=101325 Pa>
>>> Chemical('CCCCCCCC', T=500, P=1E7)
<Chemical [octane], T=500.00 K, P=10000000 Pa>
>>> Chemical('7440-36-0', P=1000)
<Chemical [antimony], T=298.15 K, P=1000 Pa>
Getting basic properties:
>>> N2 = Chemical('Nitrogen')
>>> N2.Tm, N2.Tb, N2.Tc # melting, boiling, and critical points [K]
(63.15, 77.355, 126.2)
>>> N2.Pt, N2.Pc # sublimation and critical pressure [Pa]
(12526.9697368421, 3394387.5)
>>> N2.CAS, N2.formula, N2.InChI, N2.smiles, N2.atoms # CAS number, formula, InChI string, smiles string, dictionary of atomic elements and their count
('7727-37-9', 'N2', 'N2/c1-2', 'N#N', {'N': 2})
Changing the T/P of the chemical, and gettign temperature-dependent
properties:
>>> N2.Cp, N2.rho, N2.mu # Heat capacity [J/kg/K], density [kg/m^3], viscosity [Pa*s]
(1039., 1.14, 1.78e-05)
>>> N2.calculate(T=65, P=1E6) # set it to a liquid at 65 K and 1 MPa
>>> N2.phase
'l'
>>> N2.Cp, N2.rho, N2.mu # properties are now of the liquid phase
(2002., 861., 0.000285)
Molar units are also available for properties:
>>> N2.Cpm, N2.Vm, N2.Hvapm # heat capacity [J/mol/K], molar volume [m^3/mol], enthalpy of vaporization [J/mol]
(56., 3.25e-05, 5982.)
A great deal of properties are available; for a complete list look at the
attributes list.
>>> N2.alpha, N2.JT # thermal diffusivity [m^2/s], Joule-Thompson coefficient [K/Pa]
(9.87e-08, -4.0e-07)
>>> N2.isentropic_exponent, N2.isobaric_expansion
(1.4, 0.0047)
For pure species, the phase is easily identified, allowing for properties
to be obtained without needing to specify the phase. However, the
properties are also available in the hypothetical gas phase (when under the
boiling point) and in the hypothetical liquid phase (when above the boiling
point) as these properties are needed to evaluate mixture properties.
Specify the phase of a property to be retrieved by appending 'l' or 'g' or
's' to the property.
>>> C50 = Chemical('pentacontane')
>>> C50.rhog, C50.Cpg, C50.kg, C50.mug
(4.241646701894199, 1126.5533755283168, 0.00941385692301755, 6.973325939594919e-06)
Temperature dependent properties are calculated by objects which provide
many useful features related to the properties. To determine the
temperature at which nitrogen has a saturation pressure of 1 MPa:
>>> N2.VaporPressure.solve_property(1E6)
103.73528598652341
To compute an integral of the ideal-gas heat capacity of nitrogen
to determine the enthalpy required for a given change in temperature.
Note the thermodynamic objects calculate values in molar units always.
>>> N2.HeatCapacityGas.T_dependent_property_integral(100, 120) # J/mol/K
582.0121860897898
Derivatives of properties can be calculated as well, as may be needed by
for example heat transfer calculations:
>>> N2.SurfaceTension.T_dependent_property_derivative(77)
-0.00022695346296730534
If a property is needed at multiple temperatures or pressures, it is faster
to use the object directly to perform the calculation rather than setting
the conditions for the chemical.
>>> [N2.VaporPressure(T) for T in range(80, 120, 10)]
[136979.4840843189, 360712.5746603142, 778846.276691705, 1466996.7208525643]
These objects are also how the methods by which the properties are
calculated can be changed. To see the available methods for a property:
>>> N2.VaporPressure.all_methods
set(['VDI_PPDS', 'BOILING_CRITICAL', 'WAGNER_MCGARRY', 'AMBROSE_WALTON', 'COOLPROP', 'LEE_KESLER_PSAT', 'EOS', 'ANTOINE_POLING', 'SANJARI', 'DIPPR_PERRY_8E', 'Edalat', 'WAGNER_POLING'])
To specify the method which should be used for calculations of a property.
In the example below, the Lee-kesler correlation for vapor pressure is
specified.
>>> N2.calculate(80)
>>> N2.Psat
136979.4840843189
>>> N2.VaporPressure.method = 'LEE_KESLER_PSAT'
>>> N2.Psat
134987.76815364443
Properties may also be plotted via these objects:
>>> N2.VaporPressure.plot_T_dependent_property() # doctest: +SKIP
>>> N2.VolumeLiquid.plot_isotherm(T=77, Pmin=1E5, Pmax=1E7) # doctest: +SKIP
>>> N2.VolumeLiquid.plot_isobar(P=1E6, Tmin=66, Tmax=120) # doctest: +SKIP
>>> N2.VolumeLiquid.plot_TP_dependent_property(Tmin=60, Tmax=100, Pmin=1E5, Pmax=1E7) # doctest: +SKIP
Notes
-----
.. warning::
The Chemical class is not designed for high-performance or the ability
to use different thermodynamic models. It is especially limited in its
multiphase support and the ability to solve with specifications other
than temperature and pressure. It is impossible to change constant
properties such as a compound's critical temperature in this interface.
It is recommended to switch over to the :obj:`thermo.flash` interface
which solves those problems and is better positioned to grow. That
interface also requires users to be responsible for their chemical
constants and pure component correlations; while default values can
easily be loaded for most compounds, the user is ultimately responsible
for them.
Attributes
----------
T : float
Temperature of the chemical, [K]
P : float
Pressure of the chemical, [Pa]
phase : str
Phase of the chemical; one of 's', 'l', 'g', or 'l/g'.
ID : str
User specified string by which the chemical's CAS was looked up.
CAS : str
The CAS number of the chemical.
PubChem : int
PubChem Compound identifier (CID) of the chemical; all chemicals are
sourced from their database. Chemicals can be looked at online at
`<https://pubchem.ncbi.nlm.nih.gov>`_.
MW : float
Molecular weight of the compound, [g/mol]
formula : str
Molecular formula of the compound.
atoms : dict
dictionary of counts of individual atoms, indexed by symbol with
proper capitalization, [-]
similarity_variable : float
Similarity variable, see :obj:`chemicals.elements.similarity_variable`
for the definition, [mol/g]
smiles : str
Simplified molecular-input line-entry system representation of the
compound.
InChI : str
IUPAC International Chemical Identifier of the compound.
InChI_Key : str
25-character hash of the compound's InChI.
IUPAC_name : str
Preferred IUPAC name for a compound.
synonyms : list of strings
All synonyms for the compound found in PubChem, sorted by popularity.
Tm : float
Melting temperature [K]
Tb : float
Boiling temperature [K]
Tc : float
Critical temperature [K]
Pc : float
Critical pressure [Pa]
Vc : float
Critical volume [m^3/mol]
Zc : float
Critical compressibility [-]
rhoc : float
Critical density [kg/m^3]
rhocm : float
Critical molar density [mol/m^3]
omega : float
Acentric factor [-]
StielPolar : float
Stiel Polar factor, see :obj:`chemicals.acentric.Stiel_polar_factor` for
the definition [-]
Tt : float
Triple temperature, [K]
Pt : float
Triple pressure, [Pa]
Hfus : float
Enthalpy of fusion [J/kg]
Hfusm : float
Molar enthalpy of fusion [J/mol]
Hsub : float
Enthalpy of sublimation [J/kg]
Hsubm : float
Molar enthalpy of sublimation [J/mol]
Hfm : float
Standard state molar enthalpy of formation, [J/mol]
Hf : float
Standard enthalpy of formation in a mass basis, [J/kg]
Hfgm : float
Ideal-gas molar enthalpy of formation, [J/mol]
Hfg : float
Ideal-gas enthalpy of formation in a mass basis, [J/kg]
Hcm : float
Molar higher heat of combustion [J/mol]
Hc : float
Higher Heat of combustion [J/kg]
Hcm_lower : float
Molar lower heat of combustion [J/mol]
Hc_lower : float
Lower Heat of combustion [J/kg]
S0m : float
Standard state absolute molar entropy of the chemical, [J/mol/K]
S0 : float
Standard state absolute entropy of the chemical, [J/kg/K]
S0gm : float
Absolute molar entropy in an ideal gas state of the chemical, [J/mol/K]
S0g : float
Absolute mass entropy in an ideal gas state of the chemical, [J/kg/K]
Gfm : float
Standard state molar change of Gibbs energy of formation [J/mol]
Gf : float
Standard state change of Gibbs energy of formation [J/kg]
Gfgm : float
Ideal-gas molar change of Gibbs energy of formation [J/mol]
Gfg : float
Ideal-gas change of Gibbs energy of formation [J/kg]
Sfm : float
Standard state molar change of entropy of formation, [J/mol/K]
Sf : float
Standard state change of entropy of formation, [J/kg/K]
Sfgm : float
Ideal-gas molar change of entropy of formation, [J/mol/K]
Sfg : float
Ideal-gas change of entropy of formation, [J/kg/K]
Hcgm : float
Higher molar heat of combustion of the chemical in the ideal gas state,
[J/mol]
Hcg : float
Higher heat of combustion of the chemical in the ideal gas state,
[J/kg]
Hcgm_lower : float
Lower molar heat of combustion of the chemical in the ideal gas state,
[J/mol]
Hcg_lower : float
Lower heat of combustion of the chemical in the ideal gas state,
[J/kg]
Tflash : float
Flash point of the chemical, [K]
Tautoignition : float
Autoignition point of the chemical, [K]
LFL : float
Lower flammability limit of the gas in an atmosphere at STP, mole
fraction [-]
UFL : float
Upper flammability limit of the gas in an atmosphere at STP, mole
fraction [-]
TWA : tuple[quantity, unit]
Time-Weighted Average limit on worker exposure to dangerous chemicals.
STEL : tuple[quantity, unit]
Short-term Exposure limit on worker exposure to dangerous chemicals.
Ceiling : tuple[quantity, unit]
Ceiling limits on worker exposure to dangerous chemicals.
Skin : bool
Whether or not a chemical can be absorbed through the skin.
Carcinogen : str or dict
Carcinogen status information.
dipole : float
Dipole moment in debye, [3.33564095198e-30 ampere*second^2]
Stockmayer : float
Lennard-Jones depth of potential-energy minimum over k, [K]
molecular_diameter : float
Lennard-Jones molecular diameter, [angstrom]
GWP : float
Global warming potential (default 100-year outlook) (impact/mass
chemical)/(impact/mass CO2), [-]
ODP : float
Ozone Depletion potential (impact/mass chemical)/(impact/mass CFC-11),
[-]
logP : float
Octanol-water partition coefficient, [-]
legal_status : str or dict
Legal status information [-]
economic_status : list
Economic status information [-]
RI : float
Refractive Index on the Na D line, [-]
RIT : float
Temperature at which refractive index reading was made
conductivity : float
Electrical conductivity of the fluid, [S/m]
conductivityT : float
Temperature at which conductivity measurement was made
VaporPressure : object
Instance of :obj:`thermo.vapor_pressure.VaporPressure`, with data and
methods loaded for the chemical; performs the actual calculations of
vapor pressure of the chemical.
EnthalpyVaporization : object
Instance of :obj:`thermo.phase_change.EnthalpyVaporization`, with data
and methods loaded for the chemical; performs the actual calculations
of molar enthalpy of vaporization of the chemical.
VolumeSolid : object
Instance of :obj:`thermo.volume.VolumeSolid`, with data and methods
loaded for the chemical; performs the actual calculations of molar
volume of the solid phase of the chemical.
VolumeLiquid : object
Instance of :obj:`thermo.volume.VolumeLiquid`, with data and methods
loaded for the chemical; performs the actual calculations of molar
volume of the liquid phase of the chemical.
VolumeGas : object
Instance of :obj:`thermo.volume.VolumeGas`, with data and methods
loaded for the chemical; performs the actual calculations of molar
volume of the gas phase of the chemical.
HeatCapacitySolid : object
Instance of :obj:`thermo.heat_capacity.HeatCapacitySolid`, with data and
methods loaded for the chemical; performs the actual calculations of
molar heat capacity of the solid phase of the chemical.
HeatCapacityLiquid : object
Instance of :obj:`thermo.heat_capacity.HeatCapacityLiquid`, with data and
methods loaded for the chemical; performs the actual calculations of
molar heat capacity of the liquid phase of the chemical.
HeatCapacityGas : object
Instance of :obj:`thermo.heat_capacity.HeatCapacityGas`, with data and
methods loaded for the chemical; performs the actual calculations of
molar heat capacity of the gas phase of the chemical.
ViscosityLiquid : object
Instance of :obj:`thermo.viscosity.ViscosityLiquid`, with data and
methods loaded for the chemical; performs the actual calculations of
viscosity of the liquid phase of the chemical.
ViscosityGas : object
Instance of :obj:`thermo.viscosity.ViscosityGas`, with data and
methods loaded for the chemical; performs the actual calculations of
viscosity of the gas phase of the chemical.
ThermalConductivityLiquid : object
Instance of :obj:`thermo.thermal_conductivity.ThermalConductivityLiquid`,
with data and methods loaded for the chemical; performs the actual
calculations of thermal conductivity of the liquid phase of the
chemical.
ThermalConductivityGas : object
Instance of :obj:`thermo.thermal_conductivity.ThermalConductivityGas`,
with data and methods loaded for the chemical; performs the actual
calculations of thermal conductivity of the gas phase of the chemical.
SurfaceTension : object
Instance of :obj:`thermo.interface.SurfaceTension`, with data and
methods loaded for the chemical; performs the actual calculations of
surface tension of the chemical.
Permittivity : object
Instance of :obj:`thermo.permittivity.PermittivityLiquid`, with data and
methods loaded for the chemical; performs the actual calculations of
permittivity of the chemical.
Psat_298 : float
Vapor pressure of the chemical at 298.15 K, [Pa]
phase_STP : str
Phase of the chemical at 298.15 K and 101325 Pa; one of 's', 'l', 'g',
or 'l/g'.
Vml_Tb : float
Molar volume of liquid phase at the normal boiling point [m^3/mol]
Vml_Tm : float
Molar volume of liquid phase at the melting point [m^3/mol]
Vml_STP : float
Molar volume of liquid phase at 298.15 K and 101325 Pa [m^3/mol]
rhoml_STP : float
Molar density of liquid phase at 298.15 K and 101325 Pa [mol/m^3]
Vmg_STP : float
Molar volume of gas phase at 298.15 K and 101325 Pa according to
the ideal gas law, [m^3/mol]
Vms_Tm : float
Molar volume of solid phase at the melting point [m^3/mol]
rhos_Tm : float
Mass density of solid phase at the melting point [kg/m^3]
Hvap_Tbm : float
Molar enthalpy of vaporization at the normal boiling point [J/mol]
Hvap_Tb : float
Mass enthalpy of vaporization at the normal boiling point [J/kg]
Hvapm_298 : float
Molar enthalpy of vaporization at 298.15 K [J/mol]
Hvap_298 : float
Mass enthalpy of vaporization at 298.15 K [J/kg]
alpha
alphag
alphal
API
aromatic_rings
atom_fractions
Bvirial
charge
Cp
Cpg
Cpgm
Cpl
Cplm
Cpm
Cps
Cpsm
Cvg
Cvgm
eos
Hill
Hvap
Hvapm
isentropic_exponent
isobaric_expansion
isobaric_expansion_g
isobaric_expansion_l
JT
JTg
JTl
k
kg
kl
mass_fractions
mu
mug
mul
nu
nug
nul
Parachor
permittivity
Poynting
Pr
Prg
Prl
Psat
PSRK_groups
rdkitmol
rdkitmol_Hs
rho
rhog
rhogm
rhol
rholm
rhom
rhos
rhosm
rings
SG
SGg
SGl
SGs
sigma
solubility_parameter
UNIFAC_Dortmund_groups
UNIFAC_groups
UNIFAC_R
UNIFAC_Q
Van_der_Waals_area
Van_der_Waals_volume
Vm
Vmg
Vml
Vms
Z
Zg
Zl
Zs
'''
__atom_fractions = None
__mass_fractions = None
__rdkitmol = None
__rdkitmol_Hs = None
__Hill = None
__legal_status = None
__economic_status = None
def __repr__(self):
return f'<Chemical [{self.name}], T={self.T:.2f} K, P={self.P:.0f} Pa>'
def __init__(self, ID, T=298.15, P=101325, autocalc=True):
if isinstance(ID, dict):
self.CAS = ID['CASRN']
self.ID = self.name = ID['name']
self.formula = ID['formula']
# DO NOT REMOVE molecular_weight until the database gets updated with consistent MWs
self.MW = ID['MW'] if 'MW' in ID else molecular_weight(simple_formula_parser(self.formula))
self.PubChem = ID.get('PubChem', None)
self.smiles = ID.get('smiles', None)
self.InChI = ID.get('InChI', None)
self.InChI_Key = ID.get('InChI_Key', None)
self.synonyms = ID.get('synonyms', None)
else:
self.ID = ID
# Identification
self.ChemicalMetadata = search_chemical(ID)
self.CAS = self.ChemicalMetadata.CASs
self.autocalc = autocalc
if not isinstance(ID, dict):
self.PubChem = self.ChemicalMetadata.pubchemid
self.formula = self.ChemicalMetadata.formula
self.MW = molecular_weight(simple_formula_parser(self.formula)) # self.ChemicalMetadata.MW
self.smiles = self.ChemicalMetadata.smiles
self.InChI = self.ChemicalMetadata.InChI
self.InChI_Key = self.ChemicalMetadata.InChI_key
self.IUPAC_name = self.ChemicalMetadata.iupac_name.lower()
self.name = self.ChemicalMetadata.common_name.lower()
self.synonyms = self.ChemicalMetadata.synonyms
self.atoms = simple_formula_parser(self.formula)
self.similarity_variable = similarity_variable(self.atoms, self.MW)
self.eos_in_a_box = []
self.set_constant_sources()
self.set_constants()
self.set_eos(T=T, P=P)
self.set_TP_sources()
if self.autocalc:
self.set_ref()
self.calculate(T, P)
[docs] def calculate(self, T=None, P=None):
if (hasattr(self, 'T') and T == self.T and hasattr(self, 'P') and P == self.P):
return None
if T:
if T < 0:
raise ValueError('Negative value specified for Chemical temperature - aborting!')
self.T = T
if P:
if P < 0:
raise ValueError('Negative value specified for Chemical pressure - aborting!')
self.P = P
if self.autocalc:
self.phase = identify_phase(T=self.T, P=self.P, Tm=self.Tm, Tb=self.Tb, Tc=self.Tc, Psat=self.Psat)
self.eos = self.eos.to_TP(T=self.T, P=self.P)
self.eos_in_a_box[0] = self.eos
self.set_thermo()
[docs] def draw_2d(self, width=300, height=300, Hs=False): # pragma: no cover
r'''Interface for drawing a 2D image of the molecule.
Requires an HTML5 browser, and the libraries RDKit and
IPython. An exception is raised if either of these libraries is
absent.
Parameters
----------
width : int
Number of pixels wide for the view
height : int
Number of pixels tall for the view
Hs : bool
Whether or not to show hydrogen
Examples
--------
>>> Chemical('decane').draw_2d() # doctest: +ELLIPSIS
<PIL.PngImagePlugin.PngImageFile image mode=RGB size=300x300 at 0x...>
'''
try:
from rdkit.Chem import Draw
if Hs:
mol = self.rdkitmol_Hs
else:
mol = self.rdkitmol
return Draw.MolToImage(mol, size=(width, height))
except:
return 'Rdkit is required for this feature.'
[docs] def draw_3d(self, width=300, height=500, style='stick', Hs=True,
atom_labels=True): # pragma: no cover
r'''Interface for drawing an interactive 3D view of the molecule.
Requires an HTML5 browser, and the libraries RDKit, pymol3D, and
IPython. An exception is raised if all three of these libraries are
not installed.
Parameters
----------
width : int
Number of pixels wide for the view, [pixels]
height : int
Number of pixels tall for the view, [pixels]
style : str
One of 'stick', 'line', 'cross', or 'sphere', [-]
Hs : bool
Whether or not to show hydrogen, [-]
atom_labels : bool
Whether or not to label the atoms, [-]
Examples
--------
>>> Chemical('cubane').draw_3d()
None
'''
try:
import py3Dmol
from IPython.display import display
from rdkit import Chem
from rdkit.Chem import AllChem
if Hs:
mol = self.rdkitmol_Hs
else:
mol = self.rdkitmol
AllChem.EmbedMultipleConfs(mol)
mb = Chem.MolToMolBlock(mol)
p = py3Dmol.view(width=width, height=height)
p.addModel(mb,'sdf')
p.setStyle({style:{}})
if atom_labels:
p.addPropertyLabels("atom","",{'alignment': 'center'})
p.zoomTo()
display(p.show())
except:
return 'py3Dmol, RDKit, and IPython are required for this feature.'
[docs] def set_constant_sources(self):
self.Tm_sources = Tm_methods(CASRN=self.CAS)
self.Tm_source = self.Tm_sources[0] if self.Tm_sources else None
self.Tb_sources = Tb_methods(CASRN=self.CAS)
self.Tb_source = self.Tb_sources[0] if self.Tb_sources else None
# Critical Point
self.Tc_methods = Tc_methods(self.CAS)
self.Tc_method = self.Tc_methods[0] if self.Tc_methods else None
self.Pc_methods = Pc_methods(self.CAS)
self.Pc_method = self.Pc_methods[0] if self.Pc_methods else None
self.Vc_methods = Vc_methods(self.CAS)
self.Vc_method = self.Vc_methods[0] if self.Vc_methods else None
self.omega_methods = omega_methods(self.CAS)
self.omega_method = self.omega_methods[0] if self.omega_methods else None
# Triple point
self.Tt_sources = Tt_methods(self.CAS)
self.Tt_source = self.Tt_sources[0] if self.Tt_sources else None
self.Pt_sources = Pt_methods(self.CAS)
self.Pt_source = self.Pt_sources[0] if self.Pt_sources else None
# Enthalpy
self.Hfus_methods = Hfus_methods(CASRN=self.CAS)
self.Hfus_method = self.Hfus_methods[0] if self.Hfus_methods else None
# Fire Safety Limits
self.Tflash_sources = T_flash_methods(self.CAS)
self.Tflash_source = self.Tflash_sources[0] if self.Tflash_sources else None
self.Tautoignition_sources = T_autoignition_methods(self.CAS)
self.Tautoignition_source = self.Tautoignition_sources[0] if self.Tautoignition_sources else None
# Chemical Exposure Limits
self.TWA_sources = TWA_methods(self.CAS)
self.TWA_source = self.TWA_sources[0] if self.TWA_sources else None
self.STEL_sources = STEL_methods(self.CAS)
self.STEL_source = self.STEL_sources[0] if self.STEL_sources else None
self.Ceiling_sources = Ceiling_methods(self.CAS)
self.Ceiling_source = self.Ceiling_sources[0] if self.Ceiling_sources else None
self.Skin_sources = Skin_methods(self.CAS)
self.Skin_source = self.Skin_sources[0] if self.Skin_sources else None
# self.Carcinogen_sources = Carcinogen_methods(self.CAS)
# self.Carcinogen_source = self.Carcinogen_sources[0] if self.Carcinogen_sources else None
self.Hfg_sources = Hfg_methods(CASRN=self.CAS)
self.Hfg_source = self.Hfg_sources[0] if self.Hfg_sources else None
self.S0g_sources = S0g_methods(CASRN=self.CAS)
self.S0g_source = self.S0g_sources[0] if self.S0g_sources else None
# Misc
self.dipole_sources = dipole_moment_methods(CASRN=self.CAS)
self.dipole_source = self.dipole_sources[0] if self.dipole_sources else None
# Environmental
self.GWP_sources = GWP_methods(CASRN=self.CAS)
self.GWP_source = self.GWP_sources[0] if self.GWP_sources else None
self.ODP_sources = ODP_methods(CASRN=self.CAS)
self.ODP_source = self.ODP_sources[0] if self.ODP_sources else None
self.logP_sources = logP_methods(CASRN=self.CAS)
self.logP_source = self.logP_sources[0] if self.logP_sources else None
# Analytical
self.RI_sources = RI_methods(CASRN=self.CAS)
self.RI_source = self.RI_sources[0] if self.RI_sources else None
self.conductivity_sources = conductivity_methods(CASRN=self.CAS)
self.conductivity_source = self.conductivity_sources[0] if self.conductivity_sources else None
[docs] def set_constants(self):
self.Tm = Tm(self.CAS, method=self.Tm_source)
self.Tb = Tb(self.CAS, method=self.Tb_source)
# Critical Point
self.Tc = Tc(self.CAS, method=self.Tc_method)
self.Pc = Pc(self.CAS, method=self.Pc_method)
self.Vc = Vc(self.CAS, method=self.Vc_method)
self.omega = omega(self.CAS, method=self.omega_method)
self.Zc = Z(self.Tc, self.Pc, self.Vc) if all((self.Tc, self.Pc, self.Vc)) else None
self.rhoc = Vm_to_rho(self.Vc, self.MW) if self.Vc else None
self.rhocm = 1./self.Vc if self.Vc else None
# Triple point
self.Pt = Pt(self.CAS, method=self.Pt_source)
self.Tt = Tt(self.CAS, method=self.Tt_source)
# Enthalpy
self.Hfusm = Hfus(method=self.Hfus_method, CASRN=self.CAS)
self.Hfus = property_molar_to_mass(self.Hfusm, self.MW) if self.Hfusm is not None else None
# Chemical Exposure Limits
self.TWA = TWA(self.CAS, method=self.TWA_source)
self.STEL = STEL(self.CAS, method=self.STEL_source)
self.Ceiling = Ceiling(self.CAS, method=self.Ceiling_source)
self.Skin = Skin(self.CAS, method=self.Skin_source)
self.Carcinogen = Carcinogen(self.CAS)
# Misc
self.dipole = dipole(self.CAS, method=self.dipole_source) # Units of Debye
self.Stockmayer_sources = Stockmayer_methods(Tc=self.Tc, Zc=self.Zc, omega=self.omega, CASRN=self.CAS)
self.Stockmayer_source = self.Stockmayer_sources[0] if self.Stockmayer_sources else None
self.Stockmayer = Stockmayer(Tm=self.Tm, Tb=self.Tb, Tc=self.Tc, Zc=self.Zc, omega=self.omega, method=self.Stockmayer_source, CASRN=self.CAS)
# Environmental
self.GWP = GWP(CASRN=self.CAS, method=self.GWP_source)
self.ODP = ODP(CASRN=self.CAS, method=self.ODP_source)
self.logP = logP(CASRN=self.CAS, method=self.logP_source)
# Analytical
self.RI, self.RIT = RI(CASRN=self.CAS, method=self.RI_source)
self.conductivity, self.conductivityT = conductivity(CASRN=self.CAS, method=self.conductivity_source)
[docs] def set_eos(self, T, P, eos=PR):
try:
self.eos = eos(T=T, P=P, Tc=self.Tc, Pc=self.Pc, omega=self.omega)
except:
# Handle overflow errors and so on
self.eos = IG(T=T, P=P)
@property
def eos(self):
r'''Equation of state object held by the chemical; used to calculate
excess thermodynamic quantities, and also provides a vapor pressure
curve, enthalpy of vaporization curve, fugacity, thermodynamic partial
derivatives, and more; see :obj:`thermo.eos` for a full listing.
Examples
--------
>>> Chemical('methane').eos.V_g
0.0244
'''
return self.eos_in_a_box[0]
@eos.setter
def eos(self, eos):
if self.eos_in_a_box:
self.eos_in_a_box.pop()
# Pass this mutable list to objects so if it is changed, it gets
# changed in the property method too
self.eos_in_a_box.append(eos)
[docs] def set_TP_sources(self):
# Tempearture and Pressure Denepdence
# Get and choose initial methods
self.VaporPressure = VaporPressure(Tb=self.Tb, Tc=self.Tc, Pc=self.Pc,
omega=self.omega, CASRN=self.CAS,
eos=self.eos_in_a_box,
**user_chemical_property_lookup(self.CAS, 'VaporPressure'))
self.Psat_298 = self.VaporPressure.T_dependent_property(298.15)
self.phase_STP = identify_phase(T=298.15, P=101325., Tm=self.Tm, Tb=self.Tb, Tc=self.Tc, Psat=self.Psat_298)
if self.Pt is None and self.Tt is not None:
self.Pt = self.VaporPressure(self.Tt)
self.Pt_source = 'VaporPressure'
# Chemistry
if self.phase_STP == 'g':
H_fun = Hfg
H_methods_fun = Hfg_methods
elif self.phase_STP == 'l':
H_fun = Hfl
H_methods_fun = Hfl_methods
elif self.phase_STP == 's':
H_fun = Hfs
H_methods_fun = Hfs_methods
else:
H_methods_fun = H_fun = None
if H_fun is not None:
self.Hf_sources = H_methods_fun(CASRN=self.CAS)
self.Hf_source = self.Hf_sources[0] if self.Hf_sources else None
self.Hfm = H_fun(CASRN=self.CAS, method=self.Hf_source)
else:
self.Hf_sources = []
self.Hf_source = self.Hfm = None
self.Hf = property_molar_to_mass(self.Hfm, self.MW) if (self.Hfm is not None) else None
self.combustion_stoichiometry = combustion_stoichiometry(self.atoms)
try:
self.Hcm = HHV_stoichiometry(self.combustion_stoichiometry, Hf=self.Hfm) if self.Hfm is not None else None
except:
self.Hcm = None
self.Hc = property_molar_to_mass(self.Hcm, self.MW) if (self.Hcm is not None) else None
self.Hcm_lower = LHV_from_HHV(self.Hcm, self.combustion_stoichiometry.get('H2O', 0.0)) if self.Hcm is not None else None
self.Hc_lower = property_molar_to_mass(self.Hcm_lower, self.MW) if (self.Hcm_lower is not None) else None
# Fire Safety Limits
self.Tflash = T_flash(self.CAS, method=self.Tflash_source)
self.Tautoignition = T_autoignition(self.CAS, method=self.Tautoignition_source)
self.LFL_sources = LFL_methods(atoms=self.atoms, Hc=self.Hcm, CASRN=self.CAS)
self.LFL_source = self.LFL_sources[0]
self.UFL_sources = UFL_methods(atoms=self.atoms, Hc=self.Hcm, CASRN=self.CAS)
self.UFL_source = self.UFL_sources[0]
try:
self.LFL = LFL(atoms=self.atoms, Hc=self.Hcm, CASRN=self.CAS, method=self.LFL_source)
except:
self.LFL = None
try:
self.UFL = UFL(atoms=self.atoms, Hc=self.Hcm, CASRN=self.CAS, method=self.UFL_source)
except:
self.UFL = None
self.Hfgm = Hfg(CASRN=self.CAS, method=self.Hfg_source)
self.Hfg = property_molar_to_mass(self.Hfgm, self.MW) if (self.Hfgm is not None) else None
self.S0gm = S0g(CASRN=self.CAS, method=self.S0g_source)
self.S0g = property_molar_to_mass(self.S0gm, self.MW) if (self.S0gm is not None) else None
# Calculated later
self.S0m = None
self.S0 = None
# Compute Gf and Gf(ig)
dHfs_std = []
S0_abs_elements = []
coeffs_elements = []
for atom, count in self.atoms.items():
try:
ele = periodic_table[atom]
H0, S0 = ele.Hf, ele.S0
if ele.number in homonuclear_elements:
H0, S0 = 0.5 * H0, 0.5 * S0
except KeyError:
H0, S0 = None, None # D, T
dHfs_std.append(H0)
S0_abs_elements.append(S0)
coeffs_elements.append(count)
self.elemental_reaction_data = (dHfs_std, S0_abs_elements, coeffs_elements)
try:
self.Gfgm = Gibbs_formation(self.Hfgm, self.S0gm, dHfs_std, S0_abs_elements, coeffs_elements)
except:
self.Gfgm = None
self.Gfg = property_molar_to_mass(self.Gfgm, self.MW) if (self.Gfgm is not None) else None
# Compute Entropy of formation
self.Sfgm = (self.Hfgm - self.Gfgm)/298.15 if (self.Hfgm is not None and self.Gfgm is not None) else None # hardcoded
self.Sfg = property_molar_to_mass(self.Sfgm, self.MW) if (self.Sfgm is not None) else None
try:
self.Hcgm = HHV_stoichiometry(self.combustion_stoichiometry, Hf=self.Hfgm) if self.Hfgm is not None else None
except:
self.Hcgm = None
self.Hcg = property_molar_to_mass(self.Hcgm, self.MW) if (self.Hcgm is not None) else None
self.Hcgm_lower = LHV_from_HHV(self.Hcgm, self.combustion_stoichiometry.get('H2O', 0.0)) if self.Hcgm is not None else None
self.Hcg_lower = property_molar_to_mass(self.Hcgm_lower, self.MW) if (self.Hcgm_lower is not None) else None
try:
self.StielPolar = Stiel_polar_factor(Psat=self.VaporPressure(T=self.Tc*0.6), Pc=self.Pc, omega=self.omega)
except:
self.StielPolar = None
self.VolumeLiquid = VolumeLiquid(MW=self.MW, Tb=self.Tb, Tc=self.Tc,
Pc=self.Pc, Vc=self.Vc, Zc=self.Zc, omega=self.omega,
dipole=self.dipole,
Psat=self.VaporPressure,
eos=self.eos_in_a_box, CASRN=self.CAS,
**user_chemical_property_lookup(self.CAS, 'VolumeLiquid'))
self.Vml_Tb = self.VolumeLiquid.T_dependent_property(self.Tb) if self.Tb else None
self.Vml_Tm = self.VolumeLiquid.T_dependent_property(self.Tm) if self.Tm else None
self.Vml_STP = self.VolumeLiquid.T_dependent_property(298.15)
self.rhoml_STP = 1.0/self.Vml_STP if self.Vml_STP else None
self.rhol_STP = Vm_to_rho(self.Vml_STP, self.MW) if self.Vml_STP else None
self.Vml_60F = self.VolumeLiquid.T_dependent_property(288.7055555555555)
self.rhoml_60F = 1.0/self.Vml_60F if self.Vml_60F else None
self.rhol_60F = Vm_to_rho(self.Vml_60F, self.MW) if self.Vml_60F else None
self.VolumeGas = VolumeGas(MW=self.MW, Tc=self.Tc, Pc=self.Pc,
omega=self.omega, dipole=self.dipole,
eos=self.eos_in_a_box, CASRN=self.CAS)
self.Vmg_STP = ideal_gas(T=298.15, P=101325)
self.VolumeSolid = VolumeSolid(CASRN=self.CAS, MW=self.MW, Tt=self.Tt, Vml_Tt=self.Vml_Tm,
**user_chemical_property_lookup(self.CAS, 'VolumeSolid'))
self.Vms_Tm = self.VolumeSolid.T_dependent_property(self.Tm) if self.Tm else None
self.rhoms_Tm = 1.0/self.Vms_Tm if self.Vms_Tm is not None else None
self.rhos_Tm = Vm_to_rho(self.Vms_Tm, self.MW) if self.Vms_Tm else None
self.HeatCapacityGas = HeatCapacityGas(CASRN=self.CAS, MW=self.MW, similarity_variable=self.similarity_variable, **user_chemical_property_lookup(self.CAS, 'HeatCapacityGas'))
self.HeatCapacitySolid = HeatCapacitySolid(MW=self.MW, similarity_variable=self.similarity_variable, CASRN=self.CAS, **user_chemical_property_lookup(self.CAS, 'HeatCapacitySolid'))
self.HeatCapacityLiquid = HeatCapacityLiquid(CASRN=self.CAS, MW=self.MW, similarity_variable=self.similarity_variable, Tc=self.Tc, omega=self.omega, Cpgm=self.HeatCapacityGas.T_dependent_property, **user_chemical_property_lookup(self.CAS, 'HeatCapacityLiquid'))
self.EnthalpyVaporization = EnthalpyVaporization(CASRN=self.CAS, Tb=self.Tb, Tc=self.Tc, Pc=self.Pc, omega=self.omega,
similarity_variable=self.similarity_variable,
**user_chemical_property_lookup(self.CAS, 'EnthalpyVaporization'))
self.Hvap_Tbm = self.EnthalpyVaporization.T_dependent_property(self.Tb) if self.Tb else None
self.Hvap_Tb = property_molar_to_mass(self.Hvap_Tbm, self.MW)
self.Svap_Tbm = self.Hvap_Tb/self.Tb if (self.Tb is not None and self.Hvap_Tb is not None) else None
self.Hvapm_298 = self.EnthalpyVaporization.T_dependent_property(298.15)
self.Hvap_298 = property_molar_to_mass(self.Hvapm_298, self.MW) if self.Hvapm_298 else None
self.EnthalpySublimation = EnthalpySublimation(CASRN=self.CAS, Tm=self.Tm, Tt=self.Tt,
Cpg=self.HeatCapacityGas, Cps=self.HeatCapacitySolid,
Hvap=self.EnthalpyVaporization,
**user_chemical_property_lookup(self.CAS, 'EnthalpySublimation'))
self.Hsubm = self.Hsub_Ttm = self.EnthalpySublimation(self.Tt) if self.Tt is not None else None
self.Hsub = self.Hsub_Tt = property_molar_to_mass(self.Hsub_Ttm, self.MW) if self.Hsub_Ttm is not None else None
self.Ssub_Ttm = self.Hsub_Ttm/self.Tt if (self.Tt is not None and self.Hsub_Ttm is not None) else None
self.Sfusm = self.Hfusm/self.Tm if (self.Tm is not None and self.Hfusm is not None) else None
self.SublimationPressure = SublimationPressure(CASRN=self.CAS, Tt=self.Tt, Pt=self.Pt, Hsub_t=self.Hsub_Ttm,
**user_chemical_property_lookup(self.CAS, 'SublimationPressure'))
self.ViscosityLiquid = ViscosityLiquid(CASRN=self.CAS, MW=self.MW, Tm=self.Tm, Tc=self.Tc, Pc=self.Pc, Vc=self.Vc, omega=self.omega, Psat=self.VaporPressure, Vml=self.VolumeLiquid,
**user_chemical_property_lookup(self.CAS, 'ViscosityLiquid'))
self.ViscosityGas = ViscosityGas(CASRN=self.CAS, MW=self.MW, Tc=self.Tc, Pc=self.Pc, Zc=self.Zc, dipole=self.dipole, Vmg=self.VolumeGas.T_atmospheric_dependent_property,
**user_chemical_property_lookup(self.CAS, 'ViscosityGas'))
self.ThermalConductivityLiquid = ThermalConductivityLiquid(CASRN=self.CAS, MW=self.MW, Tm=self.Tm, Tb=self.Tb, Tc=self.Tc, Pc=self.Pc, omega=self.omega, Hfus=self.Hfusm,
**user_chemical_property_lookup(self.CAS, 'ThermalConductivityLiquid'))
self.ThermalConductivityGas = ThermalConductivityGas(CASRN=self.CAS, MW=self.MW, Tb=self.Tb, Tc=self.Tc, Pc=self.Pc, Vc=self.Vc, Zc=self.Zc, omega=self.omega, dipole=self.dipole, Vmg=self.VolumeGas, Cpgm=self.HeatCapacityGas,
mug=self.ViscosityGas,
**user_chemical_property_lookup(self.CAS, 'ThermalConductivityGas'))
self.ThermalConductivitySolid = ThermalConductivitySolid(CASRN=self.CAS, **user_chemical_property_lookup(self.CAS, 'ThermalConductivitySolid'))
self.SurfaceTension = SurfaceTension(CASRN=self.CAS, MW=self.MW, Tb=self.Tb, Tc=self.Tc, Pc=self.Pc, Vc=self.Vc, Zc=self.Zc, omega=self.omega, StielPolar=self.StielPolar, Hvap_Tb=self.Hvap_Tb, Vml=self.VolumeLiquid, Cpl=self.HeatCapacityLiquid,
**user_chemical_property_lookup(self.CAS, 'SurfaceTension'))
self.Permittivity = self.PermittivityLiquid = PermittivityLiquid(CASRN=self.CAS, **user_chemical_property_lookup(self.CAS, 'PermittivityLiquid'))
# set molecular_diameter; depends on Vml_Tb, Vml_Tm
self.molecular_diameter_sources = molecular_diameter_methods(Tc=self.Tc, Pc=self.Pc, Vc=self.Vc, Zc=self.Zc, omega=self.omega, Vm=self.Vml_Tm, Vb=self.Vml_Tb, CASRN=self.CAS)
self.molecular_diameter_source = self.molecular_diameter_sources[0] if self.molecular_diameter_sources else None
self.molecular_diameter = molecular_diameter(Tc=self.Tc, Pc=self.Pc, Vc=self.Vc, Zc=self.Zc, omega=self.omega, Vm=self.Vml_Tm, Vb=self.Vml_Tb, method=self.molecular_diameter_source, CASRN=self.CAS)
# Adjust Gf, Hf if needed
try:
if self.Hfgm is not None and self.Hfm is None:
Hfm = None
if self.phase_STP == 'l' and self.Hvapm_298 is not None:
Hfm = Hf_basis_converter(Hvapm=self.Hvapm_298, Hf_gas=self.Hfgm)
elif self.phase_STP == 'g':
Hfm = self.Hfgm
if Hfm is not None:
self.Hfm = Hfm
self.Hf = property_molar_to_mass(self.Hfm, self.MW) if (self.Hfm is not None) else None
elif self.Hfm is not None and self.Hfgm is None:
Hfmg = None
if self.phase_STP == 'l' and self.Hvapm_298 is not None:
Hfmg = Hf_basis_converter(Hvapm=self.Hvapm_298, Hf_liq=self.Hfm)
elif self.phase_STP == 'g':
Hfmg = self.Hfm
if Hfmg is not None:
self.Hfmg = Hfmg
self.Hfg = property_molar_to_mass(self.Hfmg, self.MW) if (self.Hfmg is not None) else None
except:
pass
try:
from thermo.chemical_utils import S0_basis_converter
if self.S0gm is not None and self.S0m is None:
S0m = None
if self.phase_STP == 'l':
S0m = S0_basis_converter(self, S0_gas=self.S0gm)
elif self.phase_STP == 'g':
S0m = self.S0gm
if S0m is not None:
self.S0m = S0m
self.S0 = property_molar_to_mass(self.S0m, self.MW) if (self.S0m is not None) else None
elif self.S0m is not None and self.S0gm is None:
S0gm = None
if self.phase_STP == 'l':
S0gm = S0_basis_converter(self, S0_liq=self.S0m)
elif self.phase_STP == 'g':
S0gm = self.S0m
if S0gm is not None:
self.S0gm = S0gm
self.S0g = property_molar_to_mass(self.S0gm, self.MW) if (self.S0gm is not None) else None
except:
pass
try:
self.Gfm = Gibbs_formation(self.Hfm, self.S0m, *self.elemental_reaction_data)
except:
self.Gfm = None
self.Gf = property_molar_to_mass(self.Gfm, self.MW) if (self.Gfm is not None) else None
self.Sfm = (self.Hfm - self.Gfm)/298.15 if (self.Hfm is not None and self.Gfm is not None) else None
self.Sf = property_molar_to_mass(self.Sfm, self.MW) if (self.Sfm is not None) else None
self.solubility_parameter_STP = solubility_parameter(T=298.15, Hvapm=self.Hvapm_298, Vml=self.Vml_STP) if (self.Hvapm_298 is not None and self.Vml_STP is not None) else None
[docs] def set_ref(self, T_ref=298.15, P_ref=101325, phase_ref='calc', H_ref=0, S_ref=0):
# Muse run after set_TP_sources, set_phase due to HeatCapacity*, phase_STP
self.T_ref = getattr(self, T_ref) if isinstance(T_ref, str) else T_ref
self.P_ref = getattr(self, P_ref) if isinstance(P_ref, str) else P_ref
self.H_ref = getattr(self, H_ref) if isinstance(H_ref, str) else H_ref
self.S_ref = getattr(self, S_ref) if isinstance(S_ref, str) else S_ref
self.phase_ref = self.phase_STP if phase_ref == 'calc' else phase_ref
integrators = {'s': self.HeatCapacitySolid.T_dependent_property_integral,
'l': self.HeatCapacityLiquid.T_dependent_property_integral,
'g': self.HeatCapacityGas.T_dependent_property_integral}
integrators_T = {'s': self.HeatCapacitySolid.T_dependent_property_integral_over_T,
'l': self.HeatCapacityLiquid.T_dependent_property_integral_over_T,
'g': self.HeatCapacityGas.T_dependent_property_integral_over_T}
# Integrals stored to avoid recalculation, all from T_low to T_high
try:
# Enthalpy integrals
if self.phase_ref != 'l' and self.Tm and self.Tb:
self.H_int_l_Tm_to_Tb = integrators['l'](self.Tm, self.Tb)
if self.phase_ref == 's' and self.Tm:
self.H_int_T_ref_s_to_Tm = integrators['s'](self.T_ref, self.Tm)
if self.phase_ref == 'g' and self.Tb:
self.H_int_Tb_to_T_ref_g = integrators['g'](self.Tb, self.T_ref)
if self.phase_ref == 'l' and self.Tm and self.Tb:
self.H_int_l_T_ref_l_to_Tb = integrators['l'](self.T_ref, self.Tb)
self.H_int_l_Tm_to_T_ref_l = integrators['l'](self.Tm, self.T_ref)
# Entropy integrals
if self.phase_ref != 'l' and self.Tm and self.Tb:
self.S_int_l_Tm_to_Tb = integrators_T['l'](self.Tm, self.Tb)
if self.phase_ref == 's' and self.Tm:
self.S_int_T_ref_s_to_Tm = integrators_T['s'](self.T_ref, self.Tm)
if self.phase_ref == 'g' and self.Tb:
self.S_int_Tb_to_T_ref_g = integrators_T['g'](self.Tb, self.T_ref)
if self.phase_ref == 'l' and self.Tm and self.Tb:
self.S_int_l_T_ref_l_to_Tb = integrators_T['l'](self.T_ref, self.Tb)
self.S_int_l_Tm_to_T_ref_l = integrators_T['l'](self.Tm, self.T_ref)
except:
pass
# Excess properties stored
try:
if self.phase_ref == 'g':
self.eos_phase_ref = self.eos.to_TP(self.T_ref, self.P_ref)
self.H_dep_ref_g = self.eos_phase_ref.H_dep_g
self.S_dep_ref_g = self.eos_phase_ref.S_dep_g
elif self.phase_ref == 'l':
self.eos_phase_ref = self.eos.to_TP(self.T_ref, self.P_ref)
self.H_dep_ref_l = self.eos_phase_ref.H_dep_l
self.S_dep_ref_l = self.eos_phase_ref.S_dep_l
self.H_dep_T_ref_Pb = self.eos.to_TP(self.T_ref, 101325).H_dep_l
self.S_dep_T_ref_Pb = self.eos.to_TP(self.T_ref, 101325).S_dep_l
if self.Tb:
self.eos_Tb = self.eos.to_TP(self.Tb, 101325)
self.H_dep_Tb_Pb_g = self.eos_Tb.H_dep_g
self.H_dep_Tb_Pb_l = self.eos_Tb.H_dep_l
self.H_dep_Tb_P_ref_g = self.eos.to_TP(self.Tb, self.P_ref).H_dep_g
self.S_dep_Tb_P_ref_g = self.eos.to_TP(self.Tb, self.P_ref).S_dep_g
self.S_dep_Tb_Pb_g = self.eos_Tb.S_dep_g
self.S_dep_Tb_Pb_l = self.eos_Tb.S_dep_l
# if self.Tt and self.Pt:
# self.eos_Tt = self.eos.to_TP(self.Tt, self.Pt)
# self.H_dep_Tt_g = self.eos_Tt.H_dep_g
## self.H_dep_Tt_l = self.eos_Tt.H_dep_l
#
# self.S_dep_Tt_g = self.eos_Tt.S_dep_g
## self.S_dep_Tt_l = self.eos_Tt.S_dep_l
except:
pass
[docs] def calc_H(self, T, P):
integrators = {'s': self.HeatCapacitySolid.T_dependent_property_integral,
'l': self.HeatCapacityLiquid.T_dependent_property_integral,
'g': self.HeatCapacityGas.T_dependent_property_integral}
try:
H = self.H_ref
if self.phase == self.phase_ref:
H += integrators[self.phase](self.T_ref, T)
elif self.phase_ref == 's' and self.phase == 'l':
H += self.H_int_T_ref_s_to_Tm + self.Hfusm + integrators['l'](self.Tm, T)
elif self.phase_ref == 'l' and self.phase == 's':
H += -self.H_int_l_Tm_to_T_ref_l - self.Hfusm + integrators['s'](self.Tm, T)
elif self.phase_ref == 'l' and self.phase == 'g':
H += self.H_int_l_T_ref_l_to_Tb + self.Hvap_Tbm + integrators['g'](self.Tb, T)
elif self.phase_ref == 'g' and self.phase == 'l':
H += -self.H_int_Tb_to_T_ref_g - self.Hvap_Tbm + integrators['l'](self.Tb, T)
elif self.phase_ref == 's' and self.phase == 'g':
H += self.H_int_T_ref_s_to_Tm + self.Hfusm + self.H_int_l_Tm_to_Tb + self.Hvap_Tbm + integrators['g'](self.Tb, T)
elif self.phase_ref == 'g' and self.phase == 's':
H += -self.H_int_Tb_to_T_ref_g - self.Hvap_Tbm - self.H_int_l_Tm_to_Tb - self.Hfusm + integrators['s'](self.Tm, T)
else:
raise Exception('Unknown error')
except:
return None
return H
[docs] def calc_H_excess(self, T, P):
H_dep = 0
if self.phase_ref == 'g' and self.phase == 'g':
H_dep += self.eos.to_TP(T, P).H_dep_g - self.H_dep_ref_g
elif self.phase_ref == 'l' and self.phase == 'l':
try:
H_dep += self.eos.to_TP(T, P).H_dep_l - self._eos_T_101325.H_dep_l
except:
H_dep += 0
elif self.phase_ref == 'g' and self.phase == 'l':
H_dep += self.H_dep_Tb_Pb_g - self.H_dep_Tb_P_ref_g
H_dep += (self.eos.to_TP(T, P).H_dep_l - self._eos_T_101325.H_dep_l)
elif self.phase_ref == 'l' and self.phase == 'g':
H_dep += self.H_dep_T_ref_Pb - self.H_dep_ref_l
H_dep += (self.eos.to_TP(T, P).H_dep_g - self.H_dep_Tb_Pb_g)
return H_dep
[docs] def calc_S_excess(self, T, P):
S_dep = 0
if self.phase_ref == 'g' and self.phase == 'g':
S_dep += self.eos.to_TP(T, P).S_dep_g - self.S_dep_ref_g
elif self.phase_ref == 'l' and self.phase == 'l':
try:
S_dep += self.eos.to_TP(T, P).S_dep_l - self._eos_T_101325.S_dep_l
except:
S_dep += 0
elif self.phase_ref == 'g' and self.phase == 'l':
S_dep += self.S_dep_Tb_Pb_g - self.S_dep_Tb_P_ref_g
S_dep += (self.eos.to_TP(T, P).S_dep_l - self._eos_T_101325.S_dep_l)
elif self.phase_ref == 'l' and self.phase == 'g':
S_dep += self.S_dep_T_ref_Pb - self.S_dep_ref_l
S_dep += (self.eos.to_TP(T, P).S_dep_g - self.S_dep_Tb_Pb_g)
return S_dep
[docs] def calc_S(self, T, P):
integrators_T = {'s': self.HeatCapacitySolid.T_dependent_property_integral_over_T,
'l': self.HeatCapacityLiquid.T_dependent_property_integral_over_T,
'g': self.HeatCapacityGas.T_dependent_property_integral_over_T}
try:
S = self.S_ref
if self.phase == self.phase_ref:
S += integrators_T[self.phase](self.T_ref, T)
if self.phase in ['l', 'g']:
S += -R*log(P/self.P_ref)
elif self.phase_ref == 's' and self.phase == 'l':
S += self.S_int_T_ref_s_to_Tm + self.Hfusm/self.Tm + integrators_T['l'](self.Tm, T)
elif self.phase_ref == 'l' and self.phase == 's':
S += - self.S_int_l_Tm_to_T_ref_l - self.Hfusm/self.Tm + integrators_T['s'](self.Tm, T)
elif self.phase_ref == 'l' and self.phase == 'g':
S += self.S_int_l_T_ref_l_to_Tb + self.Hvap_Tbm/self.Tb + integrators_T['g'](self.Tb, T) -R*log(P/self.P_ref) # TODO add to other states
elif self.phase_ref == 'g' and self.phase == 'l':
S += - self.S_int_Tb_to_T_ref_g - self.Hvapm/self.Tb + integrators_T['l'](self.Tb, T)
elif self.phase_ref == 's' and self.phase == 'g':
S += self.S_int_T_ref_s_to_Tm + self.Hfusm/self.Tm + self.S_int_l_Tm_to_Tb + self.Hvap_Tbm/self.Tb + integrators_T['g'](self.Tb, T)
elif self.phase_ref == 'g' and self.phase == 's':
S += - self.S_int_Tb_to_T_ref_g - self.Hvap_Tbm/self.Tb - self.S_int_l_Tm_to_Tb - self.Hfusm/self.Tm + integrators_T['s'](self.Tm, T)
else:
raise Exception('Unknown error')
except:
return None
return S
[docs] def calculate_TH(self, T, H):
def to_solve(P):
self.calculate(T, P)
return self.H - H
return secant(to_solve, self.P)
[docs] def calculate_PH(self, P, H):
def to_solve(T):
self.calculate(T, P)
return self.H - H
return secant(to_solve, self.T)
[docs] def calculate_TS(self, T, S):
def to_solve(P):
self.calculate(T, P)
return self.S - S
return secant(to_solve, self.P)
[docs] def calculate_PS(self, P, S):
def to_solve(T):
self.calculate(T, P)
return self.S - S
return secant(to_solve, self.T)
[docs] def set_thermo(self):
try:
self._eos_T_101325 = self.eos.to_TP(self.T, 101325)
self.Hm = self.calc_H(self.T, self.P)
self.Hm += self.calc_H_excess(self.T, self.P)
self.H = property_molar_to_mass(self.Hm, self.MW) if (self.Hm is not None) else None
self.Sm = self.calc_S(self.T, self.P)
self.Sm += self.calc_S_excess(self.T, self.P)
self.S = property_molar_to_mass(self.Sm, self.MW) if (self.Sm is not None) else None
self.G = self.H - self.T*self.S if (self.H is not None and self.S is not None) else None
self.Gm = self.Hm - self.T*self.Sm if (self.Hm is not None and self.Sm is not None) else None
except:
pass
@property
def Um(self):
r'''Internal energy of the chemical at its current temperature and
pressure, in units of [J/mol].
This property requires that :obj:`thermo.chemical.set_thermo` ran
successfully to be accurate.
It also depends on the molar volume of the chemical at its current
conditions.
'''
return self.Hm - self.P*self.Vm if (self.Vm and self.Hm is not None) else None
@property
def U(self):
r'''Internal energy of the chemical at its current temperature and
pressure, in units of [J/kg].
This property requires that :obj:`thermo.chemical.set_thermo` ran
successfully to be accurate.
It also depends on the molar volume of the chemical at its current
conditions.
'''
return property_molar_to_mass(self.Um, self.MW) if (self.Um is not None) else None
@property
def Am(self):
r'''Helmholtz energy of the chemical at its current temperature and
pressure, in units of [J/mol].
This property requires that :obj:`thermo.chemical.set_thermo` ran
successfully to be accurate.
It also depends on the molar volume of the chemical at its current
conditions.
'''
return self.Um - self.T*self.Sm if (self.Um is not None and self.Sm is not None) else None
@property
def A(self):
r'''Helmholtz energy of the chemical at its current temperature and
pressure, in units of [J/kg].
This property requires that :obj:`thermo.chemical.set_thermo` ran
successfully to be accurate.
It also depends on the molar volume of the chemical at its current
conditions.
'''
return self.U - self.T*self.S if (self.U is not None and self.S is not None) else None
### Temperature independent properties - calculate lazily
@property
def charge(self):
r'''Charge of a chemical, computed with RDKit from a chemical's SMILES.
If RDKit is not available, holds None.
Examples
--------
>>> Chemical('sodium ion').charge
1
'''
try:
if not self.rdkitmol:
return charge_from_formula(self.formula)
else:
from rdkit import Chem
return Chem.GetFormalCharge(self.rdkitmol)
except:
return charge_from_formula(self.formula)
@property
def rings(self):
r'''Number of rings in a chemical, computed with RDKit from a
chemical's SMILES. If RDKit is not available, holds None.
Examples
--------
>>> Chemical('Paclitaxel').rings
7
'''
try:
from rdkit.Chem import Descriptors
return Descriptors.RingCount(self.rdkitmol)
except:
return None
@property
def aromatic_rings(self):
r'''Number of aromatic rings in a chemical, computed with RDKit from a
chemical's SMILES. If RDKit is not available, holds None.
Examples
--------
>>> Chemical('Paclitaxel').aromatic_rings
3
'''
try:
from rdkit.Chem import Descriptors
return Descriptors.NumAromaticRings(self.rdkitmol)
except:
return None
@property
def rdkitmol(self):
r'''RDKit object of the chemical, without hydrogen. If RDKit is not
available, holds None.
For examples of what can be done with RDKit, see
`their website <http://www.rdkit.org/docs/GettingStartedInPython.html>`_.
'''
if self.__rdkitmol:
return self.__rdkitmol
else:
try:
from rdkit import Chem
self.__rdkitmol = Chem.MolFromSmiles(self.smiles)
return self.__rdkitmol
except:
return None
@property
def rdkitmol_Hs(self):
r'''RDKit object of the chemical, with hydrogen. If RDKit is not
available, holds None.
For examples of what can be done with RDKit, see
`their website <http://www.rdkit.org/docs/GettingStartedInPython.html>`_.
'''
if self.__rdkitmol_Hs:
return self.__rdkitmol_Hs
else:
try:
from rdkit import Chem
self.__rdkitmol_Hs = Chem.AddHs(self.rdkitmol)
return self.__rdkitmol_Hs
except:
return None
@property
def Hill(self):
r'''Hill formula of a compound. For a description of the Hill system,
see :obj:`chemicals.elements.atoms_to_Hill`.
Examples
--------
>>> Chemical('furfuryl alcohol').Hill
'C5H6O2'
'''
if self.__Hill:
return self.__Hill
else:
self.__Hill = atoms_to_Hill(self.atoms)
return self.__Hill
@property
def atom_fractions(self):
r'''Dictionary of atom:fractional occurence of the elements in a
chemical. Useful when performing element balances. For mass-fraction
occurences, see :obj:`mass_fractions`.
Examples
--------
>>> Chemical('Ammonium aluminium sulfate').atom_fractions
{'Al': 0.0625, 'H': 0.25, 'N': 0.0625, 'O': 0.5, 'S': 0.125}
'''
if self.__atom_fractions:
return self.__atom_fractions
else:
self.__atom_fractions = atom_fractions(self.atoms)
return self.__atom_fractions
@property
def mass_fractions(self):
r'''Dictionary of atom:mass-weighted fractional occurence of elements.
Useful when performing mass balances. For atom-fraction occurences, see
:obj:`atom_fractions`.
Examples
--------
>>> Chemical('water').mass_fractions
{'H': 0.11189834407236524, 'O': 0.8881016559276347}
'''
if self.__mass_fractions:
return self.__mass_fractions
else:
self.__mass_fractions = mass_fractions(self.atoms, self.MW)
return self.__mass_fractions
@property
def legal_status(self):
r'''Dictionary of legal status indicators for the chemical.
Examples
--------
>>> Chemical('benzene').legal_status
{'DSL': 'LISTED', 'TSCA': 'LISTED', 'EINECS': 'LISTED', 'NLP': 'UNLISTED', 'SPIN': 'LISTED'}
'''
if self.__legal_status:
return self.__legal_status
else:
self.__legal_status = legal_status(self.CAS, method='COMBINED')
return self.__legal_status
@property
def economic_status(self):
r'''Dictionary of economic status indicators for the chemical.
Examples
--------
>>> Chemical('benzene').economic_status
["US public: {'Manufactured': 6165232.1, 'Imported': 463146.474, 'Exported': 271908.252}", '1,000,000 - 10,000,000 tonnes per annum', 'Intermediate Use Only', 'OECD HPV Chemicals']
'''
if self.__economic_status:
return self.__economic_status
else:
self.__economic_status = economic_status(self.CAS, method='Combined')
return self.__economic_status
@property
def UNIFAC_groups(self):
r'''Dictionary of UNIFAC subgroup: count groups for the original
UNIFAC subgroups, as determined by `DDBST's online service <http://www.ddbst.com/unifacga.html>`_.
Examples
--------
>>> Chemical('Cumene').UNIFAC_groups
{1: 2, 9: 5, 13: 1}
'''
try:
return self.__UNIFAC_groups
except:
pass
assignment = UNIFAC_group_assignment_DDBST(self.CAS, 'UNIFAC')
if not assignment:
assignment = None
self.__UNIFAC_groups = assignment
return assignment
@property
def UNIFAC_Dortmund_groups(self):
r'''Dictionary of Dortmund UNIFAC subgroup: count groups for the
Dortmund UNIFAC subgroups, as determined by `DDBST's online service <http://www.ddbst.com/unifacga.html>`_.
Examples
--------
>>> Chemical('Cumene').UNIFAC_Dortmund_groups
{1: 2, 9: 5, 13: 1}
'''
try:
return self.__UNIFAC_Dortmund_groups
except:
pass
assignment = UNIFAC_group_assignment_DDBST(self.CAS, 'MODIFIED_UNIFAC')
if not assignment:
assignment = None
self.__UNIFAC_Dortmund_groups = assignment
return assignment
@property
def PSRK_groups(self):
r'''Dictionary of PSRK subgroup: count groups for the PSRK subgroups,
as determined by `DDBST's online service <http://www.ddbst.com/unifacga.html>`_.
Examples
--------
>>> Chemical('Cumene').PSRK_groups
{1: 2, 9: 5, 13: 1}
'''
try:
return self.__PSRK_groups
except:
pass
assignment = UNIFAC_group_assignment_DDBST(self.CAS, 'PSRK')
if not assignment:
assignment = None
self.__PSRK_groups = assignment
return assignment
@property
def UNIFAC_R(self):
r'''UNIFAC `R` (normalized Van der Waals volume), dimensionless.
Used in the UNIFAC model.
Examples
--------
>>> Chemical('benzene').UNIFAC_R
3.1878
'''
if self.UNIFAC_groups:
return UNIFAC_RQ(self.UNIFAC_groups)[0]
return None
@property
def UNIFAC_Q(self):
r'''UNIFAC `Q` (normalized Van der Waals area), dimensionless.
Used in the UNIFAC model.
Examples
--------
>>> Chemical('decane').UNIFAC_Q
6.016
'''
if self.UNIFAC_groups:
return UNIFAC_RQ(self.UNIFAC_groups)[1]
return None
@property
def Van_der_Waals_volume(self):
r'''Unnormalized Van der Waals volume, in units of [m^3/mol].
Examples
--------
>>> Chemical('hexane').Van_der_Waals_volume
6.8261966e-05
'''
if self.UNIFAC_R:
return Van_der_Waals_volume(self.UNIFAC_R)
return None
@property
def Van_der_Waals_area(self):
r'''Unnormalized Van der Waals area, in units of [m^2/mol].
Examples
--------
>>> Chemical('hexane').Van_der_Waals_area
964000.0
'''
if self.UNIFAC_Q:
return Van_der_Waals_area(self.UNIFAC_Q)
return None
@property
def R_specific(self):
r'''Specific gas constant, in units of [J/kg/K].
Examples
--------
>>> Chemical('water').R_specific
461.5
'''
return property_molar_to_mass(R, self.MW)
### One phase properties - calculate lazily
@property
def Psat(self):
r'''Vapor pressure of the chemical at its current temperature, in units
of [Pa]. For calculation of this property at other temperatures,
or specifying manually the method used to calculate it, and more - see
the object oriented interface :obj:`thermo.vapor_pressure.VaporPressure`;
each Chemical instance creates one to actually perform the calculations.
Examples
--------
>>> Chemical('water', T=320).Psat
10545.
>>> Chemical('water').VaporPressure.T_dependent_property(320)
10545.
>>> sorted(Chemical('water').VaporPressure.all_methods)
['AMBROSE_WALTON', 'ANTOINE_POLING', 'ANTOINE_WEBBOOK', 'BOILING_CRITICAL', 'COOLPROP', 'DIPPR_PERRY_8E', 'EDALAT', 'EOS', 'IAPWS', 'LEE_KESLER_PSAT', 'SANJARI', 'VDI_PPDS', 'WAGNER_MCGARRY']
'''
return self.VaporPressure(self.T)
@property
def Hvapm(self):
r'''Enthalpy of vaporization of the chemical at its current temperature,
in units of [J/mol]. For calculation of this property at other
temperatures, or specifying manually the method used to calculate it,
and more - see the object oriented interface
:obj:`thermo.phase_change.EnthalpyVaporization`; each Chemical instance
creates one to actually perform the calculations.
Examples
--------
>>> Chemical('water', T=320).Hvapm
43048.
>>> Chemical('water').EnthalpyVaporization.T_dependent_property(320)
43048.
>>> sorted(Chemical('water').EnthalpyVaporization.all_methods)
['ALIBAKHSHI', 'CHEN', 'CLAPEYRON', 'COOLPROP', 'CRC_HVAP_298', 'CRC_HVAP_TB', 'DIPPR_PERRY_8E', 'LIU', 'MORGAN_KOBAYASHI', 'PITZER', 'RIEDEL', 'SIVARAMAN_MAGEE_KOBAYASHI', 'VDI_PPDS', 'VELASCO', 'VETERE']
'''
return self.EnthalpyVaporization(self.T)
@property
def Hvap(self):
r'''Enthalpy of vaporization of the chemical at its current temperature,
in units of [J/kg].
This property uses the object-oriented interface
:obj:`thermo.phase_change.EnthalpyVaporization`, but converts its
results from molar to mass units.
Examples
--------
>>> Chemical('water', T=320).Hvap
2389540.
'''
Hvamp = self.Hvapm
if Hvamp:
return property_molar_to_mass(Hvamp, self.MW)
return None
@property
def Cpsm(self):
r'''Solid-phase heat capacity of the chemical at its current temperature,
in units of [J/mol/K]. For calculation of this property at other
temperatures, or specifying manually the method used to calculate it,
and more - see the object oriented interface
:obj:`thermo.heat_capacity.HeatCapacitySolid`; each Chemical instance
creates one to actually perform the calculations.
Examples
--------
>>> Chemical('palladium').Cpsm
24.9
>>> Chemical('palladium').HeatCapacitySolid.T_dependent_property(320)
25.
>>> sorted(Chemical('palladium').HeatCapacitySolid.all_methods)
['CRCSTD', 'LASTOVKA_S', 'PERRY151']
'''
return self.HeatCapacitySolid(self.T)
@property
def Cplm(self):
r'''Liquid-phase heat capacity of the chemical at its current temperature,
in units of [J/mol/K]. For calculation of this property at other
temperatures, or specifying manually the method used to calculate it,
and more - see the object oriented interface
:obj:`thermo.heat_capacity.HeatCapacityLiquid`; each Chemical instance
creates one to actually perform the calculations.
Notes
-----
Some methods give heat capacity along the saturation line, some at
1 atm but only up to the normal boiling point, and some give heat
capacity at 1 atm up to the normal boiling point and then along the
saturation line. Real-liquid heat capacity is pressure dependent, but
this interface is not.
Examples
--------
>>> Chemical('water').Cplm
75.3
>>> Chemical('water').HeatCapacityLiquid.T_dependent_property(320)
75.2
>>> Chemical('water').HeatCapacityLiquid.T_dependent_property_integral(300, 320)
1505.
'''
return self.HeatCapacityLiquid(self.T)
@property
def Cpgm(self):
r'''Gas-phase ideal gas heat capacity of the chemical at its current
temperature, in units of [J/mol/K]. For calculation of this property at
other temperatures, or specifying manually the method used to calculate
it, and more - see the object oriented interface
:obj:`thermo.heat_capacity.HeatCapacityGas`; each Chemical instance
creates one to actually perform the calculations.
Examples
--------
>>> Chemical('water').Cpgm
33.58
>>> Chemical('water').HeatCapacityGas.T_dependent_property(320)
33.67
>>> Chemical('water').HeatCapacityGas.T_dependent_property_integral(300, 320)
672.
'''
return self.HeatCapacityGas(self.T)
@property
def Cps(self):
r'''Solid-phase heat capacity of the chemical at its current temperature,
in units of [J/kg/K]. For calculation of this property at other
temperatures, or specifying manually the method used to calculate it,
and more - see the object oriented interface
:obj:`thermo.heat_capacity.HeatCapacitySolid`; each Chemical instance
creates one to actually perform the calculations. Note that that
interface provides output in molar units.
Examples
--------
>>> Chemical('palladium', T=400).Cps
241.
>>> Pd = Chemical('palladium', T=400)
>>> Cpsms = [Pd.HeatCapacitySolid.T_dependent_property(T) for T in np.linspace(300, 500, 2)]
>>> [property_molar_to_mass(Cps, Pd.MW) for Cps in Cpsms]
[234., 248.]
'''
Cpsm = self.HeatCapacitySolid(self.T)
if Cpsm:
return property_molar_to_mass(Cpsm, self.MW)
return None
@property
def Cpl(self):
r'''Liquid-phase heat capacity of the chemical at its current temperature,
in units of [J/kg/K]. For calculation of this property at other
temperatures, or specifying manually the method used to calculate it,
and more - see the object oriented interface
:obj:`thermo.heat_capacity.HeatCapacityLiquid`; each Chemical instance
creates one to actually perform the calculations. Note that that
interface provides output in molar units.
Examples
--------
>>> Chemical('water', T=320).Cpl
4177.
Ideal entropy change of water from 280 K to 340 K, output converted
back to mass-based units of J/kg/K.
>>> dSm = Chemical('water').HeatCapacityLiquid.T_dependent_property_integral_over_T(280, 340)
>>> property_molar_to_mass(dSm, Chemical('water').MW)
812.
'''
Cplm = self.HeatCapacityLiquid(self.T)
if Cplm:
return property_molar_to_mass(Cplm, self.MW)
return None
@property
def Cpg(self):
r'''Gas-phase heat capacity of the chemical at its current temperature,
in units of [J/kg/K]. For calculation of this property at other
temperatures, or specifying manually the method used to calculate it,
and more - see the object oriented interface
:obj:`thermo.heat_capacity.HeatCapacityGas`; each Chemical instance
creates one to actually perform the calculations. Note that that
interface provides output in molar units.
Examples
--------
>>> w = Chemical('water', T=520)
>>> w.Cpg
1967.
'''
Cpgm = self.HeatCapacityGas(self.T)
if Cpgm:
return property_molar_to_mass(Cpgm, self.MW)
return None
@property
def Cvgm(self):
r'''Gas-phase ideal-gas contant-volume heat capacity of the chemical at
its current temperature, in units of [J/mol/K]. Subtracts R from
the ideal-gas heat capacity; does not include pressure-compensation
from an equation of state.
Examples
--------
>>> w = Chemical('water', T=520)
>>> w.Cvgm
27.1
'''
Cpgm = self.HeatCapacityGas(self.T)
if Cpgm:
return Cpgm - R
return None
@property
def Cvg(self):
r'''Gas-phase ideal-gas contant-volume heat capacity of the chemical at
its current temperature, in units of [J/kg/K]. Subtracts R from
the ideal-gas heat capacity; does not include pressure-compensation
from an equation of state.
Examples
--------
>>> w = Chemical('water', T=520)
>>> w.Cvg
1506.
'''
Cvgm = self.Cvgm
if Cvgm:
return property_molar_to_mass(Cvgm, self.MW)
return None
@property
def isentropic_exponent(self):
r'''Gas-phase ideal-gas isentropic exponent of the chemical at its
current temperature, [dimensionless]. Does not include
pressure-compensation from an equation of state.
Examples
--------
>>> Chemical('hydrogen').isentropic_exponent
1.40
'''
Cp, Cv = self.Cpg, self.Cvg
if all((Cp, Cv)):
return isentropic_exponent(Cp, Cv)
return None
@property
def Vms(self):
r'''Solid-phase molar volume of the chemical at its current
temperature, in units of [m^3/mol]. For calculation of this property at
other temperatures, or specifying manually the method used to calculate
it, and more - see the object oriented interface
:obj:`thermo.volume.VolumeSolid`; each Chemical instance
creates one to actually perform the calculations.
Examples
--------
>>> Chemical('iron').Vms
7.09e-06
'''
return self.VolumeSolid(self.T)
@property
def Vml(self):
r'''Liquid-phase molar volume of the chemical at its current
temperature and pressure, in units of [m^3/mol]. For calculation of this
property at other temperatures or pressures, or specifying manually the
method used to calculate it, and more - see the object oriented interface
:obj:`thermo.volume.VolumeLiquid`; each Chemical instance
creates one to actually perform the calculations.
Examples
--------
>>> Chemical('cyclobutane', T=225).Vml
7.42395423425395e-05
'''
return self.VolumeLiquid(self.T, self.P)
@property
def Vmg(self):
r'''Gas-phase molar volume of the chemical at its current
temperature and pressure, in units of [m^3/mol]. For calculation of this
property at other temperatures or pressures, or specifying manually the
method used to calculate it, and more - see the object oriented interface
:obj:`thermo.volume.VolumeGas`; each Chemical instance
creates one to actually perform the calculations.
Examples
--------
Estimate the molar volume of the core of the sun, at 15 million K and
26.5 PetaPascals, assuming pure helium (actually 68% helium):
>>> Chemical('helium', T=15E6, P=26.5E15).Vmg
4.7e-07
'''
return self.VolumeGas(self.T, self.P)
@property
def Vmg_ideal(self):
r'''Gas-phase molar volume of the chemical at its current
temperature and pressure calculated with the ideal-gas law,
in units of [m^3/mol].
Examples
--------
>>> Chemical('helium', T=300.0, P=1e5).Vmg_ideal
0.0249433878544
'''
return ideal_gas(T=self.T, P=self.P)
@property
def rhos(self):
r'''Solid-phase mass density of the chemical at its current temperature,
in units of [kg/m^3]. For calculation of this property at
other temperatures, or specifying manually the method used
to calculate it, and more - see the object oriented interface
:obj:`thermo.volume.VolumeSolid`; each Chemical instance
creates one to actually perform the calculations. Note that that
interface provides output in molar units.
Examples
--------
>>> Chemical('iron').rhos
7870.
'''
Vms = self.Vms
if Vms:
return Vm_to_rho(Vms, self.MW)
return None
@property
def rhol(self):
r'''Liquid-phase mass density of the chemical at its current
temperature and pressure, in units of [kg/m^3]. For calculation of this
property at other temperatures and pressures, or specifying manually
the method used to calculate it, and more - see the object oriented
interface :obj:`thermo.volume.VolumeLiquid`; each Chemical instance
creates one to actually perform the calculations. Note that that
interface provides output in molar units.
Examples
--------
>>> Chemical('o-xylene', T=297).rhol
876.9946785618097
'''
Vml = self.Vml
if Vml:
return Vm_to_rho(Vml, self.MW)
return None
@property
def rhog(self):
r'''Gas-phase mass density of the chemical at its current temperature
and pressure, in units of [kg/m^3]. For calculation of this property at
other temperatures or pressures, or specifying manually the method used
to calculate it, and more - see the object oriented interface
:obj:`thermo.volume.VolumeGas`; each Chemical instance
creates one to actually perform the calculations. Note that that
interface provides output in molar units.
Examples
--------
Estimate the density of the core of the sun, at 15 million K and
26.5 PetaPascals, assuming pure helium (actually 68% helium):
>>> Chemical('helium', T=15E6, P=26.5E15).rhog
8519.
Compared to a result on
`Wikipedia <https://en.wikipedia.org/wiki/Solar_core>`_ of 150000
kg/m^3, the fundamental equation of state performs poorly.
>>> He = Chemical('helium', T=15E6, P=26.5E15)
>>> He.VolumeGas.method_P = 'IDEAL'
>>> He.rhog
850477.
The ideal-gas law performs somewhat better, but vastly overshoots
the density prediction.
'''
Vmg = self.Vmg
if Vmg:
return Vm_to_rho(Vmg, self.MW)
return None
@property
def rhosm(self):
r'''Molar density of the chemical in the solid phase at the
current temperature and pressure, in units of [mol/m^3].
Utilizes the object oriented interface and
:obj:`thermo.volume.VolumeSolid` to perform the actual calculation of
molar volume.
Examples
--------
>>> Chemical('palladium').rhosm
112760.
'''
Vms = self.Vms
if Vms:
return 1./Vms
return None
@property
def rholm(self):
r'''Molar density of the chemical in the liquid phase at the
current temperature and pressure, in units of [mol/m^3].
Utilizes the object oriented interface and
:obj:`thermo.volume.VolumeLiquid` to perform the actual calculation of
molar volume.
Examples
--------
>>> Chemical('nitrogen', T=70).rholm
29937.
'''
Vml = self.Vml
if Vml:
return 1./Vml
return None
@property
def rhogm(self):
r'''Molar density of the chemical in the gas phase at the
current temperature and pressure, in units of [mol/m^3].
Utilizes the object oriented interface and
:obj:`thermo.volume.VolumeGas` to perform the actual calculation of
molar volume.
Examples
--------
>>> Chemical('tungsten hexafluoride').rhogm
42.
'''
Vmg = self.Vmg
if Vmg:
return 1./Vmg
return None
@property
def Zs(self):
r'''Compressibility factor of the chemical in the solid phase at the
current temperature and pressure, [dimensionless].
Utilizes the object oriented interface and
:obj:`thermo.volume.VolumeSolid` to perform the actual calculation of
molar volume.
Examples
--------
>>> Chemical('palladium').Z
0.000362
'''
Vms = self.Vms
if Vms:
return Z(self.T, self.P, Vms)
return None
@property
def Zl(self):
r'''Compressibility factor of the chemical in the liquid phase at the
current temperature and pressure, [dimensionless].
Utilizes the object oriented interface and
:obj:`thermo.volume.VolumeLiquid` to perform the actual calculation of
molar volume.
Examples
--------
>>> Chemical('water').Zl
0.00073
'''
Vml = self.Vml
if Vml:
return Z(self.T, self.P, Vml)
return None
@property
def Zg(self):
r'''Compressibility factor of the chemical in the gas phase at the
current temperature and pressure, [dimensionless].
Utilizes the object oriented interface and
:obj:`thermo.volume.VolumeGas` to perform the actual calculation of
molar volume.
Examples
--------
>>> Chemical('sulfur hexafluoride', T=700, P=1E9).Zg
11.14
'''
Vmg = self.Vmg
if Vmg:
return Z(self.T, self.P, Vmg)
return None
@property
def SGs(self):
r'''Specific gravity of the solid phase of the chemical at the
specified temperature and pressure, [dimensionless].
The reference condition is water at 4 °C and 1 atm
(rho=999.017 kg/m^3). The SG varries with temperature and pressure
but only very slightly.
Examples
--------
>>> Chemical('iron').SGs
7.8
'''
rhos = self.rhos
if rhos is not None:
return SG(rhos)
return None
@property
def SGl(self):
r'''Specific gravity of the liquid phase of the chemical at the
specified temperature and pressure, [dimensionless].
The reference condition is water at 4 °C and 1 atm
(rho=999.017 kg/m^3). For liquids, SG is defined that the reference
chemical's T and P are fixed, but the chemical itself varies with
the specified T and P.
Examples
--------
>>> Chemical('water', T=365).SGl
0.965
'''
rhol = self.rhol
if rhol is not None:
return SG(rhol)
return None
@property
def SGg(self):
r'''Specific gravity of the gas phase of the chemical, [dimensionless].
The reference condition is air at 15.6 °C (60 °F) and 1 atm
(rho=1.223 kg/m^3). The definition for gases uses the compressibility
factor of the reference gas and the chemical both at the reference
conditions, not the conditions of the chemical.
Examples
--------
>>> Chemical('argon').SGg
1.3
'''
Vmg = self.VolumeGas(T=288.70555555555552, P=101325)
if Vmg:
rho = Vm_to_rho(Vmg, self.MW)
return SG(rho, rho_ref=1.2231876628642968) # calculated with Mixture
return None
@property
def API(self):
r'''API gravity of the liquid phase of the chemical, [degrees].
The reference condition is water at 15.6 °C (60 °F) and 1 atm
(rho=999.016 kg/m^3, standardized).
Examples
--------
>>> Chemical('water').API
10.
'''
Vml = self.VolumeLiquid(T=288.70555555555552, P=101325)
if Vml:
rho = Vm_to_rho(Vml, self.MW)
sg = SG(rho, rho_ref=999.016)
return SG_to_API(sg)
@property
def Bvirial(self):
r'''Second virial coefficient of the gas phase of the chemical at its
current temperature and pressure, in units of [mol/m^3].
This property uses the object-oriented interface
:obj:`thermo.volume.VolumeGas`, converting its result with
:obj:`thermo.utils.B_from_Z`.
Examples
--------
>>> Chemical('water', T=500, P=1e5).Bvirial
-0.00017
'''
if self.Vmg:
return B_from_Z(self.Zg, self.T, self.P)
return None
@property
def isobaric_expansion_l(self):
r'''Isobaric (constant-pressure) expansion of the liquid phase of the
chemical at its current temperature and pressure, in units of [1/K].
.. math::
\beta = \frac{1}{V}\left(\frac{\partial V}{\partial T} \right)_P
Utilizes the temperature-derivative method of
:obj:`thermo.volume.VolumeLiquid` to perform the actual calculation.
The derivatives are all numerical.
Examples
--------
>>> Chemical('dodecane', T=400).isobaric_expansion_l
0.00116
'''
dV_dT = self.VolumeLiquid.TP_dependent_property_derivative_T(self.T, self.P)
Vm = self.Vml
if dV_dT and Vm:
return isobaric_expansion(V=Vm, dV_dT=dV_dT)
@property
def isobaric_expansion_g(self):
r'''Isobaric (constant-pressure) expansion of the gas phase of the
chemical at its current temperature and pressure, in units of [1/K].
.. math::
\beta = \frac{1}{V}\left(\frac{\partial V}{\partial T} \right)_P
Utilizes the temperature-derivative method of
:obj:`thermo.VolumeGas` to perform the actual calculation.
The derivatives are all numerical.
Examples
--------
>>> Chemical('Hexachlorobenzene', T=900).isobaric_expansion_g
0.00115
'''
dV_dT = self.VolumeGas.TP_dependent_property_derivative_T(self.T, self.P)
Vm = self.Vmg
if dV_dT and Vm:
return isobaric_expansion(V=Vm, dV_dT=dV_dT)
@property
def mul(self):
r'''Viscosity of the chemical in the liquid phase at its current
temperature and pressure, in units of [Pa*s].
For calculation of this property at other temperatures and pressures,
or specifying manually the method used to calculate it, and more - see
the object oriented interface
:obj:`thermo.viscosity.ViscosityLiquid`; each Chemical instance
creates one to actually perform the calculations.
Examples
--------
>>> Chemical('water', T=320).mul
0.000576
'''
return self.ViscosityLiquid(self.T, self.P)
@property
def mug(self):
r'''Viscosity of the chemical in the gas phase at its current
temperature and pressure, in units of [Pa*s].
For calculation of this property at other temperatures and pressures,
or specifying manually the method used to calculate it, and more - see
the object oriented interface
:obj:`thermo.viscosity.ViscosityGas`; each Chemical instance
creates one to actually perform the calculations.
Examples
--------
>>> Chemical('water', T=320, P=100).mug
1.04e-05
'''
return self.ViscosityGas(self.T, self.P)
@property
def kl(self):
r'''Thermal conductivity of the chemical in the liquid phase at its
current temperature and pressure, in units of [W/m/K].
For calculation of this property at other temperatures and pressures,
or specifying manually the method used to calculate it, and more - see
the object oriented interface
:obj:`thermo.thermal_conductivity.ThermalConductivityLiquid`; each
Chemical instance creates one to actually perform the calculations.
Examples
--------
>>> Chemical('water', T=320).kl
0.63
'''
return self.ThermalConductivityLiquid(self.T, self.P)
@property
def kg(self):
r'''Thermal conductivity of the chemical in the gas phase at its
current temperature and pressure, in units of [W/m/K].
For calculation of this property at other temperatures and pressures,
or specifying manually the method used to calculate it, and more - see
the object oriented interface
:obj:`thermo.thermal_conductivity.ThermalConductivityGas`; each
Chemical instance creates one to actually perform the calculations.
Examples
--------
>>> Chemical('water', T=320, P=100).kg
0.02
'''
return self.ThermalConductivityGas(self.T, self.P)
@property
def ks(self):
r'''Thermal conductivity of the chemical in the solid phase at its
current temperature and pressure, in units of [W/m/K].
For calculation of this property at other temperatures,
or specifying manually the method used to calculate it, and more - see
the object oriented interface
:obj:`thermo.thermal_conductivity.ThermalConductivitySolid`; each
Chemical instance creates one to actually perform the calculations.
'''
return self.ThermalConductivitySolid(self.T)
@property
def sigma(self):
r'''Surface tension of the chemical at its current temperature, in
units of [N/m].
For calculation of this property at other temperatures,
or specifying manually the method used to calculate it, and more - see
the object oriented interface :obj:`thermo.interface.SurfaceTension`;
each Chemical instance creates one to actually perform the calculations.
Examples
--------
>>> Chemical('water', T=320).sigma
0.068
>>> Chemical('water', T=320).SurfaceTension.solve_property(0.05)
417.2
'''
return self.SurfaceTension(self.T)
@property
def permittivity(self):
r'''Relative permittivity (dielectric constant) of the chemical at its
current temperature, [dimensionless].
For calculation of this property at other temperatures,
or specifying manually the method used to calculate it, and more - see
the object oriented interface :obj:`thermo.permittivity.PermittivityLiquid`;
each Chemical instance creates one to actually perform the calculations.
Examples
--------
>>> Chemical('toluene', T=250).permittivity
2.497
'''
return self.Permittivity(self.T)
@property
def absolute_permittivity(self):
r'''Absolute permittivity of the chemical at its current temperature,
in units of [farad/meter]. Those units are equivalent to
ampere^2*second^4/kg/m^3.
Examples
--------
>>> Chemical('water', T=293.15).absolute_permittivity
7.1e-10
'''
permittivity = self.permittivity
if permittivity is not None:
return permittivity*epsilon_0
return None
@property
def JTl(self):
r'''Joule Thomson coefficient of the chemical in the liquid phase at
its current temperature and pressure, in units of [K/Pa].
.. math::
\mu_{JT} = \left(\frac{\partial T}{\partial P}\right)_H = \frac{1}{C_p}
\left[T \left(\frac{\partial V}{\partial T}\right)_P - V\right]
= \frac{V}{C_p}\left(\beta T-1\right)
Utilizes the temperature-derivative method of
:obj:`thermo.volume.VolumeLiquid` and the temperature-dependent heat
capacity method :obj:`thermo.heat_capacity.HeatCapacityLiquid` to
obtain the properties required for the actual calculation.
Examples
--------
>>> Chemical('dodecane', T=400).JTl
-3.1e-07
'''
Vml, Cplm, isobaric_expansion_l = self.Vml, self.Cplm, self.isobaric_expansion_l
if all((Vml, Cplm, isobaric_expansion_l)):
return Joule_Thomson(T=self.T, V=Vml, Cp=Cplm, beta=isobaric_expansion_l)
return None
@property
def JTg(self):
r'''Joule Thomson coefficient of the chemical in the gas phase at
its current temperature and pressure, in units of [K/Pa].
.. math::
\mu_{JT} = \left(\frac{\partial T}{\partial P}\right)_H = \frac{1}{C_p}
\left[T \left(\frac{\partial V}{\partial T}\right)_P - V\right]
= \frac{V}{C_p}\left(\beta T-1\right)
Utilizes the temperature-derivative method of
:obj:`thermo.volume.VolumeGas` and the temperature-dependent heat
capacity method :obj:`thermo.heat_capacity.HeatCapacityGas` to
obtain the properties required for the actual calculation.
Examples
--------
>>> Chemical('dodecane', T=400, P=1000).JTg
5.4e-05
'''
Vmg, Cpgm, isobaric_expansion_g = self.Vmg, self.Cpgm, self.isobaric_expansion_g
if all((Vmg, Cpgm, isobaric_expansion_g)):
return Joule_Thomson(T=self.T, V=Vmg, Cp=Cpgm, beta=isobaric_expansion_g)
return None
@property
def nul(self):
r'''Kinematic viscosity of the liquid phase of the chemical at its
current temperature and pressure, in units of [m^2/s].
.. math::
\nu = \frac{\mu}{\rho}
Utilizes the temperature and pressure dependent object oriented
interfaces :obj:`thermo.volume.VolumeLiquid`,
:obj:`thermo.viscosity.ViscosityLiquid` to calculate the
actual properties.
Examples
--------
>>> Chemical('methane', T=110).nul
2.858e-07
'''
mul, rhol = self.mul, self.rhol
if all([mul, rhol]):
return nu_mu_converter(mu=mul, rho=rhol)
return None
@property
def nug(self):
r'''Kinematic viscosity of the gas phase of the chemical at its
current temperature and pressure, in units of [m^2/s].
.. math::
\nu = \frac{\mu}{\rho}
Utilizes the temperature and pressure dependent object oriented
interfaces :obj:`thermo.volume.VolumeGas`,
:obj:`thermo.viscosity.ViscosityGas` to calculate the
actual properties.
Examples
--------
>>> Chemical('methane', T=115).nug
2.5e-06
'''
mug, rhog = self.mug, self.rhog
if all([mug, rhog]):
return nu_mu_converter(mu=mug, rho=rhog)
return None
@property
def alphal(self):
r'''Thermal diffusivity of the liquid phase of the chemical at its
current temperature and pressure, in units of [m^2/s].
.. math::
\alpha = \frac{k}{\rho Cp}
Utilizes the temperature and pressure dependent object oriented
interfaces :obj:`thermo.volume.VolumeLiquid`,
:obj:`thermo.thermal_conductivity.ThermalConductivityLiquid`,
and :obj:`thermo.heat_capacity.HeatCapacityLiquid` to calculate the
actual properties.
Examples
--------
>>> Chemical('nitrogen', T=70).alphal
9.4e-08
'''
kl, rhol, Cpl = self.kl, self.rhol, self.Cpl
if all([kl, rhol, Cpl]):
return thermal_diffusivity(k=kl, rho=rhol, Cp=Cpl)
return None
@property
def alphag(self):
r'''Thermal diffusivity of the gas phase of the chemical at its
current temperature and pressure, in units of [m^2/s].
.. math::
\alpha = \frac{k}{\rho Cp}
Utilizes the temperature and pressure dependent object oriented
interfaces :obj:`thermo.volume.VolumeGas`,
:obj:`thermo.thermal_conductivity.ThermalConductivityGas`,
and :obj:`thermo.heat_capacity.HeatCapacityGas` to calculate the
actual properties.
Examples
--------
>>> Chemical('ammonia').alphag
1.69e-05
'''
kg, rhog, Cpg = self.kg, self.rhog, self.Cpg
if all([kg, rhog, Cpg]):
return thermal_diffusivity(k=kg, rho=rhog, Cp=Cpg)
return None
@property
def Prl(self):
r'''Prandtl number of the liquid phase of the chemical at its
current temperature and pressure, [dimensionless].
.. math::
Pr = \frac{C_p \mu}{k}
Utilizes the temperature and pressure dependent object oriented
interfaces :obj:`thermo.viscosity.ViscosityLiquid`,
:obj:`thermo.thermal_conductivity.ThermalConductivityLiquid`,
and :obj:`thermo.heat_capacity.HeatCapacityLiquid` to calculate the
actual properties.
Examples
--------
>>> Chemical('nitrogen', T=70).Prl
2.78
'''
Cpl, mul, kl = self.Cpl, self.mul, self.kl
if all([Cpl, mul, kl]):
return Prandtl(Cp=Cpl, mu=mul, k=kl)
return None
@property
def Prg(self):
r'''Prandtl number of the gas phase of the chemical at its
current temperature and pressure, [dimensionless].
.. math::
Pr = \frac{C_p \mu}{k}
Utilizes the temperature and pressure dependent object oriented
interfaces :obj:`thermo.viscosity.ViscosityGas`,
:obj:`thermo.thermal_conductivity.ThermalConductivityGas`,
and :obj:`thermo.heat_capacity.HeatCapacityGas` to calculate the
actual properties.
Examples
--------
>>> Chemical('NH3').Prg
0.84
'''
Cpg, mug, kg = self.Cpg, self.mug, self.kg
if all([Cpg, mug, kg]):
return Prandtl(Cp=Cpg, mu=mug, k=kg)
return None
@property
def solubility_parameter(self):
r'''Solubility parameter of the chemical at its
current temperature and pressure, in units of [Pa^0.5].
.. math::
\delta = \sqrt{\frac{\Delta H_{vap} - RT}{V_m}}
Calculated based on enthalpy of vaporization and molar volume.
Normally calculated at STP. For uses of this property, see
:obj:`thermo.solubility.solubility_parameter`.
Examples
--------
>>> Chemical('NH3', T=200).solubility_parameter
31712.
'''
try:
return solubility_parameter(T=self.T, Hvapm=self.Hvapm, Vml=self.Vml)
except:
return None
@property
def Parachor(self):
r'''Parachor of the chemical at its
current temperature and pressure, in units of [N^0.25*m^2.75/mol].
.. math::
P = \frac{\sigma^{0.25} MW}{\rho_L - \rho_V}
Calculated based on surface tension, density of the liquid
phase, and molecular weight. For uses of this property, see
:obj:`thermo.utils.Parachor`.
The gas density is calculated using the ideal-gas law.
Examples
--------
>>> Chemical('octane').Parachor
6.2e-05
'''
sigma, rhol = self.sigma, self.rhol
rhog = Vm_to_rho(ideal_gas(T=self.T, P=self.P), MW=self.MW)
if all((sigma, rhol, rhog, self.MW)):
return Parachor(sigma=sigma, MW=self.MW, rhol=rhol, rhog=rhog)
return None
### Single-phase properties
@property
def Cp(self):
r'''Mass heat capacity of the chemical at its current phase and
temperature, in units of [J/kg/K].
Utilizes the object oriented interfaces
:obj:`thermo.heat_capacity.HeatCapacitySolid`,
:obj:`thermo.heat_capacity.HeatCapacityLiquid`,
and :obj:`thermo.heat_capacity.HeatCapacityGas` to perform the
actual calculation of each property. Note that those interfaces provide
output in molar units (J/mol/K).
Examples
--------
>>> w = Chemical('water')
>>> w.Cp, w.phase
(4180., 'l')
>>> Chemical('palladium').Cp
234.
'''
return phase_select_property(phase=self.phase, s=Chemical.Cps, l=Chemical.Cpl, g=Chemical.Cpg, self=self)
@property
def Cpm(self):
r'''Molar heat capacity of the chemical at its current phase and
temperature, in units of [J/mol/K].
Utilizes the object oriented interfaces
:obj:`thermo.heat_capacity.HeatCapacitySolid`,
:obj:`thermo.heat_capacity.HeatCapacityLiquid`,
and :obj:`thermo.heat_capacity.HeatCapacityGas` to perform the
actual calculation of each property.
Examples
--------
>>> Chemical('cubane').Cpm
137.
>>> Chemical('ethylbenzene', T=550, P=3E6).Cpm
294.
'''
return phase_select_property(phase=self.phase, s=Chemical.Cpsm,
l=Chemical.Cplm, g=Chemical.Cpgm,
self=self)
@property
def Vm(self):
r'''Molar volume of the chemical at its current phase and
temperature and pressure, in units of [m^3/mol].
Utilizes the object oriented interfaces
:obj:`thermo.volume.VolumeSolid`,
:obj:`thermo.volume.VolumeLiquid`,
and :obj:`thermo.volume.VolumeGas` to perform the
actual calculation of each property.
Examples
--------
>>> Chemical('ethylbenzene', T=550, P=3E6).Vm
0.00017
'''
return phase_select_property(phase=self.phase, s=Chemical.Vms,
l=Chemical.Vml, g=Chemical.Vmg, self=self)
@property
def rho(self):
r'''Mass density of the chemical at its current phase and
temperature and pressure, in units of [kg/m^3].
Utilizes the object oriented interfaces
:obj:`thermo.volume.VolumeSolid`,
:obj:`thermo.volume.VolumeLiquid`,
and :obj:`thermo.volume.VolumeGas` to perform the
actual calculation of each property. Note that those interfaces provide
output in units of m^3/mol.
Examples
--------
>>> Chemical('decane', T=550, P=2E6).rho
498.
'''
return phase_select_property(phase=self.phase, s=Chemical.rhos,
l=Chemical.rhol, g=Chemical.rhog,
self=self)
@property
def rhom(self):
r'''Molar density of the chemical at its current phase and
temperature and pressure, in units of [mol/m^3].
Utilizes the object oriented interfaces
:obj:`thermo.volume.VolumeSolid`,
:obj:`thermo.volume.VolumeLiquid`,
and :obj:`thermo.volume.VolumeGas` to perform the
actual calculation of each property. Note that those interfaces provide
output in units of m^3/mol.
Examples
--------
>>> Chemical('1-hexanol').rhom
7986.
'''
return phase_select_property(phase=self.phase, s=Chemical.rhosm,
l=Chemical.rholm, g=Chemical.rhogm,
self=self)
@property
def Z(self):
r'''Compressibility factor of the chemical at its current phase and
temperature and pressure, [dimensionless].
Examples
--------
>>> Chemical('MTBE', T=900, P=1E-2).Z
1.
'''
Vm = self.Vm
if Vm:
return Z(self.T, self.P, Vm)
return None
@property
def SG(self):
r'''Specific gravity of the chemical, [dimensionless].
For gas-phase conditions, this is calculated at 15.6 °C (60 °F) and 1
atm for the chemical and the reference fluid, air.
For liquid and solid phase conditions, this is calculated based on a
reference fluid of water at 4°C at 1 atm, but the with the liquid or
solid chemical's density at the currently specified conditions.
Examples
--------
>>> Chemical('MTBE').SG
0.73
'''
phase = self.phase
if phase == 'l':
return self.SGl
elif phase == 's':
return self.SGs
elif phase == 'g':
return self.SGg
rho = self.rho
if rho is not None:
return SG(rho)
return None
@property
def isobaric_expansion(self):
r'''Isobaric (constant-pressure) expansion of the chemical at its
current phase and temperature, in units of [1/K].
.. math::
\beta = \frac{1}{V}\left(\frac{\partial V}{\partial T} \right)_P
Examples
--------
Radical change in value just above and below the critical temperature
of water:
>>> Chemical('water', T=647.1, P=22048320.0).isobaric_expansion
0.77
>>> Chemical('water', T=647.2, P=22048320.0).isobaric_expansion
0.39
'''
return phase_select_property(phase=self.phase,
l=Chemical.isobaric_expansion_l,
g=Chemical.isobaric_expansion_g,
self=self)
@property
def JT(self):
r'''Joule Thomson coefficient of the chemical at its
current phase and temperature, in units of [K/Pa].
.. math::
\mu_{JT} = \left(\frac{\partial T}{\partial P}\right)_H = \frac{1}{C_p}
\left[T \left(\frac{\partial V}{\partial T}\right)_P - V\right]
= \frac{V}{C_p}\left(\beta T-1\right)
Examples
--------
>>> Chemical('water').JT
-2.21e-07
'''
return phase_select_property(phase=self.phase, l=Chemical.JTl,
g=Chemical.JTg, self=self)
@property
def mu(self):
r'''Viscosity of the chemical at its current phase, temperature, and
pressure in units of [Pa*s].
Utilizes the object oriented interfaces
:obj:`thermo.viscosity.ViscosityLiquid` and
:obj:`thermo.viscosity.ViscosityGas` to perform the
actual calculation of each property.
Examples
--------
>>> Chemical('ethanol', T=300).mu
0.0010
>>> Chemical('ethanol', T=400).mu
1.18e-05
'''
return phase_select_property(phase=self.phase, l=Chemical.mul,
g=Chemical.mug, self=self)
@property
def k(self):
r'''Thermal conductivity of the chemical at its current phase,
temperature, and pressure in units of [W/m/K].
Utilizes the object oriented interfaces
:obj:`thermo.thermal_conductivity.ThermalConductivityLiquid` and
:obj:`thermo.thermal_conductivity.ThermalConductivityGas` to perform
the actual calculation of each property.
Examples
--------
>>> Chemical('ethanol', T=300).kl
0.16
>>> Chemical('ethanol', T=400).kg
0.026
'''
return phase_select_property(phase=self.phase, l=Chemical.kl,
g=Chemical.kg, s=Chemical.ks, self=self)
@property
def nu(self):
r'''Kinematic viscosity of the the chemical at its current temperature,
pressure, and phase in units of [m^2/s].
.. math::
\nu = \frac{\mu}{\rho}
Examples
--------
>>> Chemical('argon').nu
1.38e-05
'''
return phase_select_property(phase=self.phase, l=Chemical.nul,
g=Chemical.nug, self=self)
@property
def alpha(self):
r'''Thermal diffusivity of the chemical at its current temperature,
pressure, and phase in units of [m^2/s].
.. math::
\alpha = \frac{k}{\rho Cp}
Examples
--------
>>> Chemical('furfural').alpha
8.7e-08
'''
return phase_select_property(phase=self.phase, l=Chemical.alphal,
g=Chemical.alphag, self=self)
@property
def Pr(self):
r'''Prandtl number of the chemical at its current temperature,
pressure, and phase; [dimensionless].
.. math::
Pr = \frac{C_p \mu}{k}
Examples
--------
>>> Chemical('acetone').Pr
4.1
'''
return phase_select_property(phase=self.phase, l=Chemical.Prl,
g=Chemical.Prg, self=self)
@property
def Poynting(self):
r'''Poynting correction factor [dimensionless] for use in phase
equilibria methods based on activity coefficients or other reference
states. Performs the shortcut calculation assuming molar volume is
independent of pressure.
.. math::
\text{Poy} = \exp\left[\frac{V_l (P-P^{sat})}{RT}\right]
The full calculation normally returns values very close to the
approximate ones. This property is defined in terms of
pure components only.
Examples
--------
>>> Chemical('pentane', T=300, P=2e6).Poynting
1.09
Notes
-----
The full equation shown below can be used as follows:
.. math::
\text{Poy} = \exp\left[\frac{\int_{P_i^{sat}}^P V_i^l dP}{RT}\right]
>>> from scipy.integrate import quad
>>> c = Chemical('pentane', T=300, P=2e6)
>>> exp(quad(lambda P : c.VolumeLiquid(c.T, P), c.Psat, c.P)[0]/R/c.T)
1.093
'''
Vml, Psat = self.Vml, self.Psat
if Vml and Psat:
return exp(Vml*(self.P-Psat)/R/self.T)
return None
[docs] def Tsat(self, P):
return self.VaporPressure.solve_property(P)
### Convenience Dimensionless numbers
[docs] def Reynolds(self, V=None, D=None):
return Reynolds(V=V, D=D, rho=self.rho, mu=self.mu)
[docs] def Capillary(self, V=None):
return Capillary(V=V, mu=self.mu, sigma=self.sigma)
[docs] def Weber(self, V=None, D=None):
return Weber(V=V, L=D, rho=self.rho, sigma=self.sigma)
[docs] def Bond(self, L=None):
return Bond(rhol=self.rhol, rhog=self.rhog, sigma=self.sigma, L=L)
[docs] def Jakob(self, Tw=None):
return Jakob(Cp=self.Cp, Hvap=self.Hvap, Te=Tw-self.T)
[docs] def Grashof(self, Tw=None, L=None):
return Grashof(L=L, beta=self.isobaric_expansion, T1=Tw, T2=self.T,
rho=self.rho, mu=self.mu)
[docs] def Peclet_heat(self, V=None, D=None):
return Peclet_heat(V=V, L=D, rho=self.rho, Cp=self.Cp, k=self.k)
# Add the functional groups
def _make_getter_group(name):
def get(self):
base_name = f'is_{name}'
ref = getattr(functional_groups, base_name)
return ref(self.rdkitmol)
return get
for _name in group_names:
getter = property(_make_getter_group(_name))
name = f'is_{_name}'
_add_attrs_doc = rf"""Method to return whether or not this chemical is in the category {_name}, [-]
"""
getter.__doc__ = _add_attrs_doc
setattr(Chemical, name, getter)