Source code for thermosteam._thermo

# -*- coding: utf-8 -*-
# BioSTEAM: The Biorefinery Simulation and Techno-Economic Analysis Modules
# Copyright (C) 2020, Yoel Cortes-Pena <yoelcortes@gmail.com>
# 
# This module is under the UIUC open-source license. See 
# github.com/BioSTEAMDevelopmentGroup/biosteam/blob/master/LICENSE.txt
# for license details.
"""
"""
from . import equilibrium as eq
from ._chemical import Chemical
from ._chemicals import Chemicals
from .mixture import Mixture, ideal_mixture
from .utils import read_only, cucumber

__all__ = ('Thermo',)


[docs]@cucumber # Just means you can pickle it @read_only class Thermo: """ Create a Thermo object that defines a thermodynamic property package Parameters ---------- chemicals : Chemicals or Iterable[str] Pure component chemical data. mixture : Mixture, optional Calculates mixture properties. Gamma : ActivityCoefficients subclass, optional Class for computing activity coefficients. Phi : FugacityCoefficients subclass, optional Class for computing fugacity coefficients. PCF : PoyntingCorrectionFactor subclass, optional Class for computing poynting correction factors. cache : optional Whether or not to use cached chemicals. Examples -------- Create a property package for water and ethanol: >>> import thermosteam as tmo >>> thermo = tmo.Thermo(['Ethanol', 'Water'], cache=True) >>> thermo Thermo(chemicals=CompiledChemicals([Ethanol, Water]), mixture=Mixture(rule='ideal mixing', ..., include_excess_energies=False), Gamma=DortmundActivityCoefficients, Phi=IdealFugacityCoefficients, PCF=IdealPoyintingCorrectionFactors) >>> thermo.show() # May be easier to read Thermo( chemicals=CompiledChemicals([Ethanol, Water]), mixture=Mixture( rule='ideal mixing', ... include_excess_energies=False ), Gamma=DortmundActivityCoefficients, Phi=IdealFugacityCoefficients, PCF=IdealPoyintingCorrectionFactors ) Note that the Dortmund-UNIFAC is the default activity coefficient model. The ideal-equilibrium property package (which assumes a value of 1 for all activity coefficients) is also available: >>> ideal = thermo.ideal() >>> ideal.show() Thermo( chemicals=CompiledChemicals([Ethanol, Water]), mixture=Mixture( rule='ideal mixing', ... include_excess_energies=False ), Gamma=IdealActivityCoefficients, Phi=IdealFugacityCoefficients, PCF=IdealPoyintingCorrectionFactors ) Thermodynamic equilibrium results are affected by the choice of property package: >>> # Ideal >>> tmo.settings.set_thermo(ideal) >>> stream = tmo.Stream('stream', Water=100, Ethanol=100) >>> stream.vle(T=361, P=101325) >>> stream.show() MultiStream: stream phases: ('g', 'l'), T: 361 K, P: 101325 Pa flow (kmol/hr): (g) Ethanol 32.2 Water 17.3 (l) Ethanol 67.8 Water 82.7 >>> # Modified Roult's law: >>> tmo.settings.set_thermo(thermo) >>> stream = tmo.Stream('stream', Water=100, Ethanol=100) >>> stream.vle(T=360, P=101325) >>> stream.show() MultiStream: stream phases: ('g', 'l'), T: 360 K, P: 101325 Pa flow (kmol/hr): (g) Ethanol 100 Water 100 Thermodynamic property packages are pickleable: >>> tmo.utils.save(thermo, "Ethanol-Water Property Package") >>> thermo = tmo.utils.load("Ethanol-Water Property Package") >>> thermo.show() Thermo( chemicals=CompiledChemicals([Ethanol, Water]), mixture=Mixture( rule='ideal mixing', ... include_excess_energies=False ), Gamma=DortmundActivityCoefficients, Phi=IdealFugacityCoefficients, PCF=IdealPoyintingCorrectionFactors ) Attributes ---------- chemicals : Chemicals or Iterable[str] Pure component chemical data. mixture : Mixture, optional Calculates mixture properties. Gamma : ActivityCoefficients subclass, optional Class for computing activity coefficients. Phi : FugacityCoefficients subclass, optional Class for computing fugacity coefficients. PCF : PoyntingCorrectionFactor subclass, optional Class for computing poynting correction factors. """ __slots__ = ('chemicals', 'mixture', 'Gamma', 'Phi', 'PCF') def __init__(self, chemicals, mixture=None, Gamma=eq.DortmundActivityCoefficients, Phi=eq.IdealFugacityCoefficients, PCF=eq.IdealPoyintingCorrectionFactors, cache=None): if not isinstance(chemicals, Chemicals): chemicals = Chemicals(chemicals, cache) if not mixture: mixture = ideal_mixture(chemicals) elif not isinstance(mixture, Mixture): # pragma: no cover raise ValueError(f"mixture must be a '{Mixture.__name__}' object") chemicals.compile() issubtype = issubclass if not issubtype(Gamma, eq.ActivityCoefficients): # pragma: no cover raise ValueError(f"Gamma must be a '{eq.ActivityCoefficients.__name__}' subclass") if not issubtype(Phi, eq.FugacityCoefficients): # pragma: no cover raise ValueError(f"Phi must be a '{eq.FugacityCoefficients.__name__}' subclass") if not issubtype(PCF, eq.PoyintingCorrectionFactors): # pragma: no cover raise ValueError(f"PCF must be a '{eq.PoyintingCorrectionFactors.__name__}' subclass") setattr = object.__setattr__ setattr(self, 'chemicals', chemicals) setattr(self, 'mixture', mixture) setattr(self, 'Gamma', Gamma) setattr(self, 'Phi', Phi) setattr(self, 'PCF', PCF)
[docs] def ideal(self): """Ideal thermodynamic property package.""" cls = self.__class__ ideal = cls.__new__(cls) setattr = object.__setattr__ setattr(ideal, 'chemicals', self.chemicals) setattr(ideal, 'mixture', self.mixture) setattr(ideal, 'Gamma', eq.IdealActivityCoefficients) setattr(ideal, 'Phi', eq.IdealFugacityCoefficients) setattr(ideal, 'PCF', eq.IdealPoyintingCorrectionFactors) return ideal
[docs] def as_chemical(self, chemical): """ Return chemical as a Chemical object. Parameters ---------- chemical : str or Chemical Name of chemical being retrieved. Examples -------- >>> import thermosteam as tmo >>> thermo = tmo.Thermo(['Ethanol', 'Water'], cache=True) >>> thermo.as_chemical('Water') is thermo.chemicals.Water True >>> thermo.as_chemical('Octanol') # Chemical not defined, so it will be created Chemical('Octanol') """ isa = isinstance if isa(chemical, str): try: chemical = self.chemicals[chemical] except: chemical = Chemical(chemical) elif not isa(chemical, Chemical): # pragma: no cover raise ValueError(f"can only convert '{type(chemical).__name__}' object to chemical") return chemical
[docs] def subgroup(self, IDs): """ Create a Thermo object from a subset of chemicals. Parameters ---------- IDs : Iterable[str] Names of chemicals. Examples -------- >>> import thermosteam as tmo >>> thermo = tmo.Thermo(['Ethanol', 'Water'], cache=True) >>> thermo_water = thermo.subgroup(['Water']) >>> thermo_water.show() Thermo( chemicals=CompiledChemicals([Water]), mixture=Mixture( rule='ideal mixing', ... include_excess_energies=False ), Gamma=DortmundActivityCoefficients, Phi=IdealFugacityCoefficients, PCF=IdealPoyintingCorrectionFactors ) """ chemicals = self.chemicals.subgroup(IDs) return type(self)(chemicals, None, self.Gamma, self.Phi, self.PCF)
def __repr__(self): return f"{type(self).__name__}(chemicals={self.chemicals}, mixture={self.mixture}, Gamma={self.Gamma.__name__}, Phi={self.Phi.__name__}, PCF={self.PCF.__name__})" def show(self): try: mixture_info = self.mixture._info().replace('\n', '\n ') except: # pragma: no cover mixture_info = str(self.mixture) print(f"{type(self).__name__}(\n" f" chemicals={self.chemicals},\n" f" mixture={mixture_info},\n" f" Gamma={self.Gamma.__name__},\n" f" Phi={self.Phi.__name__},\n" f" PCF={self.PCF.__name__}\n" ")") _ipython_display_ = show