11. Building a biorefinery#

Putting it all together, we can finally build a complete biorefinery from scratch or by leveraging published models in BioSTEAM. In this tutorial, we show to build: * A sugarcane ethanol biorefinery from scratch. * A cellulosic ethanol biorefinery from system factories.

11.1. Sugarcane ethanol#

In this example, a sugarcane biorefinery producing ethanol is designed according to [1], including all process setings, prices, and assumptions.

11.1.1. Thermodynamic property package#

First create the thermodynamic property package to define the chemicals in the process. We can follow the example in Thermosteam’s docs to do this:

[1]:
import biosteam as bst
bst.nbtutorial() # Ignore warnings and reset local BioSTEAM preferences

chemicals = bst.Chemicals(
    ['Water', # Define common chemicals by name
     'Ethanol',
     'Octane',
     bst.Chemical('Glucose', phase='s'), # Set the phase for chemicals not in vapor-liquid equilibrium
     bst.Chemical('Sucrose', phase='s'),
     bst.Chemical('H3PO4', phase='s'),
     bst.Chemical('P4O10',
                  rho=1540, # Density [kg/m3]
                  default=True,  # Default other chemicals properties like viscosity to that of water at 25 C
                  phase='s'),
     bst.Chemical('CO2', phase='g'),
     bst.Chemical('O2', phase='g'),
     bst.Chemical('CH4', phase='g'),
     bst.Chemical('Cellulose',
                  Cp=1.364, # Heat capacity [kJ/kg]
                  rho=1540, # Density [kg/m3]
                  default=True, # Default other chemicals properties like viscosity to that of water at 25 C
                  search_db=False, # Not in database, so do not search the database
                  phase='s',
                  formula="C6H10O5", # Glucose monomer minus water, molecular weight is computed based on formula
                  Hf=-975708.8), # Heat of formation [J/mol]
     bst.Chemical('Hemicellulose',
                  Cp=1.364,
                  rho=1540,
                  default=True,
                  search_db=False,
                  phase='s',
                  formula="C5H8O5", # Xylose monomer minus water
                  Hf=-761906.4),
     bst.Chemical('Lignin',
                  Cp=1.364,
                  rho=1540,
                  default=True,
                  search_db=False,
                  phase='s',
                  formula='C8H8O3', # Vainillin formula
                  Hf=-452909.632),
     bst.Chemical('Flocculant',
                  Cp=4.184,
                  rho=1540,
                  default=True,
                  search_db=False,
                  phase='s',
                  MW=1.), # No formula, so molecular weight should be defined
     bst.Chemical('Solids',
                  Cp=1.100,
                  rho=1540,
                  default=True,
                  search_db=False,
                  phase='s',
                  MW=1.),
     bst.Chemical('DryYeast',
                  rho=1540,
                  default=True,
                  search_db=False,
                  phase='s',
                  MW=1.,
                  synonyms={'Yeast'}),
     bst.Chemical('CaO',
                  Cp=1.02388,
                  rho=1540,
                  default=True,
                  search_db=False,
                  phase='s',
                  formula='CaO'),
     bst.Chemical('Ash',
                  rho=1540,
                  Cp=0.37656,
                  default=True,
                  search_db=False,
                  phase='s',
                  MW=1.)]
)
bst.settings.set_thermo(chemicals)

11.1.2. Process settings#

Before any simulations, we define the conditions of all our utilities and the Chemical Engineering Plant Cost Indes (CEPCI):

[2]:
bst.settings.CEPCI = 567 # CEPCI: 2013
bst.settings.electricity_price = 0.065
# Steam is produced on-site by a boiler,
# so make it the only available heating agent.
steam_utility = bst.settings.get_agent('low_pressure_steam')
bst.settings.heating_agents = [steam_utility]
steam_utility.heat_transfer_efficiency = 0.9
steam_utility.T = 529.2
steam_utility.P = 44e5

# Steam, cooling water, and chilled water are regenerated by
# on-site facilities. The regeneration and heat transfer
# prices are given accounted for by the capital cost and
# electricity consumption of these facilities
steam_utility.regeneration_price = 0.
bst.settings.get_agent('cooling_water').regeneration_price = 0
bst.settings.get_agent('chilled_water').heat_transfer_price = 0

# Raw material price (USD/kg)
price = {'Sugar cane': 0.03455, # 70% m.c
         'Water': 0.000353,
         'HCl': 0.205,
         'Lime': 0.077,
         'H3PO4': 0, # Not significant
         'NaOH':0.41,
         'Protease': 0.5,
         'Polymer': 0, # Not significant
         'Steam': 0.017,
         'Ethanol': 0.789,
         'Waste': -0.33,
         'Gasoline': 0.756} # 2 USD/gal

11.1.3. Unit operations and simulation#

First define the unit operations for feedstock handling and juicing:

[3]:
from biosteam import units
import numpy as np

bst.main_flowsheet.set_flowsheet('sugarcane_ethanol')

# We can create streams and set component splits faster by defining chemical groups
chemicals.define_group(
    name='Fiber',
    IDs=['Cellulose', 'Hemicellulose', 'Lignin'],
    composition=[0.4704 , 0.2775, 0.2520],
    wt=True, # Composition is given as weight
)
chemicals.define_group(
    name='Sugar',
    IDs=['Sucrose', 'Glucose'],
    # Default composition as equimolar
)

sugarcane = bst.Stream(
    'sugarcane',
    Water=0.7,
    Glucose=0.01208,
    Sucrose=0.1369,
    Ash=0.006,
    Fiber=0.13,
    Solids=0.015,
    total_flow=333334.2,
    units='kg/hr',
    price=price['Sugar cane']
)

enzyme = bst.Stream('enzyme',
                    Cellulose=100, Water=900, units='kg/hr',
                    price=price['Protease'])

imbibition_water = bst.Stream('imbibition_water',
                              Water=87023.35, units='kg/hr',
                              T = 338.15)

H3PO4 = bst.Stream('H3PO4',
                   H3PO4=74.23, Water=13.10, units='kg/hr',
                   price=price['H3PO4'])  # to T203

lime = bst.Stream('lime',
                  CaO=333.00, Water=2200.00, units='kg/hr',
                  price=price['Lime'])  # to P5

polymer = bst.Stream('polymer',
                     Flocculant=0.83, units='kg/hr',
                     price=price['Polymer'])  # to T205

rvf_wash_water = bst.Stream('rvf_wash_water',
                            Water=16770, units='kg/hr',
                            T=363.15)  # to C202

### Unit operations ###

# Feed from storage
U101 = units.ConveyingBelt('U101', sugarcane)

# Separate metals
U102 = units.MagneticSeparator('U102', U101-0)

# Shredded cane
U103 = units.Shredder('U103', U102-0)

# Hydrolyze starch
T201 = units.EnzymeTreatment('T201', [U103-0, enzyme], T=323.15)  # T=50

# Finely crush lipid cane
imbibition_water_recycle = bst.Stream() # To connect later
U201 = units.CrushingMill('U201', [T201-0, imbibition_water_recycle],
                          split=dict(Ash=0.92,
                                     Fiber=0.92,
                                     Sugar=0.04,
                                     Solids=1),
                          moisture_content=0.5)

# Convey out bagasse
U202 = units.ConveyingBelt('U202', U201-0, outs='Bagasse')

# Screen out fibers
S201 = units.VibratingScreen('S201', U201-1,
                             split=dict(Ash=0.35,
                                        Fiber=0.35,
                                        Sugar=0.88,
                                        Water=0.88,
                                        Solids=0))

# Mix in water
M201 = units.Mixer('M201', [S201-1, imbibition_water], imbibition_water_recycle)

# Store juice before treatment
T202 = units.StorageTank('T202', S201-0, tau=4, vessel_material='Carbon steel')

# Heat up before adding acid
H201 = units.HXutility('H201', T202-0, T=343.15)

# Mix in acid
T203 = units.MixTank('T203', [H201-0, H3PO4])

# Pump acid solution
P201 = units.Pump('P201', T203-0)

# Mix lime solution
T204 = units.MixTank('T204', [P201-0, lime], tau=0.10)

# Blend acid lipid solution with lime
T205 = units.MixTank('T205', T204-0, tau=0.10)
P202 = units.Pump('P202', T205-0)

# Mix recycle
RVF_recycle = bst.Stream() # From rotary vacuum filter; connect later
M202 = units.Mixer('M202', [P202-0, RVF_recycle])

# Heat before adding flocculant
H202 = units.HXutility('H202', M202-0, T=372.15)

# Mix in flocculant
T206 = units.MixTank('T206', [H202-0, polymer])
T206.tau = 0.10

# Separate residual solids
C201 = units.Clarifier('C201', T206-0,
                       split=dict(Ash=0,
                                  CaO=0,
                                  Fiber=0,
                                  Flocculant=0.522,
                                  Sugar=0.522,
                                  H3PO4=0.522,
                                  Water=0.522))

# Remove solids as filter cake
C202 = units.RVF('C202', [C201-1, rvf_wash_water],
                 outs=('filter_cake', ''),
                 moisture_content=0.80,
                 split=dict(Ash=0.85,
                            CaO=0.85,
                            Fiber=0.85,
                            Sugar=0.01))
P203 = units.Pump('P203', C202-1, RVF_recycle)


# Screen out small fibers from sugar stream
S202 = units.VibratingScreen('S202', C201-0,
                             outs=('', 'fiber_fines'),
                             split=dict(Ash=1.0,
                                        CaO=1.0,
                                        Fiber=1.0,
                                        Flocculant=0.0,
                                        Sugar=0.998,
                                        H3PO4=1.0,
                                        Water=0.998))
S202.mesh_opening = 2

### Process specifications ###

# Specifications dependent on lipid cane flow rate
@U103.add_specification(run=True) # Run unit operation afterwords
def correct_flows():
    feedstock = U101.ins[0]
    F_mass = feedstock.F_mass
    # correct enzyme, lime, phosphoric acid, and imbibition water
    enzyme.imass['Cellulose', 'Water'] = 0.003 * F_mass * np.array([0.1, 0.9])
    lime.imass['CaO', 'Water'] = 0.001 * F_mass * np.array([0.046, 0.954])
    H3PO4.imass['H3PO4', 'Water'] = 0.00025 * F_mass
    imbibition_water.imass['Water'] = 0.25* F_mass

# Specifications within a system
@P202.add_specification(run=True)
def correct_wash_water():
    solids = P202.ins[0].imol['Ash', 'CaO', 'Fiber'].sum()
    rvf_wash_water.imol['Water'] = 0.0574 * solids

bst.main_flowsheet.diagram(format='png') # Flow sheet up until now
../_images/tutorial_Building_a_biorefinery_12_0.png

Define unit operations for ethanol production from juice:

[4]:
### Streams ###

# Fresh water
stripping_water = bst.Stream('stripping_water', Water=5000, units='kg/hr')

# Gasoline
denaturant = bst.Stream('denaturant', Octane=230.69,
                        units='kg/hr', price=price['Gasoline'])

# Yeast
yeast = bst.Stream('yeast', Water=24700, DryYeast=10300, units='kg/hr')

# Ethanol product
ethanol = bst.Stream('ethanol', price=price['Ethanol'])

### Units ###

# Split sugar solution
S301 = units.Splitter('S301',  S202-0, split=0.10)

# Concentrate sugars
F301 = units.MultiEffectEvaporator('F301', S301-1,
                                   P=(101325, 73581, 50892, 32777),
                                   V_definition='First-effect',
                                   V=0.1) # fraction evaporated
P306 = units.Pump('P306', F301-0)

# Mix sugar solutions
M301 = units.Mixer('M301', [P306-0, S301-0])

F301.target_sugar_concentration = 0.23 # wt. % sugar
@F301.add_bounded_numerical_specification(x0=0, x1=1, xtol=1e-5, ytol=1e-2)
def sugar_concentration_at_fraction_evaporated(V):
    F301.V = V
    F301.run_until(M301, inclusive=True) # Run all units starting from F301 to M301
    sugar_concentration = M301.outs[0].get_mass_fraction('Sugar')
    return F301.target_sugar_concentration - sugar_concentration

# Cool for fermentation
H301 = units.HXutility('H301', M301-0, T=295.15)

# Yeast preparation
T305 = units.MixTank('T305', yeast)
T305.tau = 0.1

# Ethanol Production
R301 = units.Fermentation('R301', [H301-0, T305-0], outs=('CO2', ''), tau=9, efficiency=0.90, N=4)
R301.cell_growth_reaction.X = 0. # Ignore for simplicity
T301 = units.StorageTank('T301', R301-1, tau=4, vessel_material='Carbon steel')
T301.line = 'Beer tank' # Changes name on the diagram

D301 = units.VentScrubber('D301', ins=(stripping_water, R301-0),
                          outs=('vent', ''),
                          gas=('CO2',))

# Separate 99% of yeast
C301 = units.SolidsCentrifuge('C301', T301-0, outs=('recycle_yeast', ''),
                            moisture_content=0.5,
                            split=(1, 0.99999, 0.99), # This gets reverted in the next line
                            order=('Ethanol', 'Glucose',  'DryYeast'),
                            solids=('DryYeast',))
C301.split[:] = 1. - C301.split

# Add bottoms from scrubber
M302 = units.Mixer('M302', [C301-1, D301-1])
P301 = units.Pump('P301', M302-0)

# Heat up before beer column
# Exchange heat with stillage
bottoms_product = bst.Stream() # Bottoms product from beer column, connect later
H302 = units.HXprocess('H302', [P301-0, bottoms_product], U=1.28)

# Beer column
D302 = units.BinaryDistillation(
    'D302', H302-0, P=2 * 101325,
    Lr=0.99999, Hr=0.60, # Light and heavy key recoveries
    LHK=('Ethanol', 'Water'), # Light and heavy key
    k=1.25, # Ratio of actual reflux over minimum reflux
    Rmin=0.01, # Minimum allowable reflux ratio
)
D302.tray_material = 'Stainless steel 304'
D302.vessel_material = 'Stainless steel 304'
D302.reboiler.U = 1.85
P302 = units.Pump('P302', D302-1, bottoms_product)

# Mix ethanol Recycle (Set-up)
molecular_sieve_recycle = bst.Stream()
M303 = units.Mixer('M303', [D302-0, molecular_sieve_recycle])

D303 = units.BinaryDistillation(
    'D303', M303-0,
    P=10 * 101325, # Higher pressure to enable heat exchanger between condenser and multi-effect evaporator
    y_top=0.80805, x_bot=3.91e-06,  # Molar fraction of light key in the distillate and bottoms product
    k=1.25,
    LHK=('Ethanol', 'Water'),
    tray_material='Stainless steel 304',
    vessel_material='Stainless steel 304',
    is_divided=True
)
D303.reboiler.U = 1.85
P303 = units.Pump('P303', D303-1)

# Superheat vapor for mol sieve
H303 = units.HXutility('H303', D303-0, T=115+273.15, V=1, heat_only=True)

# Molecular sieve
U301 = units.MolecularSieve('U301', H303-0, [molecular_sieve_recycle, ''],
                            split=dict(Ethanol=0.1621,
                                       Water=0.925))

# Condense ethanol product
H304 = units.HXutility('H304', U301-1, V=0, T=340.)
T302 = units.StorageTank('T302', H304-0, tau=7*24, # 1 week storage capacity
                         vessel_type='Floating roof',
                         vessel_material='Carbon steel')
P304 = units.Pump('P304', T302-0)

# Storage for denaturant
T303 = units.StorageTank('T303', denaturant, tau=7*24,
                         vessel_type='Floating roof',
                         vessel_material='Carbon steel')
P305 = units.Pump('P305', T303-0)

# Denatured ethanol product
T304 = units.MixTank('T304', [P305-0, P304-0], outs=ethanol)
T304.tau = 0.10 # 6 min residence time

# Waste water
M305 = units.Mixer('M305', [P303-0, F301-1], outs='wastewater')

@P304.add_specification(run=True)
def adjust_denaturant():
    pure_ethanol = P304.ins[0]
    denaturant.imol['Octane'] = 0.021*pure_ethanol.F_mass/114.232

bst.main_flowsheet.diagram('thorough', format='png') # Flowsheet up until now
../_images/tutorial_Building_a_biorefinery_14_0.png

Define facilities which use data from unit operations to solve for utility requirements:

[5]:
s = bst.main_flowsheet.stream
BT = bst.BoilerTurbogenerator('BT',
                                (U202-0, '', 'boiler_makeup_water', 'natural_gas', '', ''),
                                boiler_efficiency=0.80,
                                turbogenerator_efficiency=0.85)

CT = bst.CoolingTower('CT')
makeup_water = bst.Stream('makeup_water', price=0.000254)

CWP = bst.ChilledWaterPackage('CWP')
PWC = bst.ProcessWaterCenter('PWC',
                               ins=(bst.Stream(), makeup_water, P303-0),
                               outs=(),
                               makeup_water_streams=(s.cooling_tower_makeup_water,
                                                     s.boiler_makeup_water),
                               process_water_streams=(imbibition_water,
                                                      rvf_wash_water,
                                                      stripping_water,
                                                      s.cooling_tower_makeup_water,
                                                      s.boiler_makeup_water))
HXN = bst.HeatExchangerNetwork('HXN', units=[F301, D303.condenser])

Create the system and simulate:

[6]:
sugarcane_sys = bst.main_flowsheet.create_system('sugarcane_sys')
sugarcane_sys.simulate()
sugarcane_sys.diagram('thorough', format='png')
../_images/tutorial_Building_a_biorefinery_18_0.png

BioSTEAM generates nested systems to solve recycle streams more robustly. It may be fun to view these systems as follows:

[7]:
sugarcane_sys.diagram('cluster', format='png') # Red streams are recycles (i.e. tear streams)
../_images/tutorial_Building_a_biorefinery_20_0.png
[8]:
sugarcane_sys.print() # Show system simulation order
System('sugarcane_sys',
    [U101,
     U102,
     U103,
     T201,
     System('SYS1',
        [U201,
         S201,
         M201],
        recycle=M201-0),
     T202,
     H201,
     T203,
     P201,
     T204,
     T205,
     P202,
     System('SYS2',
        [M202,
         H202,
         T206,
         C201,
         C202,
         P203],
        recycle=P203-0),
     S202,
     S301,
     F301,
     P306,
     M301,
     H301,
     T305,
     R301,
     T301,
     C301,
     D301,
     M302,
     P301,
     System('SYS3',
        [H302,
         D302,
         P302],
        recycle=P302-0),
     System('SYS4',
        [M303,
         D303,
         H303,
         U301],
        recycle=U301-0),
     H304,
     T302,
     P304,
     T303,
     P305,
     T304,
     P303,
     M305,
     U202],
    facilities=[HXN,
     CWP,
     BT,
     CT,
     PWC])

11.1.4. Biorefinery characterization#

Generate a breakdown of results by biorefinery area with BioSTEAM’s UnitGroup objects:

[9]:
UnitGroup = bst.process_tools.UnitGroup
juicing = UnitGroup('Juicing',
                    [U101, U102, U103, T201,
                     U201, S201, M201, T202,
                     H201, T203, P201, T204,
                     T205, P202, M202, H202,
                     T206, C201, C202, P203,
                     S202])
ethanol_production = UnitGroup('Ethanol production',
                               [S301, F301, P306, M301,
                                H301, T305, R301, T301,
                                C301, M302, P301, H302,
                                D302, P302, M303, D303,
                                H303, U301, H304, T302,
                                P304, T303, P305, T304,
                                D301, P303, M305])
facilities = UnitGroup('Facilities', sugarcane_sys.facilities)
groups = [juicing, ethanol_production, facilities]
UnitGroup.df_from_groups(groups)

[9]:
Installed equipment cost [MM$] Cooling duty [GJ/hr] Heating duty [GJ/hr] Electricity consumption [MW] Material cost [USD/hr]
Juicing 20.2 0 428 4.43 1.2e+04
Ethanol production 18.5 342 406 0.624 362
Facilities 65.1 57.7 0 3.68 16.2
[10]:
# It is also possible to automatically group units by area
area_groups = UnitGroup.group_by_area(sugarcane_sys.units)

# Use short names for metrics
for group in area_groups: group.autofill_metrics(shorthand=True)

area_names = {'0': 'Facilities',
              '100': 'Feedstock handling',
              '200': 'Juicing',
              '300': 'Ethanol production'}
for i in area_groups: i.name = area_names[i.name]
UnitGroup.df_from_groups(area_groups)
[10]:
Inst. eq. cost [MM$] Cooling [GJ/hr] Heating [GJ/hr] Elec. cons. [MW] Mat. cost [USD/hr]
Facilities 65.1 57.7 0 3.68 16.2
Feedstock handling 5.04 0 0 2.01 1.15e+04
Juicing 15.2 0 428 2.43 526
Ethanol production 18.5 342 406 0.624 362
[11]:
# For convinience, BioSTEAM has plotting methods centered on UnitGroup objects
bst.plots.plot_unit_groups(area_groups, fraction=True)
[11]:
(<Figure size 640x480 with 3 Axes>,
 array([<Axes: ylabel='Cost and Utility Breakdown [%]'>], dtype=object))
../_images/tutorial_Building_a_biorefinery_26_1.png

11.1.5. Techno-Economic Analysis (TEA)#

Here we use the default TEA setup used in [1] to solve for the internal rate of return (IRR) at a net pressent value (NPV) of 0:

[12]:
import biorefineries.sugarcane as sc
sugarcane_tea = sc.create_tea(sugarcane_sys)
sugarcane_tea.solve_IRR()
[12]:
0.06643622782993241

This result may be slightly off from the referenced study [1] due to some simplifications on yeast production. A more rigorous model is available in biorefineries.sugarcane. For additional details on TEA, checkout the chapter on TEA

11.2. Cellulosic ethanol#

In this example, two biorefinery configurations for cellulosic ethanol production is designed: one processing cornstover with dilute acid pretreatment [2] and another processing switchgrass with AFEX pretreatment [3]. For example purposes, assumptions fermentation performance in the switchgrass biorefinery are the same as in the cornstover biorefinery.

OPTION 1: Use only SystemFactory objects

[13]:
from biorefineries import cellulosic
from biorefineries.ethanol import create_ethanol_purification_system
from biorefineries.tea import create_cellulosic_ethanol_tea
import biosteam as bst

# Set property package
chemicals = cellulosic.create_cellulosic_ethanol_chemicals()
bst.settings.set_thermo(chemicals)

# Load utility conditions
cellulosic.load_process_settings()

@bst.SystemFactory(
    ID='cellulosic_ethanol_sys',
    ins=['feedstock'],
    outs=[dict(ID='ethanol', price=0.5)],
)
def create_cellulosic_ethanol_system(
        ins, outs,
        SF_pretreatment, # User SystemFactory
    ):
    feedstock, = ins
    ethanol, = outs
    U101 = cellulosic.units.FeedStockHandling('U101', feedstock)
    U101.cost_items['System'].cost = 0.
    pretreatment_sys = SF_pretreatment(
        ins=U101-0,
        mockup=True
    )
    fermentation_sys = cellulosic.create_cellulosic_fermentation_system(
        ins=pretreatment_sys.get_outlet('pretreated_biomass'),
        # Valid arguments include:
        # Integrated Bioprocess (IB)
        # Simultaneous Saccharification and Co-Fermentation (SSCF)
        # Saccharification and Co-Fermentation (SCF)
        kind='IB',
        mockup=True,
    )
    ethanol_purification_sys = create_ethanol_purification_system(
        ins=fermentation_sys.get_outlet('beer'),
        outs=[ethanol],
        mockup=True,
    )
    ethanol, stillage, stripper_bottoms_product = ethanol_purification_sys.outs
    water = bst.Stream(Water=1, T=47+273.15, P=3.9*101325, units='kg/hr')
    S401 = bst.PressureFilter('S401', (stillage, water))
    bst.create_all_facilities(
        # Certain facilities like the Fire Water Tank (in case there is a fire)
        # is sized based on feedstock flow rate
        feedstock,
        # Blowdown water from co-heat and power generation is
        # sent to wastewater treatment. Although this can be ignored
        # because the blowdown is negligible, we add it here for completition
        blowdown_recycle=True,
        recycle_process_water_streams=[stripper_bottoms_product],
        HXN=False, # No heat exchanger network
    )

# Create feedstocks
cornstover = bst.Stream(
    ID='cornstover',
    price=0.0516,
    total_flow=104229.16,
    units='kg/hr',
    Water=0.20215,
    Sucrose=0.00623,
    Extract=0.11846,
    Acetate=0.01464,
    Ash=0.03986,
    Lignin=0.12744,
    Protein=0.02507,
    Glucan=0.28302,
    Xylan=0.15788,
    Arabinan=0.01925,
    Mannan=0.00485,
    Galactan=0.00116,
)
switchgrass = bst.Stream(
    ID='switchgrass',
    total_flow=104229.16,
    price=0.08,
    units='kg/hr',
    Arabinan=0.02789,
    Galactan=0.01044,
    Glucan=0.2717,
    Xylan=0.21215,
    Mannan=0.00594,
    Lignin=0.17112,
    Ash=0.01619,
    Extract=0.0756,
    Acetate=0.00897,
    Water=0.2,
)
bst.main_flowsheet.set_flowsheet('cornstover')
# Create biorefinery systems
cornstover_sys = create_cellulosic_ethanol_system(
    ID='cornstover_sys',
    ins=cornstover,
    SF_pretreatment=cellulosic.create_dilute_acid_pretreatment_system,
)
bst.main_flowsheet.set_flowsheet('switchgrass')
switchgrass_sys = create_cellulosic_ethanol_system(
    ID='switchgrass_sys',
    ins=switchgrass,
    SF_pretreatment=cellulosic.create_ammonia_fiber_expansion_pretreatment_system,
)
# Simulate biorefineries
cornstover_sys.simulate()
switchgrass_sys.simulate()

# Perform TEA
cornstover_tea = create_cellulosic_ethanol_tea(cornstover_sys)
switchgrass_tea = create_cellulosic_ethanol_tea(switchgrass_sys)
print('Cornstover Biorefinery MESP:',
      format(cornstover_tea.solve_price(cornstover_sys-0), '.2g'),
     'USD/kg')
print('Switchgrass Biorefinery MESP:',
      format(switchgrass_tea.solve_price(switchgrass_sys-0), '.2g'),
      'USD/kg')
Cornstover Biorefinery MESP: 0.69 USD/kg
Switchgrass Biorefinery MESP: 1.3 USD/kg

OPTION 2: Plug and play with SystemMesh objects

[14]:
bst.main_flowsheet.set_flowsheet('cellulosic')
bst.main_flowsheet.clear()

water = bst.Stream(Water=1, T=47+273.15, P=3.9*101325, units='kg/hr')

# Setup system mesh for cornstover / dilute acid pretreatment
SM = bst.SystemMesh()
U101 = cellulosic.units.FeedStockHandling('U101', ins=cornstover)
SM.add('feedstock_handling', U101)
SM.add('pretreatment', cellulosic.create_dilute_acid_pretreatment_system(ins=U101-0))
SM.add('fermentation',
    # Valid 'kind' for cellulosic fermentation system include:
    # - Integrated Bioprocess (IB).
    # - Simultaneous Saccharification and Co-Fermentation (SSCF).
    # - Saccharification and Co-Fermentation (SCF).
    cellulosic.create_cellulosic_fermentation_system(kind='IB'),
)
SM.add('ethanol_purification', create_ethanol_purification_system())
SM.add('pressure_filter', bst.PressureFilter('S401', ('stillage', water)))
SM.add('facilities',
    # Includes wastewater treatment, utilities, and more.
    bst.create_all_facilities(
        # Certain facilities like the Fire Water Tank (in case there is a fire)
        # is sized based on feedstock flow rate
        feedstock=U101-0,
        # Blowdown water from co-heat and power generation is
        # sent to wastewater treatment. Although this can be ignored
        # because the blowdown is negligible, we add it here for completition
        blowdown_recycle=True,
        HXN=False, # No heat exchanger network
    )
)

# Create and simulate cornstover biorefinery and perform TEA
cornstover_sys = SM(ID='cornstover_sys')
cornstover_sys.empty_outlet_streams()
cornstover_sys.simulate()
cornstover_tea = create_cellulosic_ethanol_tea(cornstover_sys)
ethanol = bst.main_flowsheet('ethanol')
print('Cornstover Biorefinery MESP:',
      format(cornstover_tea.solve_price(ethanol), '.2g'),
     'USD/kg')

# Setup system mesh for switchgrass / AFEX pretreatment
U101.feed = switchgrass
SM.add('pretreatment', cellulosic.create_ammonia_fiber_expansion_pretreatment_system(ins=U101-0))

# Create and simulate switchgrass biorefinery and perform TEA
switchgrass_sys = SM(ID='switchgrass_sys')
switchgrass_sys.empty_outlet_streams()
switchgrass_sys.simulate()
switchgrass_tea = create_cellulosic_ethanol_tea(switchgrass_sys)
print('Switchgrass Biorefinery MESP:',
      format(switchgrass_tea.solve_price(ethanol), '.2g'),
      'USD/kg')
Cornstover Biorefinery MESP: 0.69 USD/kg
Switchgrass Biorefinery MESP: 1.3 USD/kg

Note that results from using the system mesh matches the results from using the system factory. We were able to skip connecting streams between systems because the system mesh takes care of connecting streams with the same names:

[15]:
SM.show()
SystemMesh:
feedstock_handling (U101)
ins  [0] switchgrass
outs [0] s2 to 0-pretreatment
pretreatment (AFEX_pretreatment_sys)
ins  [0] s2 from feedstock_handling-0
     [1] ammonia
outs [0] pretreated_biomass to 0-fermentation
fermentation (cellulosic_fermentation_sys)
ins  [0] pretreated_biomass from pretreatment-0
     [1] cellulase
     [2] saccharification_water
     [3] DAP
     [4] CSL
outs [0] vent
     [1] beer to 0-ethanol_purification
ethanol_purification (ethanol_purification_sys)
ins  [0] beer from fermentation-1
     [1] denaturant
outs [0] ethanol
     [1] stillage to 0-pressure_filter
     [2] recycle_process_water to M1-0
pressure_filter (S401)
ins  [0] stillage from ethanol_purification-1
     [1] s1
outs [0] s47 to slurry_mixer-0
     [1] s48 to M601-0
facilities (CT, CWP, M601, WWTC, ...)

Note that here we used actual systems and units instead of system factories for the system mesh. A system mesh object offers the flexibility to use units, lists of units, systems, mock-systems, system factories, and even functions that create units to create your new system.

11.2.1. References#

  1. Huang, H., Long, S. & Singh, V. Techno-economic analysis of biodiesel and ethanol co-production from lipid-producing sugarcane. Biofuels, Bioprod. Bioref. 10, 299–315 (2016).

  2. Humbird, D. et al. Process design and economics for biochemical conversion of lignocellulosic biomass to ethanol: Dilute-acid pretreatment and enzymatic hydrolysis of corn stover. http://www.nrel.gov/docs/fy11osti/47764.pdf (2011).

  3. Serate, J. et al. Controlling microbial contamination during hydrolysis of AFEX-pretreated corn stover and switchgrass: effects on hydrolysate composition, microbial response and fermentation. Biotechnol Biofuels 8, 180 (2015).