Source code for biorefineries.cellulosic.units

# -*- coding: utf-8 -*-
"""
.. contents:: :local:

Reactors
--------
.. autoclass:: biorefineries.cellulosic.units.PretreatmentReactorSystem
.. autoclass:: biorefineries.cellulosic.units.SeedTrain
.. autoclass:: biorefineries.cellulosic.units.ContinuousPresaccharification
.. autoclass:: biorefineries.cellulosic.units.Saccharification
.. autoclass:: biorefineries.cellulosic.units.CoFermentation
.. autoclass:: biorefineries.cellulosic.units.SaccharificationAndCoFermentation
.. autoclass:: biorefineries.cellulosic.units.SimultaneousSaccharificationAndCoFermentation

Separations
-----------
.. autoclass:: biorefineries.cellulosic.units.ReverseOsmosis
.. autoclass:: biorefineries.cellulosic.units.Nanofilter
.. autoclass:: biorefineries.cellulosic.units.HydrolyzateSolidLiquidSeparator
.. autoclass:: biorefineries.cellulosic.units.PretreatmentFlash

Feedstock handling
------------------
.. autoclass:: biorefineries.cellulosic.units.FeedStockHandling

Tanks
-----
.. autoclass:: biorefineries.cellulosic.units.AmmoniaStorageTank
.. autoclass:: biorefineries.cellulosic.units.AmmoniaReacidificationTank
.. autoclass:: biorefineries.cellulosic.units.BeerTank
.. autoclass:: biorefineries.cellulosic.units.CSLStorageTank
.. autoclass:: biorefineries.cellulosic.units.DAPStorageTank
.. autoclass:: biorefineries.cellulosic.units.AmmoniaAdditionTank
.. autoclass:: biorefineries.cellulosic.units.AmmoniaMixer
.. autoclass:: biorefineries.cellulosic.units.EnzymeHydrolysateMixer
.. autoclass:: biorefineries.cellulosic.units.SulfuricAcidMixer
.. autoclass:: biorefineries.cellulosic.units.SeedHoldTank
.. autoclass:: biorefineries.cellulosic.units.SulfuricAcidStorageTank
.. autoclass:: biorefineries.cellulosic.units.SulfuricAcidTank
.. autoclass:: biorefineries.cellulosic.units.OligomerConversionTank

Heat exchange
-------------
.. autoclass:: biorefineries.cellulosic.units.HydrolysateHeatExchanger
.. autoclass:: biorefineries.cellulosic.units.PretreatmentWasteHeater
.. autoclass:: biorefineries.cellulosic.units.WasteVaporCondenser

Pumps
-----
.. autoclass:: biorefineries.cellulosic.units.HydrolysatePump
.. autoclass:: biorefineries.cellulosic.units.BlowdownDischargePump
.. autoclass:: biorefineries.cellulosic.units.HydrolyzatePump
.. autoclass:: biorefineries.cellulosic.units.ReacidifiedHydrolyzatePump

"""
from biosteam.units.design_tools.geometry import cylinder_diameter_from_volume
from thermosteam import MultiStream
from biosteam import Unit
from biosteam.units.decorators import cost
from biosteam.units.design_tools import size_batch
import thermosteam as tmo
import biosteam as bst
import numpy as np

__all__ = (
    'PretreatmentReactorSystem',
    'SeedTrain',
    'ContinuousPresaccharification',
    'Saccharification',
    'CoFermentation',
    'SaccharificationAndCoFermentation',
    'SimultaneousSaccharificationAndCoFermentation',
    'ReverseOsmosis',
    'Nanofilter',
    'HydrolysatePump',
    'AmmoniaStorageTank',
    'AmmoniaReacidificationTank',
    'BeerTank',
    'BlowdownDischargePump',
    'CSLStorageTank',
    'DAPStorageTank',
    'FeedStockHandling',
    'PretreatmentFlash',
    'HydrolysateHeatExchanger',
    'PretreatmentWasteHeater',
    'WasteVaporCondenser',
    'HydrolyzatePump',
    'HydrolyzateSolidLiquidSeparator',
    'AmmoniaAdditionTank',
    'AmmoniaMixer',
    'EnzymeHydrolysateMixer',
    'SulfuricAcidMixer',
    'OligomerConversionTank',
    'ReacidifiedHydrolyzatePump',
    'SeedHoldTank',
    'SulfuricAcidStorageTank',
    'SulfuricAcidTank',
)

Rxn = tmo.reaction.Reaction
ParallelRxn = tmo.reaction.ParallelReaction

# %% Constants

_gal2m3 = 0.003785
_gpm2m3hr = 0.227124
# _m3hr2gpm = 4.40287
_hp2kW = 0.7457
_Gcal2kJ = 4184e3

# %% Pretreatment

[docs] @cost('Dry flow rate', 'Pretreatment reactor system', units='kg/hr', S=83333, CE=522, cost=19812400 * 0.993, n=0.6, kW=4578, BM=1.5) class PretreatmentReactorSystem(bst.units.design_tools.PressureVessel, Unit): _N_ins = 1 _N_outs = 2 _graphics = bst.Flash._graphics _units = {'Residence time': 'hr', 'Reactor volume': 'm3'} def _init(self, T=130+273.15, tau=0.166, V_wf=0.8, length_to_diameter=2, vessel_material='Stainless steel 316', vessel_type='Horizontal', reactions=None, run_vle=True): self._load_components() vapor, liquid = self.outs vapor.phase = 'g' self.T = T chemicals = self.chemicals if reactions is None: self.reactions = ParallelRxn([ # Reaction definition Reactant Conversion Rxn('Glucan + H2O -> Glucose', 'Glucan', 0.0990, chemicals), Rxn('Glucan + H2O -> GlucoseOligomer', 'Glucan', 0.0030, chemicals), Rxn('Glucan -> HMF + 2 H2O', 'Glucan', 0.0030, chemicals), Rxn('Galactan + H2O -> GalactoseOligomer', 'Galactan', 0.0240, chemicals), Rxn('Galactan -> HMF + 2 H2O', 'Galactan', 0.0030, chemicals), Rxn('Mannan + H2O -> MannoseOligomer', 'Mannan', 0.0030, chemicals), Rxn('Mannan -> HMF + 2 H2O', 'Mannan', 0.0030, chemicals), Rxn('Sucrose -> HMF + Glucose + 2H2O', 'Sucrose', 1.0000, chemicals), Rxn('Xylan + H2O -> Xylose', 'Xylan', 0.9000, chemicals), Rxn('Xylan + H2O -> XyloseOligomer', 'Xylan', 0.0240, chemicals), Rxn('Xylan -> Furfural + 2 H2O', 'Xylan', 0.0500, chemicals), Rxn('Arabinan + H2O -> Arabinose', 'Arabinan', 0.9000, chemicals), Rxn('Arabinan + H2O -> ArabinoseOligomer', 'Arabinan', 0.0240, chemicals), Rxn('Arabinan -> Furfural + 2 H2O', 'Arabinan', 0.0050, chemicals), Rxn('Acetate -> AceticAcid', 'Acetate', 1.0000, chemicals), Rxn('Lignin -> SolubleLignin', 'Lignin', 0.0500, chemicals) ]) self.glucan_to_glucose = self.reactions[0] self.xylan_to_xylose = self.reactions[8] self.glucose_to_byproducts = self.reactions[1:3] self.xylose_to_byproducts = self.reactions[9:12] else: self.reactions = reactions self.tau = tau self.V_wf = V_wf self.length_to_diameter = length_to_diameter self.vessel_material = vessel_material self.vessel_type = vessel_type self.run_vle = run_vle def _load_components(self): thermo = self.thermo self._multistream = MultiStream(None, thermo=thermo) def _run(self): feed = self.ins[0] vapor, liquid = self.outs liquid.copy_like(feed) self.reactions.adiabatic_reaction(liquid) if self.T: if self.run_vle: ms = self._multistream ms.copy_like(liquid) ms.vle(T=self.T, H=ms.H) vapor.mol[:] = ms.imol['g'] liquid.mol[:] = ms.imol['l'] vapor.T = liquid.T = ms.T vapor.P = liquid.P = ms.P else: liquid.T = self.T def _design(self): Design = self.design_results ins_F_vol = self.F_vol_in V_reactor = ins_F_vol * self.tau / self.V_wf P = self.outs[0].P * 0.000145038 # Pa to psi length_to_diameter = self.length_to_diameter D = cylinder_diameter_from_volume(V_reactor, self.length_to_diameter) D *= 3.28084 # convert from m to ft L = D * length_to_diameter Design['Residence time'] = self.tau Design['Reactor volume'] = V_reactor Design.update(self._vessel_design(float(P), float(D), float(L))) self._decorated_design() def _cost(self): Design = self.design_results self.baseline_purchase_costs.update( self._vessel_purchase_cost( Design['Weight'], Design['Diameter'], Design['Length'] ) ) self._decorated_cost()
[docs] @cost('Flow rate', 'Pumps', S=43149, CE=522, cost=24800, n=0.8, kW=40, BM=2.3) @cost('Stage #1 reactor volume', 'Stage #1 reactors', cost=37700, S=20*_gal2m3, CE=522, n=0.7, BM=1.8) @cost('Stage #2 reactor volume', 'Stage #2 reactors', cost=58300, S=200*_gal2m3, CE=522, n=0.7, BM=1.8) @cost('Stage #3 reactor volume', 'Stage #3 reactors', cost=78800, S=2e3*_gal2m3, CE=522, n=0.7, BM=1.8) @cost('Stage #4 reactor volume', 'Stage #4 reactors', cost=176e3, S=20e3*_gal2m3, CE=522, n=0.7, BM=1.8) @cost('Stage #4 reactor volume', 'Stage #4 agitators', cost=26e3/2, S=20e3*_gal2m3, kW=7.5, CE=522, n=0.5, BM=1.5) @cost('Stage #5 reactor volume', 'Stage #5 reactors', cost=590e3, S=200e3*_gal2m3, CE=522, n=0.7, BM=1.8) @cost('Stage #5 reactor volume', 'Stage #5 agitators', cost=43e3/2, S=200e3*_gal2m3, kW=10, CE=522, n=0.5, BM=1.5) class SeedTrain(Unit): _N_ins = 1 _N_outs = 2 _ins_size_is_fixed = False _units= {'Flow rate': 'kg/hr', 'Stage #1 reactor volume': 'm3', 'Stage #2 reactor volume': 'm3', 'Stage #3 reactor volume': 'm3', 'Stage #4 reactor volume': 'm3', 'Stage #5 reactor volume': 'm3'} @property def N_stages(self): """Number of stages in series.""" return 5 #: Number of parallel seed trains N_trains = 2 #: Cycle time for each batch (hr) tau_batch = 24 @property def tau_turnover(self): """Turnover time (hr) calculated by batch time divided by number of trains.""" return self.tau_batch/self.N_trains #: Operating temperature (K) T = 32+273.15 # #: wt % media (e.g. corn steep liquor) in each stage # media_loading = 0.50 # #: Diammonium phosphate loading in g/L of fermentation broth # DAP = 0.67 def _init(self, reactions=None, saccharification=None): chemicals = self.chemicals if reactions is None: self.reactions = ParallelRxn([ # Reaction definition Reactant Conversion Rxn('Glucose -> 2 Ethanol + 2 CO2', 'Glucose', 0.9000, chemicals), Rxn('Glucose + 0.047 CSL + 0.018 DAP -> 6 Z_mobilis + 2.4 H2O', 'Glucose', 0.0400, chemicals), Rxn('Glucose + 2 H2O -> 2 Glycerol + O2', 'Glucose', 0.0040, chemicals), Rxn('Glucose + 2 CO2 -> 2 SuccinicAcid + O2', 'Glucose', 0.0060, chemicals), Rxn('3 Xylose -> 5 Ethanol + 5 CO2', 'Xylose', 0.8000, chemicals), Rxn('Xylose + 0.039 CSL + 0.015 DAP -> 5 Z_mobilis + 2 H2O', 'Xylose', 0.0400, chemicals), Rxn('3 Xylose + 5 H2O -> 5 Glycerol + 2.5 O2', 'Xylose', 0.0030, chemicals), Rxn('Xylose + H2O -> Xylitol + 0.5 O2', 'Xylose', 0.0460, chemicals), Rxn('3 Xylose + 5 CO2 -> 5 SuccinicAcid + 2.5 O2', 'Xylose', 0.0090, chemicals) ]) self.glucose_to_ethanol = self.reactions[0] self.xylose_to_ethanol = self.reactions[4] self.glucose_to_byproducts = self.reactions[1:4] self.xylose_to_byproducts = self.reactions[5:] else: self.reactions = reactions if callable(saccharification): self.saccharification = saccharification elif saccharification: self.saccharification = ParallelRxn([ Rxn('Glucan -> GlucoseOligomer', 'Glucan', 0.0400, chemicals), Rxn('Glucan + 0.5 H2O -> 0.5 Cellobiose', 'Glucan', 0.0120, chemicals), Rxn('Glucan + H2O -> Glucose', 'Glucan', 0.9000, chemicals), Rxn('Cellobiose + H2O -> 2Glucose', 'Cellobiose', 1.0000, chemicals)] ) else: self.saccharification = None _setup = Unit._setup def _run(self): vent, effluent= self.outs effluent.mix_from(self.ins, energy_balance=False) if self.saccharification: self.saccharification(effluent) self.reactions.force_reaction(effluent) effluent.empty_negative_flows() effluent.T = self.T vent.phase = 'g' vent.copy_flow(effluent, ('CO2', 'O2'), remove=True) def _design(self): maxvol = self.outs[1].F_vol*self.tau_turnover vol = maxvol*10**-self.N_stages Design = self.design_results for i in range(1, self.N_stages+1): Design[f'Stage #{i} reactor volume'] = vol vol *= 10 Design['Flow rate'] = sum([i.F_mass for i in self.outs]) self.add_heat_utility(self.Hnet, self.T) def _cost(self): N = self.N_trains D = self.design_results C = self.baseline_purchase_costs kW = 0 for i, x in self.cost_items.items(): S = D[x._basis] q = S/x.S C[i] = N*bst.CE/x.CE*x.cost*q**x.n kW += N*x.kW*q self.power_utility(kW)
# %% Saccharification and fermentation (consolidated bioprocess)
[docs] @cost('Flow rate', 'Transfer pumps', kW=58, S=352*_gpm2m3hr, cost=47200/5, CE=522, n=0.8, BM=2.3, N='N_transfer_pumps') @cost('Tank volume', 'Tanks', cost=3840e3/8, S=250e3*_gal2m3, CE=522, n=0.7, BM=2.0, N='N_tanks') class ContinuousPresaccharification(Unit): _N_ins = 1 _N_outs = 1 #: Residence time of countinuous saccharification tanks (hr) tau_tank = 24 #: Number of continuous saccharification tanks N_tanks = 8 #: Number of transfer pumps N_transfer_pumps = 5 #: Working volume fraction (filled tank to total tank volume) V_wf = 0.9 _units = {'Flow rate': 'm3/hr', 'Tank volume': 'm3'} def _init(self, P=101325, reactions=None): self.P = P self.reactions = reactions def _run(self): inlet = self.ins[0] outlet = self.outs[0] outlet.copy_flow(inlet) outlet.T = inlet.T outlet.P = self.P if self.reactions: self.reactions.adiabatic_reaction(outlet) def _design(self): inlet = self.ins[0] v_0 = inlet.F_vol Design = self.design_results Design['Tank volume'] = v_0 * self.tau_tank / self.V_wf / self.N_tanks Design['Flow rate'] = v_0 / self.N_transfer_pumps
[docs] class Saccharification(bst.BatchBioreactor): _N_ins = 1 _N_outs = 1 #: Unload and clean up time (hr) tau_0 = 4 #: Working volume fraction (filled tank to total tank volume) V_wf = 0.9 _units = {'Flow rate': 'm3/hr', 'Reactor volume': 'm3', 'Reactor duty': 'kJ/hr'} def _init(self, tau=72, N=None, V=3785.4118, T=48+273.15, P=101325, Nmin=2, Nmax=36, reactions=None): super()._init(tau, N, V, T, P, Nmin, Nmax) chemicals = self.chemicals #: [ParallelReaction] Enzymatic hydrolysis reactions including from #: downstream batch tank in co-fermentation. self.reactions = reactions or ParallelRxn([ Rxn('Glucan -> GlucoseOligomer', 'Glucan', 0.0400, chemicals), Rxn('Glucan + 0.5 H2O -> 0.5 Cellobiose', 'Glucan', 0.0120, chemicals), Rxn('Glucan + H2O -> Glucose', 'Glucan', 0.9000, chemicals), Rxn('Cellobiose + H2O -> 2Glucose', 'Cellobiose', 1.0000, chemicals)] ) _setup = Unit._setup @property def saccharification(self): return self.reactions @property def vent(self): return None @property def effluent(self): return self.outs[0] def _run(self): feed, = self.ins effluent, = self.outs effluent.copy_flow(feed) effluent.T = self.T effluent.P = self.P self.reactions(effluent)
[docs] class CoFermentation(bst.BatchBioreactor): _N_ins = 1 _ins_size_is_fixed = False #: Unload and clean up time (hr) tau_0 = 4 def _init(self, tau=36, N=None, V=3785.4118, T=305.15, P=101325, Nmin=2, Nmax=36, cofermentation=None, loss=None): super()._init(tau, N, V, T, P, Nmin, Nmax) self.P = P chemicals = self.chemicals self.loss = loss or ParallelRxn([ # Reaction definition Reactant Conversion Rxn('Glucose -> 2 LacticAcid', 'Glucose', 0.0300, chemicals), Rxn('3 Xylose -> 5 LacticAcid', 'Xylose', 0.0300, chemicals), Rxn('3 Arabinose -> 5 LacticAcid', 'Arabinose', 0.0300, chemicals), Rxn('Galactose -> 2 LacticAcid', 'Galactose', 0.0300, chemicals), Rxn('Mannose -> 2 LacticAcid', 'Mannose', 0.0300, chemicals), ]) if cofermentation is None: self.cofermentation = ParallelRxn([ # Reaction definition Reactant Conversion Rxn('Glucose -> 2 Ethanol + 2 CO2', 'Glucose', 0.9500, chemicals), Rxn('Glucose + 0.047 CSL + 0.018 DAP -> 6 Z_mobilis + 2.4 H2O', 'Glucose', 0.0200, chemicals), Rxn('Glucose + 2 H2O -> 2 Glycerol + O2', 'Glucose', 0.0040, chemicals), Rxn('Glucose + 2 CO2 -> 2 SuccinicAcid + O2', 'Glucose', 0.0060, chemicals), Rxn('3 Xylose -> 5 Ethanol + 5 CO2', 'Xylose', 0.8500, chemicals), Rxn('Xylose + 0.039 CSL + 0.015 DAP -> 5 Z_mobilis + 2 H2O', 'Xylose', 0.0190, chemicals), Rxn('3 Xylose + 5 H2O -> 5 Glycerol + 2.5 O2', 'Xylose', 0.0030, chemicals), Rxn('Xylose + H2O -> Xylitol + 0.5 O2', 'Xylose', 0.0460, chemicals), Rxn('3 Xylose + 5 CO2 -> 5 SuccinicAcid + 2.5 O2', 'Xylose', 0.0090, chemicals), ]) self.glucose_to_ethanol = self.cofermentation[0] self.xylose_to_ethanol = self.cofermentation[4] self.glucose_to_byproducts = self.cofermentation[1:4] self.xylose_to_byproducts = self.cofermentation[5:] else: self.cofermentation = cofermentation if 'CSL' in self.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.oil_reaction = self.lipid_reaction = ParallelRxn([ Rxn('TAG + 3 Water -> 3FFA + Glycerol', 'TAG', 0.23, chemicals), Rxn('TAG + Water -> FFA + DAG', 'TAG', 0.02, chemicals) ]) else: self.oil_reaction = self.lipid_reaction = None def _run(self): feeds = self.ins vent, effluent = self.outs vent.P = effluent.P = self.P vent.T = effluent.T = self.T vent.phase = 'g' effluent.mix_from(feeds, energy_balance=False) if self.loss: self.loss(effluent) if self.lipid_reaction: self.lipid_reaction.force_reaction(effluent) effluent.empty_negative_flows() self.cofermentation(effluent) if self.CSL_to_constituents: self.CSL_to_constituents(effluent) vent.empty() vent.receive_vent(effluent, energy_balance=False)
[docs] @cost('Flow rate', 'Recirculation pumps', kW=30, S=340*_gpm2m3hr, cost=47200, n=0.8, BM=2.3, CE=522, N='N_reactors') @cost('Reactor duty', 'Heat exchangers', CE=522, cost=23900, S=-5*_Gcal2kJ, n=0.7, BM=2.2, N='N_reactors') # Based on a similar heat exchanger @cost('Batch duty', 'Fermentor batch cooler', CE=522, cost=86928, S=-5*_Gcal2kJ, n=0.7, BM=1.8) # Based on a similar heat exchanger @cost('Reactor volume', 'Agitators', CE=522, cost=52500, S=1e6*_gal2m3, n=0.5, kW=90, BM=1.5, N='N_reactors') @cost('Reactor volume', 'Reactors', CE=522, cost=844000, S=1e6*_gal2m3, n=0.5, BM=1.5, N='N_reactors') class SaccharificationAndCoFermentation(Unit): _N_ins = 1 _N_outs = 3 _ins_size_is_fixed = False #: Saccharification temperature (K) T_saccharification = 48+273.15 #: Fermentation temperature (K) T_fermentation = 32+273.15 #: Saccharification time (hr) tau_saccharification = 60 #: Co-Fermentation time (hr) tau_cofermentation = 36 #: Unload and clean up time (hr) tau_0 = 4 #: Working volume fraction (filled tank to total tank volume) V_wf = 0.9 #: Number of reactors N_reactors = 12 _units = {'Flow rate': 'm3/hr', 'Reactor volume': 'm3', 'Batch duty': 'kJ/hr', 'Reactor duty': 'kJ/hr'} def _init(self, P=101325, saccharification_split=0.1, saccharification=None, loss=None, cofermentation=None): self.P = P self.saccharification_split = saccharification_split chemicals = self.chemicals #: [ParallelReaction] Enzymatic hydrolysis reactions including from #: downstream batch tank in co-fermentation. self.saccharification = saccharification or ParallelRxn([ Rxn('Glucan -> GlucoseOligomer', 'Glucan', 0.0400, chemicals), Rxn('Glucan + 0.5 H2O -> 0.5 Cellobiose', 'Glucan', 0.0120, chemicals), Rxn('Glucan + H2O -> Glucose', 'Glucan', 0.9000, chemicals), Rxn('Cellobiose + H2O -> 2Glucose', 'Cellobiose', 1.0000, chemicals)] ) self.loss = loss or ParallelRxn([ # Reaction definition Reactant Conversion Rxn('Glucose -> 2 LacticAcid', 'Glucose', 0.0300, chemicals), Rxn('3 Xylose -> 5 LacticAcid', 'Xylose', 0.0300, chemicals), Rxn('3 Arabinose -> 5 LacticAcid', 'Arabinose', 0.0300, chemicals), Rxn('Galactose -> 2 LacticAcid', 'Galactose', 0.0300, chemicals), Rxn('Mannose -> 2 LacticAcid', 'Mannose', 0.0300, chemicals),]) self.cofermentation = cofermentation or ParallelRxn([ # Reaction definition Reactant Conversion Rxn('Glucose -> 2 Ethanol + 2 CO2', 'Glucose', 0.9500, chemicals), Rxn('Glucose + 0.047 CSL + 0.018 DAP -> 6 Z_mobilis + 2.4 H2O', 'Glucose', 0.0200, chemicals), Rxn('Glucose + 2 H2O -> 2 Glycerol + O2', 'Glucose', 0.0040, chemicals), Rxn('Glucose + 2 CO2 -> 2 SuccinicAcid + O2', 'Glucose', 0.0060, chemicals), Rxn('3 Xylose -> 5 Ethanol + 5 CO2', 'Xylose', 0.8500, chemicals), Rxn('Xylose + 0.039 CSL + 0.015 DAP -> 5 Z_mobilis + 2 H2O', 'Xylose', 0.0190, chemicals), Rxn('3 Xylose + 5 H2O -> 5 Glycerol + 2.5 O2', 'Xylose', 0.0030, chemicals), Rxn('Xylose + H2O -> Xylitol + 0.5 O2', 'Xylose', 0.0460, chemicals), Rxn('3 Xylose + 5 CO2 -> 5 SuccinicAcid + 2.5 O2', 'Xylose', 0.0090, 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' def _run(self): feed, *other = self.ins vent, effluent, sidedraw = self.outs effluent.copy_like(feed) vent.P = effluent.P = self.P vent.T = effluent.T = self.T_fermentation vent.phase = 'g' self.saccharification(effluent) self._batch_duty = effluent.Hnet - feed.Hnet sidedraw.copy_like(effluent) sidedraw.mol *= self.saccharification_split effluent.mol -= sidedraw.mol effluent.mix_from([effluent, *other], energy_balance=False) self.loss(effluent) self.cofermentation(effluent) self.CSL_to_constituents(effluent) vent.empty() vent.receive_vent(effluent, energy_balance=False) def _design(self): effluent = self.outs[1] v_0 = effluent.F_vol Design = self.design_results Design['Flow rate'] = v_0 / self.N_reactors tau = self.tau_saccharification + self.tau_cofermentation Design.update(size_batch(v_0, tau, self.tau_0, self.N_reactors, self.V_wf)) Design['Batch duty'] = batch_duty = self._batch_duty Design['Reactor duty'] = reactor_duty = self.Hnet - batch_duty self.add_heat_utility(reactor_duty + batch_duty, effluent.T)
[docs] @cost('Flow rate', 'Recirculation pumps', kW=30, S=340*_gpm2m3hr, cost=47200, n=0.8, BM=2.3, CE=522, N='N_reactors') @cost('Reactor duty', 'Heat exchangers', CE=522, cost=23900, S=-5*_Gcal2kJ, n=0.7, BM=2.2, N='N_reactors') # Based on a similar heat exchanger @cost('Reactor volume', 'Agitators', CE=522, cost=52500, S=1e6*_gal2m3, n=0.5, kW=90, BM=1.5, N='N_reactors') @cost('Reactor volume', 'Reactors', CE=522, cost=844000, S=1e6*_gal2m3, n=0.5, BM=1.5, N='N_reactors') class SimultaneousSaccharificationAndCoFermentation(Unit): _N_ins = 1 _N_outs = 2 _ins_size_is_fixed = False #: Saccharification temperature (K) T_saccharification = 48+273.15 #: Fermentation temperature (K) T_fermentation = 32+273.15 #: Saccharification and Co-Fermentation time (hr) tau = 72 #: Unload and clean up time (hr) tau_0 = 4 #: Working volume fraction (filled tank to total tank volume) V_wf = 0.9 #: Number of reactors N_reactors = 12 _units = {'Flow rate': 'm3/hr', 'Reactor volume': 'm3', 'Batch duty': 'kJ/hr', 'Reactor duty': 'kJ/hr'} def _init(self, P=101325, saccharification=None, loss=None, cofermentation=None): self.P = P chemicals = self.chemicals #: [ParallelReaction] Enzymatic hydrolysis reactions including from #: downstream batch tank in co-fermentation. self.saccharification = saccharification or ParallelRxn([ Rxn('Glucan -> GlucoseOligomer', 'Glucan', 0.0400, chemicals), Rxn('Glucan + 0.5 H2O -> 0.5 Cellobiose', 'Glucan', 0.0120, chemicals), Rxn('Glucan + H2O -> Glucose', 'Glucan', 0.9000, chemicals), Rxn('Cellobiose + H2O -> 2Glucose', 'Cellobiose', 1.0000, chemicals) ]) self.loss = loss or ParallelRxn([ # Reaction definition Reactant Conversion Rxn('Glucose -> 2 LacticAcid', 'Glucose', 0.0300, chemicals), Rxn('3 Xylose -> 5 LacticAcid', 'Xylose', 0.0300, chemicals), Rxn('3 Arabinose -> 5 LacticAcid', 'Arabinose', 0.0300, chemicals), Rxn('Galactose -> 2 LacticAcid', 'Galactose', 0.0300, chemicals), Rxn('Mannose -> 2 LacticAcid', 'Mannose', 0.0300, chemicals),]) self.cofermentation = cofermentation or ParallelRxn([ # Reaction definition Reactant Conversion Rxn('Glucose -> 2 Ethanol + 2 CO2', 'Glucose', 0.9500, chemicals), Rxn('Glucose + 0.047 CSL + 0.018 DAP -> 6 Z_mobilis + 2.4 H2O', 'Glucose', 0.0200, chemicals), Rxn('Glucose + 2 H2O -> 2 Glycerol + O2', 'Glucose', 0.0040, chemicals), Rxn('Glucose + 2 CO2 -> 2 SuccinicAcid + O2', 'Glucose', 0.0060, chemicals), Rxn('3 Xylose -> 5 Ethanol + 5 CO2', 'Xylose', 0.8500, chemicals), Rxn('Xylose + 0.039 CSL + 0.015 DAP -> 5 Z_mobilis + 2 H2O', 'Xylose', 0.0190, chemicals), Rxn('3 Xylose + 5 H2O -> 5 Glycerol + 2.5 O2', 'Xylose', 0.0030, chemicals), Rxn('Xylose + H2O -> Xylitol + 0.5 O2', 'Xylose', 0.0460, chemicals), Rxn('3 Xylose + 5 CO2 -> 5 SuccinicAcid + 2.5 O2', 'Xylose', 0.0090, chemicals), ]) self.CSL_to_constituents = Rxn( 'CSL -> 0.5 H2O + 0.25 LacticAcid + 0.25 Protein', 'CSL', 1.0000, basis='wt', ) self.CSL_to_constituents.basis = 'mol' def _run(self): vent, effluent = self.outs vent.P = effluent.P = self.P vent.T = effluent.T = self.T_fermentation vent.phase = 'g' effluent.mix_from(self.ins, energy_balance=False) self.saccharification(effluent) self.loss(effluent) self.cofermentation(effluent) self.CSL_to_constituents(effluent) vent.empty() vent.receive_vent(effluent, energy_balance=False) def _design(self): effluent = self.outs[1] v_0 = effluent.F_vol Design = self.design_results Design['Flow rate'] = v_0 / self.N_reactors Design.update(size_batch(v_0, self.tau, self.tau_0, self.N_reactors, self.V_wf)) Design['Reactor duty'] = reactor_duty = self.Hnet self.add_heat_utility(reactor_duty, effluent.T)
# %% Pretreatment separations # Membrane separation processes. Perry's Chemical Engineer's Handbook 7th Edition.
[docs] @cost('Flow rate', 'Nanofiltration', kW=18000, S=0.25, units='m3/s', cost=17300, n=0.6, BM=1., CE=bst.units.design_tools.CEPCI_by_year[1996]) @cost('Annual flow rate', 'Membranes and maintenance', S=1., units='m3/yr', cost=0.13, n=1., BM=1., CE=bst.units.design_tools.CEPCI_by_year[1996], annual=True) class ReverseOsmosis(bst.SolidsSeparator): pass
Europe_investment_site_factor = 1.2 euro_to_dollar = 1.04 installation_cost = 115000 # euro volume_treated = 10 * 24 * 30 * 18 # m3 membrane_area = 27.5 # m2 membrane_cost = 95 # euro / m2 cleaning_cost = 50 # euro / m2 maintenance_cost = installation_cost * 0.02 yearly_operating_hours = 8000 operating_cost = membrane_area * (membrane_cost + cleaning_cost) + maintenance_cost * yearly_operating_hours / (18 * 30 * 24) operating_cost_per_volume_treated = operating_cost / volume_treated US_operating_cost = euro_to_dollar * operating_cost_per_volume_treated / Europe_investment_site_factor US_installation_cost = euro_to_dollar * installation_cost / Europe_investment_site_factor CEPCI2013 = bst.units.design_tools.CEPCI_by_year[2013] electricity_cost = 2e3 # euro / yr electricity_price = 0.04 # euro / kWh electricity_demand = electricity_cost / yearly_operating_hours / electricity_price
[docs] @cost('Flow rate', 'Nanofiltration', kW=electricity_demand, S=10, units='m3/hr', cost=US_installation_cost, n=0.6, BM=1., CE=CEPCI2013) @cost('Annual flow rate', 'Membranes and maintenance', S=1., units='m3/yr', cost=US_operating_cost, n=1., BM=1., CE=CEPCI2013, annual=True) class Nanofilter(bst.Unit): _N_ins = 1 _N_outs = 2 volume_reduction = 0.80 lignin_retention = 0.60 NaOH_retention = 0.10 sugar_retention = 0.95 sugars_IDs = ('Glucose', 'Xylose', 'Arabinose', 'Galactose', 'Mannose', 'GlucoseOligomer', 'GalactoseOligomer', 'MannoseOligomer', 'XyloseOligomer', 'ArabinoseOligomer', 'HMF', 'Furfural', 'Xylobiose') lignin_IDs = ('SolubleLignin',) retentate_only_IDs = ('Lignin', 'Glucan', 'Xylan', 'Arabinan', 'Galactan', 'Mannan', 'Solids', 'Ash') def _run(self): feed, = self.ins permeate, retentate = self.outs permeate.empty() retentate.empty() lignin_retention = self.lignin_retention sugar_retention = self.sugar_retention NaOH_retention = self.NaOH_retention volume_reduction = self.volume_reduction water = feed.imass['Water'] permeate.imass['Water'] = volume_reduction * water retentate.imass['Water'] = feed.imass['Water'] - permeate.imass['Water'] retentate.copy_flow(feed, self.retentate_only_IDs) not_permeate_unique = (*self.retentate_only_IDs, *self.lignin_IDs, *self.sugars_IDs, 'NaOH', 'Water') not_permeate_unique = set([self.chemicals[i].ID for i in not_permeate_unique]) permeate_unique = tuple([i.ID for i in self.chemicals if i.ID not in not_permeate_unique]) permeate.copy_flow(feed, permeate_unique) F_mass = feed.F_mass lignin_comps = feed.imass[self.lignin_IDs] sugar_comps = feed.imass[self.sugars_IDs] NaOH = feed.imass['NaOH'] lignin = lignin_comps.sum() sugar = sugar_comps.sum() lignin_def = lignin_comps / lignin sugar_def = sugar_comps / sugar NaOH = feed.imass['NaOH'] feed_mass = np.array([lignin, sugar, NaOH]) z_mass = feed_mass / F_mass K = np.array([lignin_retention, sugar_retention, NaOH_retention]) phi_guess = 0.5 Fa = permeate.F_mass Fb = retentate.F_mass assert abs((Fa + Fb + lignin + sugar + NaOH) - F_mass) < 1e-6 permeate_mass = feed_mass / water * (1 - K) * permeate.imass['Water'] # phi = tmo.separations.compute_phase_fraction(z_mass, K, phi_guess, Fa/F_mass, Fb/F_mass) # x_retentate = z_mass / (phi * K + (1. - phi)) # retentate_mass = x_retentate * (1. - phi) * F_mass permeate.imass[self.lignin_IDs] = permeate_mass[0] * lignin_def permeate.imass[self.sugars_IDs] = permeate_mass[1] * sugar_def permeate.imass['NaOH'] = permeate_mass[2] retentate.mol = feed.mol - permeate.mol assert (permeate.mol >= 0.).all() retentate.T = permeate.T = feed.T
# %% Simple unit operations
[docs] @cost('Flow rate', 'Pump', units='kg/hr', S=402194, CE=522, cost=22500, n=0.8, kW=74.57, BM=2.3) class HydrolysatePump(Unit): pass
[docs] @cost('Flow rate', 'Tank', S=1171, units='kg/hr', CE=522, cost=196000, n=0.7, BM=2) class AmmoniaStorageTank(Unit): pass
[docs] @cost('Flow rate', 'Agitator', S=410846, units='kg/hr', CE=522, cost=None, n=0.5, BM=1.5, kW=14.914) @cost('Flow rate', 'Tank', S=410369, units='kg/hr', CE=522, cost=None, n=0.7, BM=2) class AmmoniaReacidificationTank(Unit): pass
[docs] @cost('Flow rate', 'Tank', S=425878, units='kg/hr', CE=522, cost=636000, n=0.7, BM=1.8) @cost('Flow rate', 'Pump', S=425878, units='kg/hr', CE=522, cost=26800, n=0.8, BM=2.3, kW=93.2125) @cost('Flow rate', 'Agitator', S=425878, units='kg/hr', CE=522, cost=68300, n=0.5, BM=1.5, kW=14.914) class BeerTank(Unit): pass
[docs] @cost('Flow rate', 'Pump', S=292407, units='kg/hr', CE=551, cost=25365, n=0.8, BM=2.3, kW=93.2125) class BlowdownDischargePump(Unit): pass
[docs] @cost('Flow rate', 'Tank', S=1393, units='kg/hr', CE=522, cost=70000, n=0.7, BM=2.6) @cost('Flow rate', 'Pump', S=1393, units='kg/hr', CE=522, cost=3000, n=0.8, BM=3.1, kW=0.37285) @cost('Flow rate', 'Agitator', S=1393, units='kg/hr', CE=522, cost=21200, n=0.5, BM=1.5, kW=7.457) class CSLStorageTank(Unit): pass
[docs] @cost('Flow rate', 'Tank', S=163, units='kg/hr', CE=522, cost=102000, n=0.7, BM=1.8) @cost('Flow rate', 'Pump', S=163, units='kg/hr', CE=522, cost=3000, n=0.8, BM=3.1, kW=0.37735) @cost('Flow rate', 'Agitator', S=163, units='kg/hr', CE=522, cost=9800, n=0.5, BM=1.5, kW=4.10135) @cost('Flow rate', 'Bag unloader', S=163, units='kg/hr', CE=522, cost=30000, n=0.6, BM=1.7) class DAPStorageTank(Unit): pass
[docs] @cost('Flow rate', 'System', S=94697, units='kg/hr', CE=522, cost=13329690, n=0.6, BM=1.7, kW=783) class FeedStockHandling(Unit): pass
[docs] @cost('Flow rate', 'Tank', S=252891, units='kg/hr', CE=522, cost=511000, n=0.7, BM=2) @cost('Flow rate', 'Pump', S=252891, units='kg/hr', CE=522, cost=30000, n=0.8, BM=1.7, kW=55.9) @cost('Flow rate', 'Agitator', S=252891, units='kg/hr', CE=522, cost=90000, n=0.5, BM=1.5, kW=170) class PretreatmentFlash(bst.Flash): pass
[docs] @cost('Duty', 'Heat exchanger', S=8, units='Gcal/hr', CE=522, cost=85000, n=0.7, BM=2.2, magnitude=True) class HydrolysateHeatExchanger(bst.HXutility): pass
[docs] @cost('Duty', 'Heat Exchanger', S=8, units='Gcal/hr', CE=551, cost=92000, n=0.7, BM=2.2, magnitude=True) class PretreatmentWasteHeater(bst.HXutility): pass
[docs] @cost('Duty', 'Heat Exchanger', S=2, units='Gcal/hr', CE=522, cost=34000, n=0.7, BM=2.2, magnitude=True) class WasteVaporCondenser(bst.HXutility): pass
[docs] @cost('Flow rate', 'Pump', S=402194, units='kg/hr', CE=522, cost=22500, n=0.8, BM=2.3, kW=74.57) class HydrolyzatePump(Unit): pass
[docs] @cost('Flow rate', 'Separator', S=39000, units='kg/hr', CE=522, cost=35000000, n=0.7, BM=1.7) class HydrolyzateSolidLiquidSeparator(Unit): pass
[docs] @cost('Flow rate', 'Tank', S=410369, units='kg/hr', CE=522, cost=236000, n=0.7, BM=2) @cost('Flow rate', 'Agitator', S=410369, units='kg/hr', CE=522, cost=21900, n=0.5, BM=1.5, kW=7.457) class AmmoniaAdditionTank(bst.MixTank): pass
[docs] @cost('Flow rate', 'Mixer', S=157478, units='kg/hr', CE=522, cost=5000, n=0.5, BM=1) class AmmoniaMixer(bst.Mixer): pass
[docs] @cost('Flow rate', 'Mixer', S=380000, units='kg/hr', CE=522, cost=109000, n=0.5, BM=1.7, kW=74.57) class EnzymeHydrolysateMixer(bst.Mixer): pass
[docs] @cost('Flow rate', 'Mixer', S=136260, units='kg/hr', CE=522, cost=6000, n=0.5, BM=1) class SulfuricAcidMixer(bst.Mixer): pass
[docs] @cost('Flow rate', 'Tank', S=264116, units='kg/hr', CE=522, cost=203000, n=0.7, BM=2) @cost('Flow rate', 'Pump', S=264116, units='kg/hr', CE=551, cost=17480, n=0.8, BM=1.7, kW=55.9) @cost('Flow rate', 'Agitator', S=264116, units='kg/hr', CE=522, cost=90000, n=0.5, BM=1.5, kW=170) class OligomerConversionTank(Unit): pass
[docs] @cost('Flow rate', 'Pump', S=402195, units='kg/hr', CE=522, cost=None, n=0.8, BM=2.3, kW=44.742) class ReacidifiedHydrolyzatePump(Unit): pass
[docs] @cost('Flow rate', 'Pump', S=43149, units='kg/hr', CE=522, cost=8200, n=0.8, BM=2.3, kW=7.457) @cost('Flow rate', 'Tank', S=40414, units='kg/hr', CE=522, cost=439000, n=0.7, BM=1.8) @cost('Flow rate', 'Agitator', S=40414, units='kg/hr', CE=522, cost=31800, n=0.5, BM=1.5, kW=11.3205) class SeedHoldTank(Unit): pass
[docs] @cost('Flow rate', 'Tank', S=1981, units='kg/hr', CE=522, cost=96000, n=0.7, BM=1.5) @cost('Flow rate', 'Pump', S=1981, units='kg/hr', CE=522, cost=7493, n=0.8, BM=2.3, kW=0.5) class SulfuricAcidStorageTank(Unit): pass
[docs] @cost('Flow rate', 'Tank', S=1981, units='kg/hr', CE=551, cost=6210, n=0.7, BM=2) @cost('Flow rate', 'Pump', S=3720, units='kg/hr', CE=522, cost=8000, n=0.8, BM=2.3, kW=1) class SulfuricAcidTank(Unit): pass