# -*- coding: utf-8 -*-
"""
.. contents:: :local:
Reactors
--------
.. autoclass:: biorefineries.cane.units.SeedTrain
.. autoclass:: biorefineries.cane.units.CoFermentation
Separations
-----------
.. autoclass:: biorefineries.cane.units.OleinCrystallizer
"""
import biosteam as bst
from thermosteam import PRxn, Rxn
from biorefineries.cellulosic.units import (
SeedTrain,
CoFermentation
)
__all__ = ('SeedTrain', 'CoFermentation', 'OleinCrystallizer')
[docs]
class SeedTrain(SeedTrain):
def _init(self, reactions=None, saccharification=False):
self.saccharification = saccharification
chemicals = self.chemicals
self.reactions = reactions or PRxn([
# Reaction definition Reactant Conversion
Rxn('Glucose -> 2 Ethanol + 2 CO2', 'Glucose', 0.9000, chemicals),
Rxn('3 Xylose -> 5 Ethanol + 5 CO2', 'Xylose', 0.8000, chemicals),
Rxn('Glucose -> Cellmass', 'Glucose', 0.0473, chemicals, correct_mass_balance=True),
Rxn('Xylose -> Cellmass', 'Xylose', 0.0421, chemicals, correct_mass_balance=True),
])
def _setup(self):
super()._setup()
self.outs[0].phase = 'g'
def _run(self):
vent, effluent= self.outs
effluent.mix_from(self.ins, energy_balance=False)
self.reactions.force_reaction(effluent)
effluent.mol.remove_negatives()
effluent.T = self.T
vent.empty()
vent.copy_flow(effluent, ('CO2', 'O2', 'N2'), remove=True)
[docs]
class CoFermentation(CoFermentation):
def _init(self, tau=36, N=None, V=3785.4118, T=305.15, P=101325,
Nmin=2, Nmax=36, cofermentation=None):
bst.BatchBioreactor._init(self, tau, N, V, T, P, Nmin, Nmax)
self.P = P
chemicals = self.chemicals
self.loss = None
self.cofermentation = cofermentation or PRxn([
# Reaction definition Reactant Conversion
Rxn('Glucose -> 2 Ethanol + 2 CO2', 'Glucose', 0.9500, chemicals),
Rxn('3 Xylose -> 5 Ethanol + 5 CO2', 'Xylose', 0.8500, chemicals),
Rxn('Glucose -> Cellmass', 'Glucose', 0.05, chemicals, correct_mass_balance=True),
Rxn('Xylose -> Cellmass', 'Xylose', 0.05, chemicals, correct_mass_balance=True),
])
if 'CSL' in chemicals:
self.CSL_to_constituents = Rxn(
'CSL -> 0.5 H2O + 0.25 LacticAcid + 0.25 Protein', 'CSL', 1.0000, chemicals, basis='wt',
)
self.CSL_to_constituents.basis = 'mol'
else:
self.CSL_to_constituents = None
if all([i in self.chemicals for i in ('FFA', 'DAG', 'TAG', 'Glycerol')]):
self.lipid_reaction = self.oil_reaction = PRxn([
Rxn('TAG + 3Water -> 3FFA + Glycerol', 'TAG', 0.23, chemicals),
Rxn('TAG + Water -> FFA + DAG', 'TAG', 0.02, chemicals)
])
else:
self.lipid_reaction = self.oil_reaction = None
class AeratedCoFermentation(bst.AeratedBioreactor): # For microbial oil production
V_max_default = 500
def _init(
self, cofermentation, theta_O2=0.5,
dT_hx_loop=8,
Q_O2_consumption=-460240, # [kJ/kmol] equivalent to 110 kcal / mol as in https://www.academia.edu/19636928/Bioreactor_Design_for_Chemical_Engineers
batch=True,
**kwargs,
):
bst.StirredTankReactor._init(self, batch=batch, dT_hx_loop=dT_hx_loop, **kwargs)
chemicals = self.chemicals
self.theta_O2 = theta_O2
self.hydrolysis_reaction = Rxn('Sucrose + Water -> 2Glucose', 'Sucrose', 1.00, chemicals)
self.cofermentation = cofermentation
self.lipid_reaction = self.oil_reaction = PRxn([
Rxn('TAG + 3Water -> 3FFA + Glycerol', 'TAG', 0.23, chemicals),
Rxn('TAG + Water -> FFA + DAG', 'TAG', 0.02, chemicals)
])
self.Q_O2_consumption = Q_O2_consumption
self.optimize_power = True
self.kLa = bst.units.design_tools.aeration.kLa_stirred_Riet
self.kLa_kwargs = {'coefficients': "Van't Riet"}
def _run_vent(self, vent, effluent):
vent.copy_flow(effluent, ('CO2', 'O2', 'N2'), remove=True)
assert not effluent.imol['CO2', 'O2', 'N2'].any()
def _run_reactions(self, effluent):
self.hydrolysis_reaction.force_reaction(effluent)
self.lipid_reaction.force_reaction(effluent)
if effluent.imol['H2O'] < 0.: effluent.imol['H2O'] = 0.
self.cofermentation.force_reaction(effluent)
class AeratedFermentation(bst.AeratedBioreactor): # For microbial oil production
V_max_default = 500
def _init(
self, fermentation_reaction, cell_growth_reaction, theta_O2=0.5,
dT_hx_loop=8,
Q_O2_consumption=-460240, # [kJ/kmol] equivalent to 110 kcal / mol as in https://www.academia.edu/19636928/Bioreactor_Design_for_Chemical_Engineers
batch=True,
**kwargs,
):
bst.StirredTankReactor._init(self, batch=batch, dT_hx_loop=dT_hx_loop, **kwargs)
chemicals = self.chemicals
self.theta_O2 = theta_O2
self.hydrolysis_reaction = Rxn('Sucrose + Water -> 2Glucose', 'Sucrose', 1.00, chemicals)
self.fermentation_reaction = fermentation_reaction
self.cell_growth_reaction = cell_growth_reaction
self.lipid_reaction = self.oil_reaction = PRxn([
Rxn('TAG + 3Water -> 3FFA + Glycerol', 'TAG', 0.23, chemicals),
Rxn('TAG + Water -> FFA + DAG', 'TAG', 0.02, chemicals)
])
self.Q_O2_consumption = Q_O2_consumption
self.optimize_power = True
self.kLa = bst.units.design_tools.aeration.kLa_stirred_Riet
self.kLa_kwargs = {'coefficients': "Van't Riet"}
def _run_vent(self, vent, effluent):
vent.copy_flow(effluent, ('CO2', 'O2', 'N2'), remove=True)
assert not effluent.imol['CO2', 'O2', 'N2'].any()
def _run_reactions(self, effluent):
self.hydrolysis_reaction.force_reaction(effluent)
self.lipid_reaction.force_reaction(effluent)
if effluent.imol['H2O'] < 0.: effluent.imol['H2O'] = 0.
self.fermentation_reaction.force_reaction(effluent)
self.cell_growth_reaction.force_reaction(effluent)
[docs]
class OleinCrystallizer(bst.BatchCrystallizer):
def _init(self, T, solid_purity=0.98, melt_purity=0.90,
solid_IDs=('TAG', 'FFA', 'PL'), melt_IDs=('AcTAG',),
order=None):
super()._init(tau=5, V=1e6, T=T)
self.melt_purity = melt_purity
self.solid_purity = solid_purity
self.solid_IDs = solid_IDs
self.melt_IDs = melt_IDs
@property
def Hnet(self):
feed = self.ins[0]
effluent = self.outs[0]
if 's' in feed.phases:
solid = feed if feed.phase == 's' else feed['s']
H_in = - sum([i.Hfus * j for i,j in zip(self.chemicals, solid.mol) if i.Hfus])
else:
H_in = 0.
solids = effluent['s']
H_out = - sum([i.Hfus * j for i,j in zip(self.chemicals, solids.mol) if i.Hfus])
return H_out - H_in
def _run(self):
outlet = self.outs[0]
outlet.phases = ('s', 'l')
solid_purity = self.solid_purity
melt_purity = self.melt_purity
feed = self.ins[0]
solid_IDs = self.solid_IDs
melt_IDs = self.melt_IDs
solid_flows = feed.imass[solid_IDs]
melt_flows = feed.imass[melt_IDs]
net_solid_flow = solid_flows.sum()
net_melt_flow = melt_flows.sum()
total = net_solid_flow + net_melt_flow
minimum_melt_purity = net_melt_flow / total
minimum_solid_purity = net_solid_flow / total
outlet.empty()
if solid_purity < minimum_solid_purity:
outlet.imol['s'] = feed.mol
elif melt_purity < minimum_melt_purity:
outlet.imol['l'] = feed.mol
else: # Lever rule
solid_purity = (1. - solid_purity)
melt_fraction = (minimum_melt_purity - solid_purity) / (melt_purity - solid_purity)
melt = melt_fraction * total
pure_melt = melt * melt_purity
impurity_melt = melt - pure_melt
outlet.imass['l', melt_IDs] = pure_melt * melt_flows / melt_flows.sum()
outlet.imass['l', solid_IDs] = impurity_melt * solid_flows / solid_flows.sum()
outlet.imol['s'] = feed.mol - outlet.imol['l']
outlet.T = self.T