-
Notifications
You must be signed in to change notification settings - Fork 17
/
chemicals.py
274 lines (248 loc) · 11.1 KB
/
chemicals.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
# -*- 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.
"""
.. contents:: :local:
.. autofunction:: biorefineries.cane.chemicals.create_sugarcane_chemicals
.. autofunction:: biorefineries.cane.chemicals.create_oilcane_chemicals
.. autofunction:: biorefineries.cane.chemicals.create_cellulosic_oilcane_chemicals
.. autofunction:: biorefineries.cane.chemicals.create_acyl_olein
.. autofunction:: biorefineries.cane.chemicals.create_acetyl_diolein
"""
import thermosteam as tmo
from thermosteam import functional as fn
from chemicals import atoms_to_Hill
from thermosteam.utils import chemical_cache
from biorefineries import cellulosic
import biosteam as bst
__all__ = (
'create_sugarcane_chemicals',
'create_oilcane_chemicals',
'create_cellulosic_oilcane_chemicals',
'create_acyl_olein',
'create_acetyl_diolein',
)
@chemical_cache
def create_sugarcane_chemicals(yeast_includes_nitrogen=None):
if yeast_includes_nitrogen is None: yeast_includes_nitrogen = False
(Water, Ethanol, Glucose, Sucrose, H3PO4, P4O10, CO2, Octane, O2, N2, CH4) = chemicals = tmo.Chemicals(
['Water', 'Ethanol',
tmo.Chemical('Glucose', phase='l'),
tmo.Chemical('Sucrose', phase='l'),
tmo.Chemical('H3PO4', phase='l'),
tmo.Chemical('P4O10', phase='l'),
tmo.Chemical('CO2', phase='g'),
'Octane',
tmo.Chemical('O2', phase='g'),
tmo.Chemical('N2', phase='g'),
tmo.Chemical('CH4', phase='g')]
)
Glucose.N_solutes = 1
Sucrose.N_solutes = 2
def create_new_chemical(ID, phase='s', **constants):
chemical = tmo.Chemical(ID, phase=phase, phase_ref=phase, search_db=False, **constants)
chemicals.append(chemical)
return chemical
Ash = create_new_chemical('Ash', MW=1.)
Cellulose = create_new_chemical('Cellulose',
formula="C6H10O5", # Glucose monomer minus water
Hf=-975708.8)
Hemicellulose = create_new_chemical('Hemicellulose',
formula="C5H8O4", # Xylose monomer minus water
Hf=-761906.4)
Flocculant = create_new_chemical('Flocculant',
MW=1.)
Lignin = create_new_chemical('Lignin',
formula='C8H8O3', # Vainillin
Hf=-452909.632)
Solids = create_new_chemical('Solids', MW=1.)
Yeast = create_new_chemical(
'Yeast',
formula='CH1.61O0.56N0.16' if yeast_includes_nitrogen else 'CH1.61O0.56',
rho=1540,
Cp=Glucose.Cp(298.15),
default=True,
)
Yeast.Hf = Glucose.Hf / Glucose.MW * Yeast.MW # Same as glucose to ignore heats related to growth
CaO = create_new_chemical('CaO', formula='CaO')
### Fill missing properties ###
# Insolubles occupy a significant volume
insoluble_solids = (Ash, Cellulose, Hemicellulose,
Flocculant, Lignin, Solids, Yeast, P4O10)
# Solubles don't occupy much volume
soluble_solids = (CaO, H3PO4, Glucose, Sucrose)
for chemical in insoluble_solids:
V = fn.rho_to_V(rho=1540, MW=chemical.MW)
chemical.V.add_model(V, top_priority=True)
for chemical in soluble_solids:
V = fn.rho_to_V(rho=1e5, MW=chemical.MW)
chemical.V.add_model(V, top_priority=True)
# Add constant models for molar heat capacity of solids
# Lignocellulosic heat capacities:
# https://link.springer.com/article/10.1007/s10853-013-7815-6
# https://www.sciencedirect.com/science/article/pii/0032386182901252
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)
for chemical in chemicals: chemical.default()
chemicals.compile()
chemicals.set_synonym('Water', 'H2O')
chemicals.set_synonym('Yeast', 'DryYeast')
return chemicals
@chemical_cache
def create_acyl_olein(N_acyl):
# Assume properties are similar between oleic and palmitic chains
Hf_oleic_acid = -764.80e3
Hf_triolein = -1776e3
Hf_water = -285.825e3
Hf_glycerol = -668.6e3
# TAG + 3H2O -> GLY + 3FFA
# Hrxn = (GLY + 3FFA) - (TAG + 3H2O)
Hf_tag_to_ffa = (Hf_glycerol + 3 * Hf_oleic_acid) - (Hf_triolein + 3 * Hf_water)
Hf_minus_one_acyl = Hf_tag_to_ffa / 3.
if N_acyl == 0:
ID_model = 'Palmitic acid'
ID = 'OleicAcid'
Hf = Hf_oleic_acid
elif N_acyl == 1:
ID_model = 'Monopalmitin'
ID = 'MonoOlein'
# TAG + 2H2O -> MAG + 2FFA
# Hrxn = MAG + 2FFA - (TAG + 2H2O) # Assume to be Hf_tag_to_ffa * 2 / 3
# MAG = Hrxn - 2FFA + TAG + 2H2O
Hf = 2 * Hf_minus_one_acyl - 2 * Hf_oleic_acid + Hf_triolein + 2 * Hf_water
elif N_acyl == 2:
ID_model = 'Dipalmitin'
ID = 'DiOlein'
# TAG + H2O -> DAG + FFA
# Hrxn = DAG + FFA - (TAG + H2O) # Assume to be Hf_tag_to_ffa / 3
# DAG = Hrxn - FFA + TAG + H2O
Hf = Hf_minus_one_acyl - Hf_oleic_acid + Hf_triolein + Hf_water
elif N_acyl == 3:
ID_model = 'Tripalmitin'
ID = 'TriOlein'
Hf = Hf_triolein
model = tmo.Chemical(ID_model).at_state(phase='l', copy=True)
atoms = model.atoms
atoms['C'] += N_acyl * 2
atoms['H'] += N_acyl * 2
formula = atoms_to_Hill(atoms)
chemical = tmo.Chemical(ID, search_db=False, phase='l', phase_ref='l',
formula=formula, Hf=Hf)
chemical._Dortmund = group_counts = model.Dortmund.copy()
group_counts.set_group_counts_by_name({'CH=CH': N_acyl}, reset=False)
chemical.V.add_model(fn.rho_to_V(rho=900, MW=chemical.MW))
chemical.Cn.add_model(model.Cp(298.15) * chemical.MW)
chemical.copy_models_from(model, ['mu', 'sigma', 'kappa'])
if ID == 'MonoOlein': chemical.mu.add_model(lambda T: 0.0001)
return chemical
@chemical_cache
def create_acetyl_diolein():
# Assume properties are similar between oleic and palmitic chains
Hf_oleic_acid = -764.80e3
Hf_triolein = -1776e3
Hf_acetic_acid = -483.58e3
ID_model = 'Palmitin'
ID = 'AcetylDiOlein'
# Ac + TAG -> acTAG + FFA
# Hrxn = acTAG + FFA - (TAG + Ac) # Assume Hrxn is negligible
# acTAG = Hrxn - FFA + TAG + Ac
Hf = Hf_triolein - Hf_oleic_acid + Hf_acetic_acid
model = tmo.Chemical(ID_model).at_state(phase='l', copy=True)
formula = atoms_to_Hill({'C': 41, 'H':75, 'O': 6})
chemical = tmo.Chemical(ID, search_db=False, phase='l', phase_ref='l',
formula=formula, Hf=Hf)
chemical._Dortmund = group_counts = model.Dortmund.copy()
group_counts.set_group_counts_by_name({'CH=CH': 2, 'CH':1, 'CH2': 28, 'CH3': 3, 'CH2COO': 2, 'COOH': 1}, reset=True)
chemical.V.add_model(fn.rho_to_V(rho=900, MW=chemical.MW))
chemical.mu.add_method(900 * 20.3e-6)
chemical.Cn.add_method(model.Cp(298.15) * chemical.MW)
chemical.copy_models_from(model, ['sigma', 'kappa'])
return chemical
@chemical_cache
def create_oilcane_chemicals(yeast_includes_nitrogen=None):
chemicals = create_sugarcane_chemicals(yeast_includes_nitrogen).copy()
(Water, Ethanol, Glucose, Sucrose, H3PO4, P4O10, CO2, Octane, O2, N2, CH4,
Ash, Cellulose, Hemicellulose, Flocculant, Lignin, Solids, DryYeast, CaO) = chemicals
### Define common chemicals ###
Biodiesel = tmo.Chemical('Biodiesel',
search_ID='Methyl oleate')
Methanol = tmo.Chemical('Methanol')
Glycerol = tmo.Chemical('Glycerol')
chemicals.extend([Biodiesel, Methanol, Glycerol])
### Define new chemicals ###
def create_new_chemical(ID, phase='s', **constants):
solid = tmo.Chemical.blank(ID, phase=phase, **constants)
chemicals.append(solid)
return solid
HCl = create_new_chemical('HCl', formula='HCl')
NaOH = create_new_chemical('NaOH', formula='NaOH')
NaOCH3 = create_new_chemical('NaOCH3', formula='NaOCH3')
### Fill missing properties ###
# Solubles don't occupy much volume
for chemical in (HCl, NaOH):
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 properties as methanol
LiquidMethanol = Methanol.at_state(phase='l', copy=True)
NaOCH3.copy_models_from(LiquidMethanol, ['V', 'sigma', 'kappa', 'Cn'])
chemicals.extend([
tmo.Chemical('Phosphatidylinositol', formula='C47H83O13P',
search_db=False, CAS='383907-36-6', default=True,
Hf=-1.779e6, # Assume same as TAG on a weight basis
aliases={'PL', 'PolarLipid'}, phase='l'),
# tmo.Chemical('BetaSitosterol', search_ID='83-46-5',
# phase='l', Hf=-1000. * (533.79 + 93.90),
# aliases=['Sterol']),
create_acyl_olein(0),
create_acyl_olein(1),
create_acyl_olein(2),
create_acyl_olein(3),
tmo.Chemical('Acetone')
])
for chemical in chemicals: chemical.default()
chemicals.compile()
chemicals.set_synonym('OleicAcid', 'FFA')
chemicals.set_synonym('MonoOlein', 'MAG')
chemicals.set_synonym('DiOlein', 'DAG')
chemicals.set_synonym('TriOlein', 'TAG')
chemicals.define_group('Lipid', ('PL', 'FFA', 'MAG', 'DAG', 'TAG'))
chemicals.define_group('Oil', ('PL', 'FFA', 'MAG', 'DAG', 'TAG'))
chemicals.set_synonym('Water', 'H2O')
chemicals.set_synonym('Yeast', 'DryYeast')
return chemicals
@chemical_cache
def create_cellulosic_oilcane_chemicals(yeast_includes_nitrogen=None):
oilcane_chemicals = create_oilcane_chemicals(yeast_includes_nitrogen)
cellulosic_chemicals = cellulosic.create_cellulosic_ethanol_chemicals()
removed = {'SuccinicAcid', 'H2SO4', 'Z_mobilis', 'Oil'}
chemicals = tmo.Chemicals([
i for i in (oilcane_chemicals.tuple + cellulosic_chemicals.tuple) if i.ID not in removed
])
chemicals.extend([
create_acetyl_diolein(),
tmo.Chemical('Urea', default=True, phase='l'),
])
chemicals.extend(
bst.wastewater.high_rate.create_missing_wwt_chemicals(chemicals)
)
chemicals.compile()
chemicals.set_synonym('AcetylDiOlein', 'AcTAG')
chemicals.define_group('Lipid', ['PL', 'FFA', 'MAG', 'DAG', 'TAG', 'AcTAG'])
chemicals.define_group('lipid', ['PL', 'FFA', 'MAG', 'DAG', 'TAG', 'AcTAG'])
chemicals.define_group('Oil', ['PL', 'FFA', 'MAG', 'DAG', 'TAG'])
chemicals.set_synonym('Yeast', 'DryYeast')
chemicals.set_synonym('Yeast', 'Cellmass')
chemicals.AcetylDiOlein.V.method_P = chemicals.TriOlein.V.method_P = None
chemicals.set_synonym('Cellmass', 'Cells')
chemicals.set_synonym('Cellmass', 'cellmass')
chemicals.set_synonym('TriOlein', 'TAG')
return chemicals