def system(): bst.main_flowsheet.clear() MockChemical = bst.Chemical('MockChemical', phase='l', search_db=False, default=True) bst.settings.set_thermo([MockChemical]) with bst.System('MockSystem') as system: feed = bst.Stream('feed', MockChemical=1e6, units='kg/hr', price=0.150) product = bst.Stream('product', price=0.151) M1 = bst.MixTank('M1', feed, 's1') H1 = bst.HXutility('H1', M1-0, product, T=325, V=0.) system.simulate() return system
def get_grouped_species(stream, units='kmol/hr'): s = bst.Stream(species=species) s.setflow(flow=stream.mol, species=stream.species.IDs) return pd.Series({ i: s.getflow(*j, units=units).sum() for i, j in species_groups.items() })
def test_kolmogorov_smirnov_d(model): # Test made by Yalin. import numpy as np import biosteam as bst from chaospy import distributions as shape bst.settings.set_thermo(['H2O', 'Ethanol'], cache=True) s1 = bst.Stream('s1', H2O=100) M1 = bst.MixTank(ins=s1) M2 = bst.MixTank(ins=M1 - 0) sys = bst.System('sys', path=(M1, M2)) model = bst.Model(sys) baseline = 1 distribution = shape.Uniform(lower=0.5, upper=1.5) @model.parameter(name='M1 tau', element=M1, kind='coupled', distribution=distribution, units='hr', baseline=baseline) def set_M1_tau(i): M1.tau = i baseline = 1.75 distribution = shape.Uniform(lower=1, upper=2) @model.parameter(name='M2 tau', element=M2, kind='coupled', distribution=distribution, units='hr', baseline=baseline) def set_M2_tau(i): M2.tau = i model.metrics = [ bst.Metric(name='tau1', getter=lambda: M1.tau, units='hr', element=M1), bst.Metric(name='tau2', getter=lambda: M2.tau, units='hr', element=M2), ] np.random.seed(3221) samples = model.sample(100, rule='L') model.load_samples(samples) model.evaluate() D, p = model.kolmogorov_smirnov_d( thresholds=[1, 1.5]) # Just make sure it works for now
def create_streams(num): bst.settings.set_thermo(chems) bst_s = [] for n in range(num): s = bst.Stream(Methanol=100 * (n + 1), Ethanol=100 * (n + 1), units='kg/hr') bst_s.append(s) qs.set_thermo(cmps) qs_ws = [] for n in range(num): ws = qs.WasteStream(Methanol=100 * (n + 1), Ethanol=100 * (n + 1), units='kg/hr') qs_ws.append(ws) return bst_s, qs_ws
def __init__(self, ID='', ins=None, outs=(), P=101325): Unit.__init__(self, ID, ins, outs) self.P = P #: [ParallelReaction] Enzymatic hydrolysis reactions including from downstream batch tank in co-fermentation. self.saccharification = ParallelRxn([ # Reaction definition Reactant Conversion Rxn('Glucan -> GlucoseOligomer', 'Glucan', 0.0400), Rxn('Glucan + 0.5 H2O -> 0.5 Cellobiose', 'Glucan', 0.0120), Rxn('Glucan + H2O -> Glucose', 'Glucan', 0.9000), Rxn('Cellobiose + H2O -> Glucose', 'Cellobiose', 1.0000) ]) self.cofermentation = ParallelRxn([ # Reaction definition Reactant Conversion Rxn('Glucose -> 2 Ethanol + 2 CO2', 'Glucose', 0.9500), Rxn('Glucose + 0.047 CSL + 0.018 DAP -> 6 Z_mobilis + 2.4 H2O', 'Glucose', 0.0200), Rxn('Glucose + 2 H2O -> 2 Glycerol + O2', 'Glucose', 0.0040), Rxn('Glucose + 2 CO2 -> 2 SuccinicAcid + O2', 'Glucose', 0.0060), Rxn('3 Xylose -> 5 Ethanol + 5 CO2', 'Xylose', 0.8500), Rxn('Xylose + 0.039 CSL + 0.015 DAP -> 5 Z_mobilis + 2 H2O', 'Xylose', 0.0190), Rxn('3 Xylose + 5 H2O -> 5 Glycerol + 2.5 O2', 'Xylose', 0.0030), Rxn('Xylose + H2O -> Xylitol + 0.5 O2', 'Xylose', 0.0460), Rxn('3 Xylose + 5 CO2 -> 5 SuccinicAcid + 2.5 O2', 'Xylose', 0.0090), # Losses Rxn('Glucose -> 2 LacticAcid', 'Glucose', 0.0300), Rxn('3 Xylose -> 5 LacticAcid', 'Xylose', 0.0300), Rxn('3 Arabinose -> 5 LacticAcid', 'Arabinose', 0.0300), Rxn('Galactose -> 2 LacticAcid', 'Galactose', 0.0300), Rxn('Mannose -> 2 LacticAcid', 'Mannose', 0.0300), ]) self.CSL2constituents = Rxn( 'CSL -> 0.5 H2O + 0.25 LacticAcid + 0.25 Protein', 'CSL', 1.0000) self.saccharified_stream = bst.Stream(None)
def update_stripping_water(): stripping_water.mol[:] = stripping_water_over_vent * vent_stream.massnet puresys = System('purification', network=(M304, update_stripping_water, D401, M401, T302, H401, D402, H401, P401, H401, ethanol_recycle_sys, P402, H403, T701, P701, adjust_denaturant, T702, P702, M701, JX)) # %% Lignin Separation bst.Stream.species = species recycled_water = bst.Stream(Water=1, T=47 + 273.15, P=3.9 * 101325, units='kg/hr') splits = [('Glucose', 19, 502), ('Xylose', 40, 1022), ('OtherSugars', 81, 2175), ('SugarOligomers', 60, 1552), ('OrganicSolubleSolids', 612, 15808), ('InorganicSolubleSolids', 97, 2513), ('Furfurals', 19, 513), ('OtherOrganics', 52, 1348), ('Glucan', 1230, 25), ('Xylan', 415, 8), ('OtherStructuralCarbohydrates', 94, 2), ('Lignin', 12226, 250), ('Protein', 3376, 69), ('CellMass', 925, 19), ('OtherInsolubleSolids', 4489, 92)] # bst.Stream.default_ID_number = IDnum_400 S401 = units.PressureFilter('S401', ins=('', recycled_water),
def create_ethanol_production_system(ID='ethanol_production_sys', sugar_solution=None): ### 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']) if not sugar_solution: # Feedstock sugar_solution = bst.Stream('sugar_solution', Glucose = 3802, Sucrose = 4.309e+04, Water = 2.59e+05, H3PO4 = 83.33, units = 'kg/hr', T = 372, ) # 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', split=0.265) # Concentrate sugars F301 = units.MultiEffectEvaporator('F301', P=(101325, 73581, 50892, 32777), V=0.95) # fraction evaporated F301.components['condenser'].U = 1.85 # Note: value of steam ~ 6.86 for the following # (101325, 73580.467, 50891.17, 32777.406, 19999.925, 11331.5), # Mix sugar solutions M301 = units.Mixer('M301') # Cool for fermentation H301 = units.HXutility('H301', T=295.15) # Ethanol Production R301 = units.Fermentation('R301', outs=('CO2', ''), tau=9, efficiency=0.90, N=4) T301 = units.StorageTank('T301', tau=4, vessel_material='Carbon steel') T301.line = 'Beer tank' D301 = units.VentScrubber('D301', ins=(stripping_water, R301-0), outs=('vent', ''), gas=('CO2',)) # Separate 99% of yeast C301 = units.SolidsCentrifuge('C301', outs=('', 'recycle_yeast'), split=(1, 0.99999, 1, 0.96, 0.01), order=('Ethanol', 'Glucose', 'H3PO4', 'Water', 'DryYeast'), solids=('DryYeast',)) # Mix in Water M302 = units.Mixer('M302') P301 = units.Pump('P301') # Heat up before beer column # Exchange heat with stillage H302 = units.HXprocess('H302', phase0='l', phase1='l', U=1.28) # Beer column xbot = mass2molar_ethanol_fraction(0.00001) ytop = mass2molar_ethanol_fraction(0.574) D302 = units.BinaryDistillation('D302', P=101325, y_top=ytop, x_bot=xbot, k=1.25, LHK=('Ethanol', 'Water')) D302.tray_material = 'Stainless steel 304' D302.vessel_material = 'Stainless steel 304' D302.boiler.U = 1.85 P302 = units.Pump('P302') # Mix ethanol Recycle (Set-up) M303 = units.Mixer('M303') ytop = mass2molar_ethanol_fraction(0.9061726) D303 = units.BinaryDistillation('D303', P=101325, y_top=ytop, x_bot=xbot, k=1.25, LHK=('Ethanol', 'Water'), tray_material='Stainless steel 304', vessel_material='Stainless steel 304', is_divided=True) D303.boiler.U = 1.85 P303 = units.Pump('P303') # Superheat vapor for mol sieve H303 = units.HXutility('H303', T=115+273.15, V=1) # Molecular sieve U301 = units.MolecularSieve('U301', split=(2165.14/13356.04, 1280.06/1383.85), order=('Ethanol', 'Water')) # Condense ethanol product H304 = units.HXutility('H304', 'S149', V=0, T=340.) T302 = units.StorageTank('T302', tau=7*24, vessel_type='Floating roof', vessel_material='Carbon steel') P304 = units.Pump('P304') # Storage for gasoline T303 = units.StorageTank('T303', tau=7*24, vessel_type='Floating roof', vessel_material='Carbon steel') P305 = units.Pump('P305') # Denatured ethanol product T304 = units.MixTank('T304', outs=ethanol) T304.tau = 0.10 # Waste water M305 = units.Mixer('M305', outs='wastewater') # Yeast mixing T305 = units.MixTank('T305') T305.tau = 0.1 yeast-T305 # Multi-effect evaporator pumps P306 = units.Pump('P306') ### Ethanol system set-up ### sugar_solution-S301-1-F301-0-P306 (S301-0, P306-0)-M301-H301 (H301-0, yeast-T305-0)-R301-1-T301-0-C301 (C301-0, D301-1)-M302-P301 (P301-0, P302-0)-H302-0-D302-1-P302 (D302-0, U301-0)-M303-0-D303-0-H303-U301 D303-1-P303 def adjust_denaturant(): P304._run() pure_ethanol = P304.outs[0] denaturant.imol['Octane'] = 0.021*pure_ethanol.F_mass/114.232 P304.specification = adjust_denaturant U301-1-H304-0-T302-0-P304 denaturant-T303-P305 (P305-0, P304-0)-T304 (P303-0, F301-1, H302-1)-M305 ### System ### return bst.System(ID, [S301, F301, P306, M301, H301, T305, R301, T301, C301, M302, P301, bst.System('beer_column_heat_integration', [H302, D302, P302], recycle=P302-0), bst.System('ethanol_recycle_from_molecular_sieves', [M303, D303, H303, U301], recycle=U301-0), H304, T302, P304, T303, P305, T304, D301, P303, M305])
def create_wastewater_treatment_units(ins, outs, NaOH_price=0.15): """ Create units for wastewater treatment, including anaerobic and aerobic digestion reactors, a membrane bioreactor, a sludge centrifuge, and a reverse osmosis unit. Parameters ---------- ins : streams Wastewater streams (without solids). outs : stream sequence * [0] methane * [1] sludge * [2] treated_water * [3] waste_brine NaOH_price : float, optional Price of NaOH in USD/kg. The default is 0.15. """ methane, sludge, treated_water, waste_brine = outs well_water = bst.Stream('well_water', Water=1, T=15 + 273.15) air = bst.Stream('air_lagoon', O2=51061, N2=168162, phase='g', units='kg/hr') caustic = bst.Stream('caustic', Water=2252, NaOH=2252, units='kg/hr', price=NaOH_price) wastewater_mixer = bst.Mixer('M601', ins) WWTC = WastewaterSystemCost('WWTC', wastewater_mixer - 0) anaerobic_digestion = AnaerobicDigestion('R601', (WWTC - 0, well_water), (methane, '', '', '')) recycled_sludge_mixer = bst.Mixer('M602', (anaerobic_digestion - 1, None)) caustic_over_waste = 2 * caustic.imol['Water', 'NaOH'] / 2544301 air_over_waste = air.imol['O2', 'N2'] / 2544301 air.mol[:] = 0. waste = recycled_sludge_mixer - 0 def update_aerobic_input_streams(): waste, air, caustic = aerobic_digestion.ins F_mass_waste = waste.F_mass caustic.imol['Water', 'NaOH'] = F_mass_waste * caustic_over_waste air.imol['O2', 'N2'] = F_mass_waste * air_over_waste aerobic_digestion._run() aerobic_digestion = AerobicDigestion('R602', (waste, air, caustic), outs=('evaporated_water', '')) aerobic_digestion.specification = update_aerobic_input_streams membrane_bioreactor = MembraneBioreactor('S601', aerobic_digestion - 1) sludge_splitter = bst.Splitter('S602', membrane_bioreactor - 1, split=0.96) fresh_sludge_mixer = bst.Mixer( 'M603', (anaerobic_digestion - 2, sludge_splitter - 1)) sludge_centrifuge = SludgeCentrifuge('S603', fresh_sludge_mixer - 0, outs=('', sludge)) sludge_centrifuge - 0 - 1 - recycled_sludge_mixer reverse_osmosis = ReverseOsmosis('S604', membrane_bioreactor - 0, outs=(treated_water, waste_brine))
def create_wastewater_treatment_system( wastewater_streams=(), wastewater_treatment_area=600, NaOH_price=0.15, ): """ Create a system for wastewater treatment. Parameters ---------- wastewater_streams : Iterable[:class:`~thermosteam.Stream`], optional Wastewater streams (without solids). wastewater_treatment_area : int, optional Area number to label unit operations. The default is 600. NaOH_price : float, optional Price of NaOH in USD/kg. The default is 0.15. Returns ------- wastewater_treatment : :class:`~biosteam.System` Wastewater treatment system. The system includes anaerobic and aerobic digestion, a membrane bioreactor, a sludge centrifuge, and reverse osmosis. """ n = wastewater_treatment_area well_water = bst.Stream('well_water', Water=1, T=15 + 273.15) air = bst.Stream('air_lagoon', O2=51061, N2=168162, phase='g', units='kg/hr') caustic = bst.Stream('caustic', Water=2252, NaOH=2252, units='kg/hr', price=NaOH_price * 0.5) wastewater_mixer = bst.Mixer(f'M{n+1}', wastewater_streams) WWTC = WastewaterSystemCost('WWTC', wastewater_mixer - 0) anaerobic_digestion = AnaerobicDigestion(f'R{n+1}', (WWTC - 0, well_water)) recycled_sludge_mixer = bst.Mixer(f'M{n+2}', (anaerobic_digestion - 1, None)) caustic_over_waste = caustic.mol / 2544300.6261793654 air_over_waste = air.mol / 2544300.6261793654 waste = recycled_sludge_mixer - 0 def update_aerobic_input_streams(): F_mass_waste = waste.F_mass caustic.mol[:] = F_mass_waste * caustic_over_waste air.mol[:] = F_mass_waste * air_over_waste aerobic_digestion = AerobicDigestion(f'R{n+2}', (waste, air, caustic), outs=('evaporated_water', '')) membrane_bioreactor = MembraneBioreactor(f'S{n+1}', aerobic_digestion - 1) sludge_splitter = bst.Splitter(f'S{n+2}', membrane_bioreactor - 1, split=0.96) fresh_sludge_mixer = bst.Mixer( f'M{n+3}', (anaerobic_digestion - 2, sludge_splitter - 1)) sludge_centrifuge = SludgeCentrifuge(f'S{n+3}', fresh_sludge_mixer - 0, outs=('', 'sludge')) sludge_centrifuge - 0 - 1 - recycled_sludge_mixer reverse_osmosis = ReverseOsmosis(f'S{n+4}', membrane_bioreactor - 0, outs=('treated_water', 'waste_brine')) aerobic_digestion_sys = bst.System( 'aerobic_digestion_sys', path=(recycled_sludge_mixer, update_aerobic_input_streams, aerobic_digestion, membrane_bioreactor, sludge_splitter, fresh_sludge_mixer, sludge_centrifuge), recycle=recycled_sludge_mixer - 0) wastewater_treatment = bst.System('wastewater_treatment', path=[ wastewater_mixer, WWTC, anaerobic_digestion, aerobic_digestion_sys, reverse_osmosis ]) return wastewater_treatment
bst.main_flowsheet.set_flowsheet('sugarcane_pretreatment_section') F_mass_sugarcane = 333334 z_mass_sugarcane = chemicals.kwarray( dict(Glucose=0.0120811, Lignin=0.0327653, Solids=0.015, Sucrose=0.136919, Ash=0.006, Cellulose=0.0611531, Hemicellulose=0.036082, Water=0.7)) sugarcane = sugar_cane = bst.Stream('sugar_cane', flow=F_mass_sugarcane * z_mass_sugarcane, 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,
def create_system(ID='sugarcane_sys'): ### Streams ### chemicals = bst.settings.get_chemicals() z_mass_sugarcane = chemicals.kwarray( dict(Glucose=0.0120811, Lignin=0.0327653, Solids=0.015, Sucrose=0.136919, Ash=0.006, Cellulose=0.0611531, Hemicellulose=0.036082, Water=0.7)) sugarcane = bst.Stream('sugarcane', flow=333334 * z_mass_sugarcane, 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 the shredder U101 = units.ConveyingBelt('U101', ins=sugarcane) # Separate metals U102 = units.MagneticSeparator('U102', ins=U101 - 0) # Shredded cane U103 = units.Shredder('U103', ins=U102 - 0) # Hydrolyze starch T201 = units.EnzymeTreatment('T201', T=323.15) # T=50 # Finely crush lipid cane U201 = units.CrushingMill('U201', split=dict(Ash=0.92, Cellulose=0.92, Glucose=0.04, Hemicellulose=0.92, Lignin=0.92, Sucrose=0.04, Solids=1), moisture_content=0.5) # Convey out bagasse U202 = units.ConveyingBelt('U202', ins=U201.outs[0], outs='Bagasse') # Mix in water M201 = units.Mixer('M201') # crushing_mill_recycle_sys = bst.System('crushing_mill_recycle_sys', # path=(U201, S201, M201), # recycle=M201-0) # Screen out fibers S201 = units.VibratingScreen('S201', split=dict(Ash=0.35, Cellulose=0.35, Glucose=0.88, Hemicellulose=0.35, Lignin=0.35, Solids=0, Sucrose=0.88, Water=0.88)) # Store juice before treatment T202 = units.StorageTank('T202', tau=4, vessel_material='Carbon steel') # Heat up before adding acid H201 = units.HXutility('H201', T=343.15) # Mix in acid T203 = units.MixTank('T203') # Pump acid solution P201 = units.Pump('P201') # Mix lime solution T204 = units.MixTank('T204', tau=0.10) P202 = units.Pump('P202') # Blend acid lipid solution with lime T205 = units.MixTank('T205', tau=0.10) # Mix recycle M202 = units.Mixer('M202') # Heat before adding flocculant H202 = units.HXutility('H202', T=372.15) # Mix in flocculant T206 = units.MixTank('T206') T206.tau = 0.10 # Separate residual solids C201 = units.Clarifier('C201', split=dict(Ash=0, CaO=0, Cellulose=0, Flocculant=0.522, Glucose=0.522, Hemicellulose=0, Lignin=0, H3PO4=0.522, Sucrose=0.522, Water=0.522)) # Remove solids as filter cake C202 = units.RVF('C202', outs=('filter_cake', ''), moisture_content=0.80, split=dict(Ash=0.85, CaO=0.85, Cellulose=0.85, Glucose=0.01, Hemicellulose=0.85, Lignin=0.85, Sucrose=0.01)) P203 = units.Pump('P203') # Screen out small fibers from sugar stream S202 = units.VibratingScreen('S202', outs=('', 'fiber_fines'), split=dict(Ash=1.0, CaO=1.0, Cellulose=1.0, Flocculant=0.0, Glucose=0.998, Hemicellulose=1.0, Lignin=1.0, H3PO4=1.0, Sucrose=0.998, Water=0.998)) S202.mesh_opening = 2 ### Process specifications ### # Specifications dependent on lipid cane flow rate def correct_flows(): U103._run() F_mass = sugarcane.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 U103.specification = correct_flows # Specifications within a system def correct_wash_water(): P202._run() solids = P202.outs[0].imol['Ash', 'CaO', 'Cellulose', 'Hemicellulose', 'Lignin'].sum() rvf_wash_water.imol['Water'] = 0.0574 * solids P202.specification = correct_wash_water ### System set-up ### (U103 - 0, enzyme) - T201 (T201 - 0, M201 - 0) - U201 - 1 - S201 - 0 - T202 (S201 - 1, imbibition_water) - M201 T202 - 0 - H201 (H201 - 0, H3PO4) - T203 - P201 (P201 - 0, lime - T204 - 0) - T205 - P202 (P202 - 0, P203 - 0) - M202 - H202 (H202 - 0, polymer) - T206 - C201 (C201 - 1, rvf_wash_water) - C202 - 1 - P203 C201 - 0 - S202 ### Ethanol section ### ethanol_production_sys = create_ethanol_production_system( sugar_solution=S202 - 0) ### Facilities ### s = F.stream BT = units.BoilerTurbogenerator( 'BT', (U202 - 0, '', 'boiler_makeup_water', 'natural_gas', '', ''), boiler_efficiency=0.80, turbogenerator_efficiency=0.85) CT = units.CoolingTower('CT') makeup_water_streams = (s.cooling_tower_makeup_water, s.boiler_makeup_water) process_water_streams = (s.imbibition_water, rvf_wash_water, s.stripping_water, *makeup_water_streams) makeup_water = bst.Stream('makeup_water', price=0.000254) CWP = units.ChilledWaterPackage('CWP') PWC = units.ProcessWaterCenter('PWC', (bst.Stream(), makeup_water), (), None, makeup_water_streams, process_water_streams) ### System ### return bst.System( ID, [ U101, U102, U103, T201, bst.System("juice_extraction_sys", [U201, S201, M201], recycle=M201 - 0), T202, H201, T203, P201, T204, T205, P202, bst.System('juice_separation_sys', [M202, H202, T206, C201, C202, P203], recycle=P203 - 0), S202, ethanol_production_sys, U202 ], facilities=(CWP, BT, CT, PWC), )
from biorefineries.lipidcane.process_settings import price __all__ = ('lipidcane_sys', 'lipidcane_tea', 'lipidcane', 'lipid_cane') # %% Pretreatment section bst.settings.set_thermo(pretreatment_chemicals) ### Streams ### lipidcane = lipid_cane = bst.Stream('lipid_cane', Ash=2000.042, Cellulose=26986.69, Glucose=2007.067, Hemicellulose=15922.734, Lignin=14459.241, Lipid=10035.334, Solids=5017.667, Sucrose=22746.761, Water=234157.798, units='kg/hr', price=price['Lipid 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',
def test_equipment_lifetimes(): from biorefineries.sugarcane import create_tea bst.settings.set_thermo(['Water'], cache=True) class A(bst.Unit): _BM = { 'Equipment A': 2, 'Equipment B': 3, } _equipment_lifetime = { 'Equipment A': 10, 'Equipment B': 5, } def _cost(self): purchase_costs = self.purchase_costs purchase_costs['Equipment A'] = 1e6 purchase_costs['Equipment B'] = 1e5 class B(bst.Unit): _BM = { 'Equipment A': 2, } _equipment_lifetime = 15 def _cost(self): self.purchase_costs['Equipment A'] = 1e6 class C(bst.Unit): _BM = { 'Equipment A': 4, } def _cost(self): self.purchase_costs['Equipment A'] = 1e6 @bst.decorators.cost('Flow rate', units='kmol/hr', S=1, BM=3, cost=1e6, n=0.6, CE=bst.CE, lifetime=8) class D(bst.Unit): pass @bst.decorators.cost('Flow rate', units='kmol/hr', S=1, BM=4, cost=1e6, n=0.6, CE=bst.CE, lifetime=20) class E(bst.Unit): pass D_feed = bst.Stream('D_feed', Water=1) E_feed = D_feed.copy('E_feed') units = [A(None, 'A_feed'), B(None, 'B_feed'), C(None, 'C_feed'), D(None, D_feed), E(None, E_feed)] test_sys = bst.System('test_sys', units) test_sys.simulate() tea = create_tea(test_sys) table = tea.get_cashflow_table() C_FCI = table['Fixed capital investment [MM$]'] # Test with lang factor = 3 (default for sugarcane biorefinery) cashflows_FCI = [6.12, 9.18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.3, 0.0, 0.0, 3.0, 0.0, 3.3, 0.0, 0.0, 0.0, 0.0, 0.3, 0.0, 0.0, 0.0, 0.0] assert_allclose(C_FCI, cashflows_FCI) # Cashflows include maintainance and others, so all entries are not zero cashflows = [-6120000.0, -9945000.0, -4321300.0, -4321300.0, -4321300.0, -4321300.0, -4321300.0, -4621300.0, -4321300.0, -4321300.0, -7321300.0, -4321300.0, -7621300.0, -4321300.0, -4321300.0, -4321300.0, -4321300.0, -4621300.0, -4321300.0, -4321300.0, -4321300.0, -3556300.0] assert_allclose(tea.cashflow_array, cashflows) # Test with bare module costs tea.lang_factor = None table = tea.get_cashflow_table() C_FCI = table['Fixed capital investment [MM$]'] cashflows_FCI = [6.12, 9.18, 0.0, 0.0, 0.0, 0.0, 0.0, 0.3, 0.0, 0.0, 3.0, 0.0, 2.3, 0.0, 0.0, 0.0, 0.0, 0.3, 0.0, 0.0, 0.0, 0.0] assert_allclose(C_FCI, cashflows_FCI) cashflows = [-6120000.0, -9945000.0, -4321300.0, -4321300.0, -4321300.0, -4321300.0, -4321300.0, -4621300.0, -4321300.0, -4321300.0, -7321300.0, -4321300.0, -6621300.0, -4321300.0, -4321300.0, -4321300.0, -4321300.0, -4621300.0, -4321300.0, -4321300.0, -4321300.0, -3556300.0] assert_allclose(tea.cashflow_array, cashflows)
def synthesize_network(hus, T_min_app=5., Qmin=1e-3, force_ideal_thermo=False): pinch_T_arr, hot_util_load, cold_util_load, T_in_arr, T_out_arr,\ hxs, hot_indices, cold_indices, indices, streams_inlet, hx_utils_rearranged, \ streams_quenched = temperature_interval_pinch_analysis(hus, T_min_app, force_ideal_thermo) H_out_arr = [i.H for i in streams_quenched] duties = np.array([abs(hx.Q) for hx in hxs]) dTs = np.abs(T_in_arr - T_out_arr) dTs[dTs == 0.] = 1e-12 C_flow_vector = duties / dTs Q_hot_side = {} Q_cold_side = {} stream_HXs_dict = {i: [] for i in indices} is_cold = lambda x: x in cold_indices load_duties(streams_inlet, streams_quenched, pinch_T_arr, T_out_arr, indices, is_cold, Q_hot_side, Q_cold_side) matches_hs = {i: [] for i in cold_indices} matches_cs = {i: [] for i in hot_indices} candidate_hot_streams = hot_indices.copy() candidate_cold_streams = cold_indices.copy() HXs_hot_side = [] HXs_cold_side = [] streams_transient_cold_side = [i.copy() for i in streams_inlet] streams_transient_hot_side = [i.copy() for i in streams_inlet] for i in hot_indices: s = streams_transient_cold_side[i] if s.T != pinch_T_arr[i]: s.vle(T=pinch_T_arr[i], P=s.P) for i in cold_indices: s = streams_transient_hot_side[i] if s.T != pinch_T_arr[i]: s.vle(T=pinch_T_arr[i], P=s.P) def get_stream_at_H_max(cold): s_cs = streams_transient_cold_side[cold] s_hs = streams_transient_hot_side[cold] return s_cs if s_cs.H > s_hs.H else s_hs def get_stream_at_H_min(hot): s_cs = streams_transient_cold_side[hot] s_hs = streams_transient_hot_side[hot] return s_cs if s_cs.H < s_hs.H else s_hs def get_T_transient_cold_side(index): return streams_transient_cold_side[index].T def get_T_transient_hot_side(index): return streams_transient_hot_side[index].T attempts = set() # ------------- Cold side design ------------- # unavailables = set( [i for i in hot_indices if T_out_arr[i] >= pinch_T_arr[i]]) unavailables.update( [i for i in cold_indices if T_in_arr[i] >= pinch_T_arr[i]]) for hot in hot_indices: stream_quenched = False potential_matches = [] for cold in cold_indices: if (C_flow_vector[hot] >= C_flow_vector[cold] and get_T_transient_cold_side(hot) > get_T_transient_cold_side(cold) + T_min_app and (hot not in unavailables) and (cold not in unavailables) and (cold not in matches_cs[hot]) and (cold in candidate_cold_streams)): potential_matches.append(cold) potential_matches = sorted( potential_matches, key=lambda pot_cold: min(C_flow_vector[hot], C_flow_vector[ pot_cold]) * (get_T_transient_cold_side(hot) - get_T_transient_cold_side(pot_cold) - T_min_app), reverse=True) for cold in potential_matches: ID = 'HX_%s_%s_cs' % (hot, cold) if ID in attempts: continue attempts.add(ID) Q_hstr = Q_cold_side[hot][1] Q_cstr = Q_cold_side[cold][1] Q_res = Q_cstr - Q_hstr # if abs(get_T_transient_cold_side(cold) - pinch_T_arr[cold])<= 0.01: # continue hot_stream = streams_transient_cold_side[hot].copy() cold_stream = streams_transient_cold_side[cold].copy() hot_stream.ID = 's_%s__%s' % (hot, ID) cold_stream.ID = 's_%s__%s' % (cold, ID) hot_out = bst.Stream('%s__s_%s' % (ID, hot), thermo=hot_stream.thermo) cold_out = bst.Stream('%s__s_%s' % (ID, cold), thermo=cold_stream.thermo) H_lim = H_out_arr[hot] new_HX = bst.units.HXprocess(ID=ID, ins=(hot_stream, cold_stream), outs=(hot_out, cold_out), H_lim0=H_lim, T_lim1=pinch_T_arr[cold], dT=T_min_app, thermo=hot_stream.thermo) new_HX._run() if abs(new_HX.Q) < Qmin: continue HXs_cold_side.append(new_HX) stream_HXs_dict[hot].append(new_HX) stream_HXs_dict[cold].append(new_HX) Q_cold_side[hot][1] -= new_HX.Q Q_cold_side[cold][1] -= new_HX.Q streams_transient_cold_side[hot] = new_HX.outs[0] streams_transient_cold_side[cold] = new_HX.outs[1] H_out = new_HX.outs[0].H assert H_out - new_HX.ins[0].H <= 0. stream_quenched = H_out < H_lim or np.allclose(H_out, H_lim) matches_cs[hot].append(cold) if stream_quenched: break # ------------- Hot side design ------------- # unavailables = set( [i for i in hot_indices if T_in_arr[i] <= pinch_T_arr[i]]) unavailables.update( [i for i in cold_indices if T_out_arr[i] <= pinch_T_arr[i]]) for cold in cold_indices: # streams_transient_hot_side[cold] = streams_transient_cold_side[cold].copy() # T_transient_hot_side[cold] = min(T_transient_hot_side[cold], get_T_transient_cold_side(cold)) potential_matches = [] for hot in candidate_hot_streams: if ((cold in matches_cs and hot in matches_cs[cold]) or (cold in matches_hs and hot in matches_hs[cold])): break if (C_flow_vector[cold] >= C_flow_vector[hot] and get_T_transient_hot_side(hot) > get_T_transient_hot_side(cold) + T_min_app and (hot not in unavailables) and (cold not in unavailables) and (hot not in matches_hs[cold]) and (hot in candidate_hot_streams)): potential_matches.append(hot) potential_matches = sorted( potential_matches, key=lambda x: (min(C_flow_vector[cold], C_flow_vector[x]) * (get_T_transient_hot_side(x) - get_T_transient_hot_side(cold) - T_min_app)), reverse=True) stream_quenched = False for hot in potential_matches: ID = 'HX_%s_%s_hs' % (cold, hot) if ID in attempts: continue attempts.add(ID) Q_hstr = Q_hot_side[hot][1] Q_cstr = Q_hot_side[cold][1] Q_res = Q_cstr - Q_hstr # if abs(get_T_transient_hot_side(hot) - pinch_T_arr[hot])< 1e-6: # continue hot_stream = streams_transient_hot_side[hot].copy() cold_stream = streams_transient_hot_side[cold].copy() cold_stream.ID = 's_%s__%s' % (cold, ID) hot_stream.ID = 's_%s__%s' % (hot, ID) hot_out = bst.Stream('%s__s_%s' % (ID, hot), thermo=hot_stream.thermo) cold_out = bst.Stream('%s__s_%s' % (ID, cold), thermo=cold_stream.thermo) H_lim = H_out_arr[cold] new_HX = bst.units.HXprocess(ID=ID, ins=(cold_stream, hot_stream), outs=(cold_out, hot_out), H_lim0=H_lim, T_lim1=pinch_T_arr[hot], dT=T_min_app, thermo=hot_stream.thermo) try: new_HX._run() except: continue if abs(new_HX.Q) < Qmin: continue HXs_hot_side.append(new_HX) stream_HXs_dict[hot].append(new_HX) stream_HXs_dict[cold].append(new_HX) Q_hot_side[hot][1] -= new_HX.Q Q_hot_side[cold][1] -= new_HX.Q streams_transient_hot_side[cold] = new_HX.outs[0] streams_transient_hot_side[hot] = new_HX.outs[1] H_out = new_HX.outs[0].H assert H_out - new_HX.ins[0].H >= 0. stream_quenched = H_out > H_lim or np.allclose(H_out, H_lim) matches_hs[cold].append(hot) if stream_quenched: break # Offset heating requirement on cold side for cold in cold_indices: if Q_cold_side[cold][0] == 'heat' and Q_cold_side[cold][1] > 0: for hot in hot_indices: ID = 'HX_%s_%s_cs' % (hot, cold) if ID in attempts: continue attempts.add(ID) T_cold_in = get_T_transient_cold_side(cold) T_hot_in = get_T_transient_cold_side(hot) # if ((cold in matches_cs and hot in matches_cs[cold]) # or (cold in matches_hs and hot in matches_hs[cold])): # continue if (Q_cold_side[hot][0] == 'cool' and Q_cold_side[hot][1] > 0 and T_hot_in - T_cold_in >= T_min_app): # if abs(T_cold_in - pinch_T_arr[cold])<= 0.01: # continue hot_stream = streams_transient_cold_side[hot].copy() cold_stream = streams_transient_cold_side[cold].copy() hot_stream.ID = 's_%s__%s' % (hot, ID) cold_stream.ID = 's_%s__%s' % (cold, ID) hot_out = bst.Stream('%s__s_%s' % (ID, hot), thermo=hot_stream.thermo) cold_out = bst.Stream('%s__s_%s' % (ID, cold), thermo=cold_stream.thermo) new_HX = bst.units.HXprocess(ID=ID, ins=(hot_stream, cold_stream), outs=(hot_out, cold_out), H_lim0=H_out_arr[hot], T_lim1=T_out_arr[cold], dT=T_min_app, thermo=hot_stream.thermo) try: new_HX._run() except: continue if abs(new_HX.Q) < Qmin: continue HXs_cold_side.append(new_HX) stream_HXs_dict[hot].append(new_HX) stream_HXs_dict[cold].append(new_HX) Q_cold_side[hot][1] -= new_HX.Q Q_cold_side[cold][1] -= new_HX.Q streams_transient_cold_side[hot] = new_HX.outs[0] streams_transient_cold_side[cold] = new_HX.outs[1] matches_cs[hot].append(cold) # Offset cooling requirement on hot side for hot in hot_indices: stream_quenched = False if Q_hot_side[hot][0] == 'cool' and Q_hot_side[hot][1] > 0: for cold in cold_indices: ID = 'HX_%s_%s_hs' % (cold, hot) if ID in attempts: continue attempts.add(ID) # if ((hot in matches_cs and cold in matches_cs[hot]) # or (hot in matches_hs and cold in matches_hs[hot])): # continue original_cold_stream = get_stream_at_H_max(cold) T_cold_in = original_cold_stream.T T_hot_in = get_T_transient_hot_side(hot) if (Q_hot_side[cold][0] == 'heat' and Q_hot_side[cold][1] > 0 and T_hot_in - T_cold_in >= T_min_app): # if abs(T_hot_in - pinch_T_arr[hot])<= 0.01: # continue cold_stream = original_cold_stream.copy() hot_stream = streams_transient_hot_side[hot].copy() cold_stream.ID = 's_%s__%s' % (cold, ID) hot_stream.ID = 's_%s__%s' % (hot, ID) hot_out = bst.Stream('%s__s_%s' % (ID, hot), thermo=hot_stream.thermo) cold_out = bst.Stream('%s__s_%s' % (ID, cold), thermo=cold_stream.thermo) H_lim = H_out_arr[cold] new_HX = bst.units.HXprocess(ID=ID, ins=(cold_stream, hot_stream), outs=(cold_out, hot_out), H_lim0=H_lim, T_lim1=T_out_arr[hot], dT=T_min_app, thermo=hot_stream.thermo) try: new_HX._run() except: continue if abs(new_HX.Q) < Qmin: continue HXs_hot_side.append(new_HX) stream_HXs_dict[hot].append(new_HX) stream_HXs_dict[cold].append(new_HX) Q_hot_side[hot][1] -= new_HX.Q Q_hot_side[cold][1] -= new_HX.Q streams_transient_hot_side[cold] = new_HX.outs[0] streams_transient_hot_side[hot] = new_HX.outs[1] H_out = new_HX.outs[0].H assert H_out - new_HX.ins[0].H >= 0. stream_quenched = H_out > H_lim or np.allclose( H_out, H_lim) matches_hs[cold].append(hot) if stream_quenched: break def get_hottest_stream_from_life_cycle(cold): s_hottest = get_stream_at_H_max(cold) H_max = s_hottest.H chemicals = s_hottest.chemicals for u in HXs_cold_side + HXs_hot_side: for s in u.outs: if '_%s_' % (cold) in s.ID and '__s_%s' % ( cold ) in s.ID and s.chemicals is chemicals and np.allclose( s.mol, s_hottest.mol): if s.H > H_max: H_max = s.H s_hottest = s return s_hottest def get_coldest_stream_from_life_cycle(hot): s_coldest = get_stream_at_H_min(hot) H_min = s_coldest.H chemicals = s_coldest.chemicals for u in HXs_cold_side + HXs_hot_side: for s in u.outs: if '_%s_' % (hot) in s.ID and '__s_%s' % ( hot ) in s.ID and s.chemicals is chemicals and np.allclose( s.mol, s_coldest.mol): if s.H < H_min: H_min = s.H s_coldest = s return s_coldest # Add final utility HXs new_HX_utils = [] for hot in hot_indices: hot_stream = get_coldest_stream_from_life_cycle(hot).copy() ID = 'Util_%s_cs' % (hot) hot_stream.ID = 's_%s__%s' % (hot, ID) outsID = '%s__s_%s' % (ID, hot) new_HX_util = bst.units.HXutility(ID=ID, ins=hot_stream, outs=outsID, H=H_out_arr[hot], rigorous=True, thermo=hot_stream.thermo) new_HX_util._run() s_out = new_HX_util - 0 np.testing.assert_allclose(s_out.H, H_out_arr[hot], rtol=5e-3, atol=1.) atol_T = 2. if 's' in hxs[hot].outs[0].phases else 0.001 np.testing.assert_allclose(s_out.T, T_out_arr[hot], rtol=5e-3, atol=atol_T) new_HX_utils.append(new_HX_util) stream_HXs_dict[hot].append(new_HX_util) for cold in cold_indices: cold_stream = get_hottest_stream_from_life_cycle(cold).copy() ID = 'Util_%s_hs' % (cold) cold_stream.ID = 's_%s__%s' % (cold, ID) outsID = '%s__s_%s' % (ID, cold) new_HX_util = bst.units.HXutility(ID=ID, ins=cold_stream, outs=outsID, H=H_out_arr[cold], rigorous=True, thermo=cold_stream.thermo) new_HX_util._run() s_out = new_HX_util - 0 np.testing.assert_allclose(s_out.H, H_out_arr[cold], rtol=1e-2, atol=1.) atol_T = 2. if 's' in hxs[cold].outs[0].phases else 0.001 np.testing.assert_allclose(s_out.T, T_out_arr[cold], rtol=5e-2, atol=atol_T) new_HX_utils.append(new_HX_util) stream_HXs_dict[cold].append(new_HX_util) return HXs_hot_side, HXs_cold_side, new_HX_utils, hxs, T_in_arr,\ T_out_arr, pinch_T_arr, C_flow_vector, hx_utils_rearranged, streams_inlet, stream_HXs_dict,\ hot_indices, cold_indices
def create_system(ID='lipidcane_sys'): chemicals = bst.settings.get_chemicals() s = bst.main_flowsheet.stream u = bst.main_flowsheet.unit ### Streams ### lipidcane = lipidcane = bst.Stream('lipidcane', Ash=2000.042, Cellulose=26986.69, Glucose=2007.067, Hemicellulose=15922.734, Lignin=14459.241, Lipid=10035.334, Solids=5017.667, Sucrose=22746.761, Water=234157.798, units='kg/hr', price=price['Lipid 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 oil_wash_water = bst.Stream('oil_wash_water', Water=1350, units='kg/hr', T=358.15) # to T207 ### Unit operations ### bst.Stream.ticket_name = 'd' bst.Stream.ticket_number = 100 # Feed the shredder U101 = units.ConveyingBelt('U101', ins=lipidcane) U101.cost_items['Conveying belt'].ub = 5000 # Separate metals U102 = units.MagneticSeparator('U102', ins=U101 - 0) # Shredded cane U103 = units.Shredder('U103', ins=U102 - 0) bst.Stream.ticket_number = 200 # Hydrolyze starch T201 = units.EnzymeTreatment('T201', T=323.15) # T=50 # Finely crush lipid cane U201 = units.CrushingMill('U201', split=dict(Ash=0.92, Cellulose=0.92, Glucose=0.04, Hemicellulose=0.92, Lignin=0.92, Sucrose=0.04, Lipid=0.1, Solids=1), moisture_content=0.5) # Convey out bagasse U202 = units.ConveyingBelt('U202', ins=U201.outs[0], outs='Bagasse') # Mix in water M201 = units.Mixer('M201') # Screen out fibers S201 = units.VibratingScreen('S201', split=dict(Ash=0.35, Cellulose=0.35, Glucose=0.88, Hemicellulose=0.35, Lignin=0.35, Lipid=0.88, Solids=0, Sucrose=0.88, Water=0.88)) # Store juice before treatment T202 = units.StorageTank('T202', tau=4, vessel_material='Carbon steel') # Heat up before adding acid H201 = units.HXutility('H201', T=343.15, V=0) # Mix in acid T203 = units.MixTank('T203') T203.tau = 0.10 # Pump acid solution P201 = units.Pump('P201') # Mix lime solution T204 = units.MixTank('T204') T204.tau = 0.10 P202 = units.Pump('P202') # Blend acid lipid solution with lime T205 = units.MixTank('T205') # Mix recycle M202 = units.Mixer('M202') # Heat before adding flocculant H202 = units.HXutility('H202', T=372.15, V=0) # Mix in flocculant T206 = units.MixTank('T206') T206.tau = 0.10 # Separate residual solids C201 = units.Clarifier('C201', split=dict(Ash=0, CaO=0, Cellulose=0, Flocculant=0.522, Glucose=0.522, Hemicellulose=0, Lignin=0, Lipid=0.98, H3PO4=0.522, Sucrose=0.522, Water=0.522)) # Remove solids as filter cake C202 = units.RVF('C202', outs=('filter_cake', ''), moisture_content=0.80, split=dict(Ash=0.85, CaO=0.85, Cellulose=0.85, Glucose=0.01, Hemicellulose=0.85, Lignin=0.85, Sucrose=0.01)) P203 = units.Pump('P203') # Separate oil and sugar T207 = units.MixTank('T207') T207_2 = units.Splitter('T207_2', split=dict(Lipid=1, Water=1e-4)) # Cool the oil H203 = units.HXutility('H203', T=343.15, V=0) # Screen out small fibers from sugar stream S202 = units.VibratingScreen('S202', outs=('', 'fiber_fines'), split=dict(Ash=1.0, CaO=1.0, Cellulose=1.0, Flocculant=0.0, Glucose=0.998, Hemicellulose=1.0, Lignin=1.0, Lipid=1.0, H3PO4=1.0, Sucrose=0.998, Water=0.998)) sugar = S202 - 0 S202.mesh_opening = 2 # Add distilled water to wash lipid T208 = units.MixTank('T208') T208.tau = 0.10 # Centrifuge out water C203 = units.LiquidsSplitCentrifuge('C203', split=dict(Lipid=0.99, Water=0.01)) # Vacume out water F201 = units.SplitFlash('F201', T=357.15, P=2026.5, outs=('water_vapor', ''), split=dict(Lipid=0.0001, Water=0.999)) ### Process specifications ### def correct_flows(): F_mass = lipidcane.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 T201._run() T201.specification = correct_flows # Specifications within a system def correct_lipid_wash_water(): oil_wash_water.imol['Water'] = 100 / 11 * H202.outs[0].imol['Lipid'] T208._run() T208.specification = correct_lipid_wash_water def correct_wash_water(): P202._run() solids = P202.outs[0].imol['Ash', 'CaO', 'Cellulose', 'Hemicellulose', 'Lignin'].sum() rvf_wash_water.imol['Water'] = 0.0574 * solids P202.specification = correct_wash_water ### System set-up ### (U103 - 0, enzyme) - T201 (T201 - 0, M201 - 0) - U201 - 1 - S201 - 0 - T202 (S201 - 1, imbibition_water) - M201 T202 - 0 - H201 (H201 - 0, H3PO4) - T203 - P201 (P201 - 0, lime - T204 - 0) - T205 - P202 (P202 - 0, P203 - 0) - M202 - H202 (H202 - 0, polymer) - T206 - C201 (C201 - 1, rvf_wash_water) - C202 - 1 - P203 C201 - 0 - T207 - T207_2 - 0 - H203 (H203 - 0, oil_wash_water) - T208 - C203 - 0 - F201 T207 - T207_2 - 1 - S202 lipid = F201 - 1 ### Ethanol section ### ### Utilities ### MW_etoh = chemicals.Ethanol.MW MW_water = chemicals.Water.MW def mass2molar_ethanol_fraction(ethanol_mass_fraction): """Return ethanol mol fraction in a ethanol water mixture""" x = ethanol_mass_fraction return x / MW_etoh / (x / MW_etoh + (1 - x) / MW_water) ### Input streams ### ethanol_production_sys = create_ethanol_production_system( sugar_solution=S202 - 0) u.M305.ins.append(C203 - 1) ### Biodiesel section ### ### Streams ### # Fresh degummed oil oil = lipid # Fresh methanol methanol = bst.Stream('methanol', Methanol=1, price=price['Methanol']) # Catalyst catalyst = bst.Stream('catalyst', NaOCH3=0.25, Methanol=0.75, units='kg/hr', price=price['NaOCH3']) # price=0.25*price['NaOCH3'] + 0.75*Methanol.price) # Water to remove glycerol biodiesel_wash_water = bst.Stream('biodiesel_wash_water', Water=13.6, T=273.15 + 60, price=price['Water']) # Acid to neutralize catalyst after second centrifuge HCl1 = bst.Stream('HCl1', HCl=0.21, Water=0.79, price=price['HCl']) # price=0.21*price['HCl'] + 0.79*Water.price) # 35% HCl by mass # Acid to remove soaps after first centrifuge HCl2 = bst.Stream('HCl2', HCl=0.21, Water=0.79, price=HCl1.price) # Base to neutralize acid before distillation NaOH = bst.Stream('NaOH', NaOH=1, price=price['NaOH']) # Products crude_glycerol = bst.Stream('crude_glycerol', price=price['Crude glycerol']) biodiesel = bst.Stream('biodiesel', price=price['Biodiesel']) # Waste waste = bst.Stream('waste', price=price['Waste']) ### Units ### ### Biodiesel Transesterification Section ### # Aparently reactors are adiabatic, meoh coming in at 40C, lipid at 60C # From USDA Biodiesel model x_cat = 1.05e-05 # Catalyst molar fraction in methanol feed # Mix Recycle Methanol and Fresh Methanol T401 = units.StorageTank('T401') P401 = units.Pump('P401') # Storage Tank for Catalyst T402 = units.StorageTank('T402') P402 = units.Pump('P402') # Tank for oil T403 = units.StorageTank('T403') T403.tau = 4 P403 = units.Pump('P403') # Mix Methanol and Catalyst stream T404 = units.MixTank('T404') P404 = units.Pump('P404') # Split Methanol/Catalyst to reactors (this is done through a process specification, so use a fake splitter) S401 = bst.FakeSplitter('S401') # First Reactor R401 = units.Transesterification('R401', efficiency=0.90, methanol2lipid=6, T=333.15, catalyst_molfrac=x_cat) # Centrifuge to remove glycerol C401 = units.LiquidsSplitCentrifuge('C401', split=dict(Lipid=0.99, Methanol=0.40, Glycerol=0.06, Biodiesel=0.999, Water=0.40, NaOH=0, HCl=0, NaOCH3=0.40)) P405 = units.Pump('P405') # Second Reactor R402 = units.Transesterification('R402', efficiency=0.90, methanol2lipid=6, T=333.15, catalyst_molfrac=x_cat) def adjust_feed_to_reactors(): R402._run() S401.ins[0].sum(S401.outs) R402.specification = adjust_feed_to_reactors # Centrifuge to remove glycerol C402 = units.LiquidsSplitCentrifuge('C402', split=dict(Lipid=0.90, Methanol=0.10, Glycerol=0.05, Biodiesel=0.999, Water=0.10, NaOH=0, HCl=0, NaOCH3=0.10)) # Acids and bases per catalyst by mol k1 = 0.323 / 1.5 k2 = 1.060 / 1.5 k3 = 0.04505 / 1.5 def adjust_acid_and_base(): T404._run() # Adjust according to USDA biodiesel model catalyst_mol = T404.outs[0].imol['NaOCH3'] NaOH.imol['NaOH'] = k1 * catalyst_mol HCl1.imol['HCl'] = k2 * catalyst_mol HCl2.imol['HCl'] = k3 * catalyst_mol T404.specification = adjust_acid_and_base ### Biodiesel Purification Section ### # Water wash T405 = units.MixTank('T405') P406 = units.Pump('P406') # Centrifuge out water C403 = units.LiquidsRatioCentrifuge('C403', K_chemicals=('Methanol', 'Glycerol'), Ks=np.array([0.382, 0.183]), top_solvents=('Biodiesel', ), top_split=(0.999, ), bot_solvents=('Water', 'Lipid', 'NaOH', 'HCl'), bot_split=(0.999, 1, 1, 1)) # Vacuum dry biodiesel # Consider doing this by split, and keeping this unit constant # 290 Pa, 324 K according to USDA Biodiesel Model F401 = units.SplitFlash('F401', order=('Water', 'Methanol', 'Biodiesel'), split=(0.9999, 0.9999, 0.00001), P=2026.5, T=331.5, Q=0) F401.line = 'Vacuum dryer' F401.material = 'Stainless steel 304' P407 = units.Pump('P407') ### Glycerol Purification Section ### # Condense vacuumed methanol to recycle H401 = units.HXutility('H401', V=0, T=295) P408 = units.Pump('P408') # Mix recycled streams and HCl T406 = units.MixTank('T406') P409 = units.Pump('P409') # Centrifuge out waste fat # assume all the lipid, free_lipid and biodiesel is washed out C404 = units.LiquidsSplitCentrifuge('C404', outs=('', waste), order=('Methanol', 'Glycerol', 'Water'), split=(0.999, 0.999, 0.999)) # Add and mix NaOH T407 = units.MixTank('T407') P410 = units.Pump('P410') # Methanol/Water distillation column D401 = units.BinaryDistillation('D401', LHK=('Methanol', 'Water'), P=101325, y_top=0.99999, x_bot=0.0001, k=2.5, is_divided=True, vessel_material='Stainless steel 304', tray_material='Stainless steel 304') # Condense water to recycle H402 = units.HXutility('H402', T=353, V=0) # Glycerol/Water flash (not a distillation column) w = 0.20 / chemicals.Water.MW g = 0.80 / chemicals.Glycerol.MW x_water = w / (w + g) D402 = units.BinaryDistillation('D402', LHK=('Water', 'Glycerol'), k=1.25, P=101325, y_top=0.999, x_bot=x_water, tray_material='Stainless steel 304', vessel_material='Stainless steel 304') def startup_water(): imol = D402.ins[0].imol water, glycerol = imol['Water', 'Glycerol'] minimum_water = 5 * (w / (w + g)) * glycerol if water < minimum_water: imol['Water'] = minimum_water D402._run() # Remove accumulation D402.outs[0].imol['Water'] = 1100 * C402.outs[0].imol['Glycerol'] D402.specification = startup_water # Condense recycle methanol H403 = units.HXutility('H403', V=0, T=315) P411 = units.Pump('P411') # Condense recycle water H404 = units.HXutility('H404', V=0, T=315) P412 = units.Pump('P412') # Storage tank for glycerol T408 = units.StorageTank('T408', outs=crude_glycerol) # Storage tank for biodiesel T409 = units.StorageTank('T409', outs=biodiesel) F401 - 1 - P407 - 0 - T409 ### Biodiesel system set-up ### # Biodiesel Transesterification Section oil - T403 - P403 (P403 - 0, S401 - 0) - R401 - 0 - C401 (C401 - 0, S401 - 1) - R402 - 0 - C402 - 1 # Specs for product https://www.afdc.energy.gov/fuels/biodiesel_specifications.html # minimum spec requirements: # 0.050 wt % water (lower to 0.045) # 0.2 wt % meoh (lower to 0.15) # 0.02 wt % glycerol (lower to 0.015) # 0.4 wt % free lipids (lower to 0.35) # Find Water Flow def adjust_biodiesel_wash_water(): total_glycerol = (C401.outs[1].imol['Glycerol'] + R402.outs[0].imol['Glycerol']) wash_water = (x_water / (1 - x_water) * total_glycerol - HCl1.imol['Water'] - NaOH.imol['Water'] - oil.imol['Water'] - HCl2.imol['Water']) biodiesel_wash_water.imol[ 'Water'] = wash_water if wash_water > 0 else 0. T405._run() T405.specification = adjust_biodiesel_wash_water D402 - 0 - H404 - 0 - P412 # Biodiesel wash (C402 - 0, P412 - 0, biodiesel_wash_water, HCl1) - T405 - P406 - C403 # Glycerol recycle and purification section C403 - 0 - F401 F401 - 0 - H401 - P408 C401 - 1 - P405 (P405 - 0, C402 - 1, C403 - 1, P408 - 0, HCl2) - T406 - P409 - C404 (C404 - 0, NaOH) - T407 - P410 H402 - 0 - D401 - 1 - D402 - 1 - T408 P410 - 0 - H402 # Mass Balance for Methanol, Recycle Methanol, and Catalyst stream B401 = bst.MassBalance( 'B401', description='Adjust catalyst and methanol feed to reactors', variable_inlets=[catalyst, methanol], constant_inlets=[D401 - 0], constant_outlets=[1**R401, 1**R402], chemical_IDs=('Methanol', 'NaOCH3')) # Find Fresh Methanol Flow D401 - 0 - B401 - H403 - P411 # Recycle methanol methanol - T401 - P401 # Mix fresh and recycled methanol catalyst - T402 - P402 # Fresh catalyst (P411 - 0, P401 - 0, P402 - 0) - T404 - P404 - S401 # Mix Catalyst with Methanol # Initial guess D402.outs[0].imol['Methanol', 'Glycerol', 'Water'] = [0.00127, 3.59e-05, 0.0244] ### Facilities ### # Burn bagasse from conveyor belt BT = units.BoilerTurbogenerator( 'BT', (U202 - 0, '', 'boiler_makeup_water', 'natural_gas', '', ''), boiler_efficiency=0.80, turbogenerator_efficiency=0.85) CT = units.CoolingTower('CT') makeup_water_streams = (s.cooling_tower_makeup_water, s.boiler_makeup_water) process_water_streams = (s.imbibition_water, s.biodiesel_wash_water, oil_wash_water, rvf_wash_water, s.stripping_water, *makeup_water_streams) makeup_water = bst.Stream('makeup_water', price=0.000254) CWP = units.ChilledWaterPackage('CWP') PWC = units.ProcessWaterCenter('PWC', (bst.Stream(), makeup_water), (), None, makeup_water_streams, process_water_streams) return bst.System( ID, [ U101, U102, U103, T201, bst.System('juice_extraction_sys', [U201, S201, M201], recycle=M201 - 0), T202, H201, T203, P201, T204, T205, P202, bst.System( 'juice_separation_sys', [M202, H202, T206, C201, C202, P203], recycle=P203 - 0), T207, T207_2, H203, T208, C203, F201, T403, P403, R401, C401, R402, C402, bst.System('methanol_recycle_sys', [ T406, P409, C404, T407, P410, H402, D401, D402, H404, P412, T405, P406, C403 ], recycle=C403 - 1), F401, P407, T409, H401, P408, P405, B401, H403, P411, T401, P401, T402, P402, T404, P404, S401, S202, ethanol_production_sys, T408, U202 ], facilities=(CWP, BT, CT, PWC), )
def create_ethanol_subsystem_example(): """ Test BioSTEAM by creating a conventional sugarcane fermentation and ethanol purification process. Examples -------- >>> ethanol_sys = create_ethanol_subsystem_example() >>> # The sugarcane_example_subsystem flowsheet may help for accessing units >>> from biosteam import main_flowsheet as F >>> fs = F.flowsheet['ethanol_subsystem_example'] >>> fs.unit # Check unit operation registry Register: <Fermentation: R301> <StorageTank: T301> <VentScrubber: D301> <SolidsCentrifuge: C301> <Mixer: M302> <Pump: P301> <HXprocess: H302> <BinaryDistillation: D302> <Pump: P302> <Mixer: M303> <BinaryDistillation: D303> <Pump: P303> <HXutility: H303> <MolecularSieve: U301> >>> R301 = fs.unit.R301 # Get unit operation """ original_flowsheet = main_flowsheet.get_flowsheet() main_flowsheet.set_flowsheet('ethanol_subsystem_example') ### Create property package ### chemicals = tmo.Chemicals( ['Water', 'Ethanol', 'Glucose', 'Sucrose', 'H3PO4', 'P4O10', 'CO2', 'Octane', 'O2'] ) Water, Ethanol, Glucose, Sucrose, H3PO4, P4O10, CO2, Octane, O2 = chemicals CO2.at_state(phase='g') H3PO4.at_state(phase='s') P4O10.at_state(phase='s') Glucose.at_state(phase='s') Sucrose.at_state(phase='s') DryYeast = tmo.Chemical('DryYeast', MW=1., phase='s', search_db=False, default=True, CAS='Yeast') chemicals.append(DryYeast) Ash = tmo.Chemical('Ash', MW=1., search_db=False, phase='s', default=True) chemicals.append(Ash) # Insolubles occupy a significant volume insoluble_solids = (Ash, DryYeast) # Solubles don't occupy much volume soluble_solids = (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 Ash.Cn.add_model(0.09 * 4.184 * Ash.MW) for chemical in chemicals: chemical.default() bst.settings.set_thermo(chemicals) chemicals.set_synonym('Water', 'H2O') ### Create fresh streams ### # Fresh water stripping_water = bst.Stream('stripping_water', Water=5000, units='kg/hr') fermentation_feed = bst.Stream('fermentation_feed', phase='l', Glucose=21.11, Sucrose=125.9, Water=1370 + 4409, H3PO4=0.7575, DryYeast=1.03e+04) # Ethanol Production R301 = units.Fermentation('R301', outs=('CO2', ''), tau=9, efficiency=0.90, N=4) T301 = units.StorageTank('T301', tau=4, vessel_material='Carbon steel') T301.line = 'Beer tank' D301 = units.VentScrubber('D301', ins=(stripping_water, R301-0), gas=('CO2',)) # Separate 99% of yeast C301 = units.SolidsCentrifuge('C301', outs=('recycle_yeast', ''), split=(1, 0.99999, 1, 0.96, 0.01), order=('Ethanol', 'Glucose', 'H3PO4', 'Water', 'DryYeast'), solids=('DryYeast',)) C301.split[:] = 1. - C301.split # Mix in Water M302 = units.Mixer('M302') P301 = units.Pump('P301') # Heat up before beer column # Exchange heat with stillage H302 = units.HXprocess('H302', outs=('', 'stillage'), phase0='l', phase1='l', U=1.28) # Beer column x_bot = 3.910570816782338e-06 y_top = 0.34508430224337167 D302 = units.BinaryDistillation('D302', P=101325, y_top=y_top, x_bot=x_bot, k=1.25, LHK=('Ethanol', 'Water')) D302.tray_material = 'Stainless steel 304' D302.vessel_material = 'Stainless steel 304' D302.boiler.U = 1.85 P302 = units.Pump('P302') # Mix ethanol Recycle (Set-up) M303 = units.Mixer('M303') x_bot = 3.910570816782338e-06 y_top = 0.7906528373264998 D303 = units.BinaryDistillation('D303', P=101325, y_top=y_top, x_bot=x_bot, k=1.25, LHK=('Ethanol', 'Water'), tray_material='Stainless steel 304', vessel_material='Stainless steel 304', is_divided=True) D303.boiler.U = 1.85 P303 = units.Pump('P303') # Superheat vapor for mol sieve H303 = units.HXutility('H303', T=115+273.15, V=1) # Molecular sieve U301 = units.MolecularSieve('U301', split=(2165.14/13356.04, 1280.06/1383.85), order=('Ethanol', 'Water')) fermentation_feed-R301-1-T301-0-C301 (C301-1, D301-1)-M302-P301 (P301-0, P302-0)-H302-0-D302-1-P302 (D302-0, U301-0)-M303-0-D303-0-H303-U301 D303-1-P303 ethanol_subsystem_example = main_flowsheet.create_system('ethanol_subsystem_example') ethanol_subsystem_example.simulate() main_flowsheet.set_flowsheet(original_flowsheet) return ethanol_subsystem_example