Thermo property packages¶
A Thermo object defines a thermodynamic property package. To build a Thermo
object, we must first define all the chemicals involed. The following example will show how this can be done through the creation of a property package for the co-production of biodiesel and ethanol from lipid-cane [1]:
Chemicals¶
We can first start by defining the common chemicals already in the data base:
[1]:
import thermosteam as tmo
Biodiesel = tmo.Chemical('Biodiesel',
search_ID='Methyl oleate')
lipidcane_chemicals = tmo.Chemicals(
['Water', 'Methanol', 'Ethanol', 'Glycerol',
'Glucose', 'Sucrose', 'H3PO4', 'P4O10', 'CO2',
'Octane', 'O2', Biodiesel])
(Water, Methanol, Ethanol,
Glycerol, Glucose, Sucrose,
H3PO4, P4O10, CO2, Octane, O2, Biodiesel) = lipidcane_chemicals
We can assume that CO2 and O2 will always remain a gas in the process by setting the state:
[2]:
O2.at_state(phase='g')
CO2.at_state(phase='g')
Similarly, we can assume glucose, sucrose, and phosphoric acid all remain as solids:
[3]:
H3PO4.at_state(phase='s')
P4O10.at_state(phase='s')
Glucose.at_state(phase='s')
Sucrose.at_state(phase='s')
Now we can define the solids in the process (both soluble and insoluble). We can use the Chemical.blank
method to create a “blank” Chemical
object and add the thermo models ourselves:
[4]:
def create_new_chemical(ID, phase='s', **constants):
# Create a new solid chemical without any data
solid = tmo.Chemical.blank(ID, phase=phase, **constants)
# Add chemical to the Chemicals object
lipidcane_chemicals.append(solid)
return solid
# Cellulose and hemicellulose are modeled
# as their monomer minus on H2O.
Cellulose = create_new_chemical('Cellulose',
formula="C6H10O5", # Hydrolyzed glucose monomer
Hf=-975708.8)
Hemicellulose = create_new_chemical('Hemicellulose',
formula="C5H8O5", # Hydrolyzed xylose monomer
Hf=-761906.4)
Flocculant = create_new_chemical('Flocculant', MW=1.)
# Lignin is modeled as vanillin.
Lignin = create_new_chemical('Lignin',
formula='C8H8O3', # Vanillin
Hf=-452909.632)
Ash = create_new_chemical('Ash', MW=1.)
Solids = create_new_chemical('Solids', MW=1.)
DryYeast = create_new_chemical('DryYeast', MW=1., CAS='Yeast')
NaOCH3 = create_new_chemical('NaOCH3', formula='NaOCH3')
CaO = create_new_chemical('CaO', formula='CaO')
HCl = create_new_chemical('HCl', formula='HCl')
NaOH = create_new_chemical('NaOH', formula='NaOH')
Note that we are still missing the lipid, modeled as Triolein. However, Triolein is not in the data bank, so let’s start making it from scratch:
[5]:
Lipid = create_new_chemical(
'Lipid',
phase='l',
Hf=-2193.7e3,
formula = 'C57H104O6',
)
Instead of creating new models based on external sources, here we will approximate Triolein with the properties of Tripalmitin (which does exist in the data bank):
[6]:
Tripalmitin = tmo.Chemical('Tripalmitin').at_state(phase='l', copy=True)
Lipid.copy_models_from(Tripalmitin, ['V', 'sigma', 'kappa', 'Cn'])
All what is left is to fill the chemical properties. This done through the add_model
method of the chemical model handles. Let’s begin with the solids using data from [1-4]:
[7]:
from thermosteam import functional as fn
# Assume a constant volume for lipid
lipid_molar_volume = fn.rho_to_V(rho=900, MW=Lipid.MW)
Lipid.V.add_model(lipid_molar_volume)
# Insolubles occupy a significant volume
insoluble_solids = (Ash, Cellulose, Hemicellulose, Sucrose,
Flocculant, Lignin, Solids, DryYeast, P4O10)
for chemical in insoluble_solids:
V = fn.rho_to_V(rho=1540, MW=chemical.MW)
chemical.V.add_model(V, top_priority=True)
# Solubles don't occupy much volume
soluble_solids = (CaO, HCl, NaOH, H3PO4, Glucose)
for chemical in soluble_solids:
V = fn.rho_to_V(rho=1e5, MW=chemical.MW)
chemical.V.add_model(V, top_priority=True)
# Assume sodium methoxide has some of the same properities as methanol
LiquidMethanol = Methanol.at_state(phase='l', copy=True)
NaOCH3.copy_models_from(LiquidMethanol, ['V', 'sigma','kappa', 'Cn'])
# Add constant models for molar heat capacity of solids
Ash.Cn.add_model(0.09 * 4.184 * Ash.MW)
CaO.Cn.add_model(1.02388 * CaO.MW)
Cellulose.Cn.add_model(1.364 * Cellulose.MW)
Hemicellulose.Cn.add_model(1.364 * Hemicellulose.MW)
Flocculant.Cn.add_model(4.184 * Flocculant.MW)
Lignin.Cn.add_model(1.364 * Lignin.MW)
Solids.Cn.add_model(1.100 * Solids.MW)
[7]:
1.1
We don’t care much about the rest of the properties (e.g. thermal conductivity), so we can default them to the values of water:
[8]:
for chemical in lipidcane_chemicals: chemical.default()
Finalize the chemicals by compiling:
[9]:
lipidcane_chemicals.compile()
This enables methods such as <CompiledChemicals>.array
to create chemical data ordered according to the IDs, as well as <CompiledChemicals>.get_index
to get the index of a chemical:
[10]:
lipidcane_chemicals.array(['Water', 'Ethanol'], [2, 2])
[10]:
array([2., 0., 2., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
0., 0., 0., 0., 0., 0., 0.])
[11]:
lipidcane_chemicals.get_index(('Water', 'Ethanol'))
[11]:
[0, 2]
After compiling, it is possible to set synonyms for indexing:
[12]:
lipidcane_chemicals.set_synonym('Water', 'H2O')
print(lipidcane_chemicals.H2O)
Water
Mixture objects¶
Before creating a Thermo
object, we must define the mixing rules to calculate mixture properties. A Mixture
object is able to calculate mixture properties through functors. In this example we will use a function to create a Mixture
object with ideal mixing rules:
[13]:
mixture = tmo.mixture.ideal_mixture(lipidcane_chemicals,
rigorous_energy_balance=True,
include_excess_energies=False)
mixture
Mixture(
rule='ideal mixing', ...
rigorous_energy_balance=True,
include_excess_energies=False
)
You can use the mixture for estimating mixture properties:
[14]:
array = lipidcane_chemicals.array
mol = array(['Water', 'Ethanol'], [2, 2])
mixture.H('l', mol, 300, 101325)
[14]:
694.8275427425033
[15]:
mol = array(['Water', 'Ethanol'], [2, 2])
mixture.Cn('l', mol, 300)
[15]:
376.3036205916924
You can also estimate multi-phase mixture properties through methods that start with “x” (e.g. xCn
):
[16]:
mol_liquid = array(['Water', 'Ethanol'], [2, 2])
mol_vapor = array(['Water', 'Ethanol'], [2, 2])
phase_data = [('l', mol_liquid), ('g', mol_vapor)]
mixture.xCn(phase_data, T=300)
[16]:
574.6439104469846
Note: To implement a your own Mixture object, you can request help on this process through https://github.com/BioSTEAMDevelopmentGroup/thermosteam.
Thermo objects¶
Once the chemicals and mixture objects are finalized, we can compile them into a Thermo object:
[17]:
thermo = tmo.Thermo(lipidcane_chemicals, mixture)
thermo
Thermo(
chemicals=CompiledChemicals([Water, Methanol, Ethanol, Glycerol, Glucose, Sucrose, H3PO4, P4O10, CO2, Octane, O2, Biodiesel, Cellulose, Hemicellulose, Flocculant, Lignin, Ash, Solids, DryYeast, NaOCH3, CaO, HCl, NaOH, Lipid]),
mixture=Mixture(
rule='ideal mixing', ...
rigorous_energy_balance=True,
include_excess_energies=False
),
Gamma=DortmundActivityCoefficients,
Phi=IdealFugacityCoefficients,
PCF=IdealPoyintingCorrectionFactors
)
Note that a Thermo object contains ActivityCoefficients
, FugacityCoefficients
, and PoyintingCorrectionFactors
subclasses to define fugacity estimation methods. By default, the Dortmund modified UNIFAC method for estimating activities is selected, while ideal values for (vapor phase) fugacity coefficients and poyinting correction factos are selected. Additionally, a Thermo
object defaults to ideal mixing rules for estimating mixture properties, and neglects excess properties in
the calculation of enthalpy and entropy:
[18]:
thermo = tmo.Thermo(lipidcane_chemicals)
thermo.mixture
Mixture(
rule='ideal mixing', ...
rigorous_energy_balance=True,
include_excess_energies=False
)
References¶
Huang, H., Long, S., & Singh, V. (2016) “Techno-economic analysis of biodiesel and ethanol co-production from lipid-producing sugarcane” Biofuels, Bioproducts and Biorefining, 10(3), 299–315. https://doi.org/10.1002/bbb.1640
Hatakeyama, T., Nakamura, K., & Hatakeyama, H. (1982). Studies on heat capacity of cellulose and lignin by differential scanning calorimetry. Polymer, 23(12), 1801–1804. https://doi.org/10.1016/0032-3861(82)90125-2
Thybring, E. E. (2014). Explaining the heat capacity of wood constituents by molecular vibrations. Journal of Materials Science, 49(3), 1317–1327. https://doi.org/10.1007/s10853-013-7815-6
Murphy W. K., and K. R. Masters. (1978). Gross heat of combustion of northern red oak (Quercus rubra) chemical components. Wood Sci. 10:139-141.