def diagram(self, radius=0, upstream=True, downstream=True, file=None, format='png', **graph_attrs): """ Display a `Graphviz <https://pypi.org/project/graphviz/>`__ diagram of the unit and all neighboring units within given radius. Parameters ---------- radius : int Maxium number streams between neighbors. downstream=True : bool, optional Whether to show downstream operations upstream=True : bool, optional Whether to show upstream operations file : Must be one of the following: * [str] File name to save diagram. * [None] Display diagram in console. format : str Format of file. """ if radius > 0: neighborhood = self._neighborhood(radius, upstream, downstream) neighborhood.add(self) sys = bst.System('', neighborhood) return sys.diagram('thorough', file, format, **graph_attrs) f = self.get_digraph(format, **graph_attrs) finalize_digraph(f, file, format)
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 build_network(self): units = self.path + [self.sink] network = [] for segment in units: if isinstance(segment, list): source = segment[0] for recycle in segment[-1].outs: if recycle.sink is source: break system = bst.System(segment, recycle=recycle) network.append(system) else: network.append(segment) return network
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 test_parameter_hook(): import biosteam as bst from chaospy.distributions import Uniform sys = bst.System(None, ()) model = bst.Model(sys) @model.parameter(distribution=Uniform(0., 1.), hook=round) def set_param(x): pass np.random.seed(0) samples = model.sample(10, 'L') model.load_samples(samples) actual_values = [[1.], [0.], [0.], [0.], [0.], [0.], [1.], [1.], [1.], [1.]] assert_allclose(model.table.values, actual_values)
def create_tea(sys, OSBL_units, ignored_units=()): BT = tmo.utils.get_instance(OSBL_units, bst.facilities.BoilerTurbogenerator) OSBL_units = list(OSBL_units) OSBL_units.remove(BT) ethanol_tea = CellulosicEthanolTEA( system=sys, IRR=0.10, duration=(2007, 2037), depreciation='MACRS7', income_tax=0.35, operating_days=350.4, lang_factor=None, construction_schedule=(0.08, 0.60, 0.32), startup_months=3, startup_FOCfrac=1, startup_salesfrac=0.5, startup_VOCfrac=0.75, WC_over_FCI=0.05, finance_interest=0.08, finance_years=10, finance_fraction=0.4, OSBL_units=OSBL_units, # BT not included warehouse=0.04, site_development=0.09, additional_piping=0.045, proratable_costs=0.10, field_expenses=0.10, construction=0.20, contingency=0.10, other_indirect_costs=0.10, labor_cost=2.5e6, labor_burden=0.90, property_insurance=0.007, maintenance=0.03) for i in ignored_units: ethanol_tea.units.remove(i) ethanol_tea.units.remove(BT) Area700 = bst.TEA.like(bst.System('Area700', (BT, )), ethanol_tea) Area700.labor_cost = 0 Area700.depreciation = 'MACRS20' Area700.OSBL_units = (BT, ) tea = bst.CombinedTEA([ethanol_tea, Area700], IRR=0.10) sys._TEA = tea return tea
def test_sanunit(): import biosteam as bst from qsdsan import Components, WasteStream, sanunits components = Components.load_default() bst.settings.set_thermo(components) ws1 = WasteStream(S_Ac=5, H2O=1000, units='kg/hr') ws2 = WasteStream(X_NOO=10, H2O=1000, units='kg/hr') M1 = sanunits.Mixer('M1', ins=(ws1, ws2, ''), outs='mixture') M1.show() assert type(M1.ins[0]).__name__ == 'WasteStream' S1 = sanunits.Splitter('S1', ins=M1-0, outs=('', ''), split=0.2) ins3 = WasteStream(S_CH3OH=7, H2O=1000, units='kg/hr') P1 = sanunits.Pump('P1', ins=ins3) M2 = sanunits.MixTank('M2', ins=(S1-0, P1-0), tau=2) M2-0-2-M1 System = bst.System('System', path=(M1, S1, P1, M2), recycle=M2-0) System.simulate() assert_allclose(M2.installed_cost, 65519.00446342958, rtol=1e-3)
def create_evaluation_model(): import biosteam as bst from chaospy.distributions import Uniform sys = bst.System(None, ()) model = bst.Model(sys) parameter_box = [100.] @model.parameter(bounds=[90, 110], distribution=Uniform(90, 110), units='kg/hr') def set_parameter(parameter_1): parameter_box[0] = parameter_1 @model.parameter(bounds=[0., 1.], distribution=Uniform(0., 1.)) def set_parameter(parameter_2): ... @model.metric def good_metric(): p = parameter_box[0] return 2. * p * p / (p + 100.) switch_box = [False] @model.metric def bad_metric(): give_nan = switch_box[0] switch_box[0] = not give_nan p = parameter_box[0] return float('Nan') if give_nan else 2. * p * p / (p + 100.) @model.metric def non_correlated_metric(): return float(switch_box[0]) sample = model.sample(1000, 'L') model.load_samples(sample) model.evaluate() return model
def __call__(self, ID=None, ins=None, outs=None, mockup=False, area=None, udct=None, operating_hours=None, **kwargs): ins = create_streams(self.ins, ins, 'inlets', self.fixed_ins_size) outs = create_streams(self.outs, outs, 'outlets', self.fixed_outs_size) rename = area is not None with (bst.MockSystem() if mockup else bst.System(ID or self.ID, operating_hours=operating_hours)) as system: if rename: unit_registry = system.flowsheet.unit irrelevant_units = system._irrelevant_units unit_registry.untrack(irrelevant_units) self.f(ins, outs, **kwargs) system.load_inlet_ports(ins, optional=[ins[i] for i in self.optional_ins_index]) system.load_outlet_ports(outs, optional=[outs[i] for i in self.optional_outs_index]) if rename: units = system.units if udct: unit_dct = {i.ID: i for i in units} unit_registry.track(irrelevant_units) utils.rename_units(units, area) if udct: return system, unit_dct elif udct: unit_dct = {i.ID: i for i in system.units} return system, unit_dct return system
def create_fattyalcohol_production_sys( ID='fattyalcohol_production_sys', fa_stoichiometry='Glucose -> 1.5 Hexanol + 1.5 Octanol + 4.5 Decanol + H2O + CO2', yg_stoichiometry='Glucose -> 3.72 Cells + 2 CO2'): chemicals = tmo.settings.get_chemicals() ### Reactions ### fatty_alcohol_production = Rxn(fa_stoichiometry, 'Glucose', 0.5, basis='wt') fatty_alcohol_production.basis = 'mol' fatty_alcohol_production.correct_atomic_balance(constants=[ 'Hexanol', 'Octanol', 'Decanol', 'Dodecanol', 'Tetradecanol' ]) yeast_growth = Rxn(yg_stoichiometry, 'Glucose', .999, correct_mass_balance=True) ### Streams ### filepath = os.path.dirname(__file__) stream_data_path = os.path.join(filepath, 'streams.yaml') stream_data = tmo.ThermoData.from_yaml(stream_data_path) glucose, process_water, tridecane, CSL, DAP, salt, \ cell_recycle, mixed_bioreactor_feed = stream_data.create_streams( ['glucose', 'process_water', 'tridecane', 'CSL', 'DAP', 'salt', 'cell_recycle', 'mixed_bioreactor_feed'] ) process_water.T = 310.15 # Assume heat integration so that this stream comes in heated ### Unit operations ### M101 = units.Mixer('M101', ins=(tridecane, 'solvent_recycle')) M101.ins[ 1].T = 310.15 # Assume heat integration so that this stream comes in heated one_week = 7 * 24 T101 = units.StorageTank('T101', glucose, tau=one_week) T102 = units.StorageTank('T102', M101 - 0, tau=0.25) P102 = units.Pump('P102', T102 - 0) T103 = fa_units.CSLTank('T103', CSL) T104 = units.StorageTank('T104', salt, tau=one_week) T106 = fa_units.DAPTank('T106', DAP) def balance_feeds_to_bioreactor(): feed_imol = mixed_bioreactor_feed.imol glucose.imol['Glucose'] = feed_imol['Glucose'] process_water.imol[ 'Water'] = feed_imol['Water'] - M101.ins[1].imol['Water'] tridecane.imol['Tridecane'] = feed_imol['Tridecane'] - M101.ins[ 1].imol['Tridecane'] salt.imol['NaCl'] = feed_imol['NaCl'] cell_recycle.imol['Cells'] = feed_imol['Cells'] CSL.imol['CSL'] = feed_imol['CSL'] DAP.imol['DAP'] = feed_imol['DAP'] M101._run() M101.specification = balance_feeds_to_bioreactor M102 = units.Mixer('M102', ins=(T101 - 0, P102 - 0, T103 - 0, T104 - 0, T106 - 0, process_water, cell_recycle)) H101 = units.HXutility('H101', ins=M102 - 0, outs=mixed_bioreactor_feed, material='Stainless steel/stainless steel', T=310.15, V=0) def make_sure_enough_CSL_and_DAP_are_fed(): # tmo.reaction.CHECK_FEASIBILITY = False feed = R101.ins[0] CSL.imass['CSL'] = feed.imass['CSL'] = 0.0025 * feed.F_mass DAP.imass['DAP'] = feed.imass['DAP'] = 0.33 * feed.F_vol R101._run() # effluent = R101.outs[1] # if effluent.imol['CSL'] < 0. or effluent.imol['DAP'] < 0.: # CSL.imol['CSL'] = feed.imol['CSL'] = CSL_flow - effluent.imol['CSL'] # DAP.imol['DAP'] = feed.imol['DAP'] = DAP_flow - effluent.imol['DAP'] # effluent.imol['CSL'] = effluent.imol['DAP'] = 0. # tmo.reaction.CHECK_FEASIBILITY = True R101 = fa_units.FattyAlcoholBioreactor( 'R101', outs=('CO2', ''), ins=H101 - 0, fermentation_reaction=fatty_alcohol_production, postfermentation_reaction=yeast_growth, T=310.15, tau=32, V=3785, Nmin=2, Nmax=32) R101.specification = make_sure_enough_CSL_and_DAP_are_fed T105 = units.StorageTank('T105', ins=R101 - 1, tau=2) solids_split = chemicals.kwarray({'Cells': 0.999}) solids_split[solids_split == 0] = 0.001 solids_split[chemicals.index('Water')] = 0 P104 = units.Pump('P104', ins=T105 - 0) C101 = fa_units.SolidLiquidsSplitCentrifuge( 'C101', ins=P104 - 0, outs=('', 'aqueous_fraction', 'cell_mass'), solids_split=solids_split, liquids_split=dict(Water=0.0004, Tridecane=0.9966, Hexanol=0.6781, Octanol=0.9608, Decanol=0.9966, Dodecanol=0.9999, Tetradecanol=1.0), moisture_content=0.45) P107 = units.Pump('P107', C101 - 0, 'oil_fraction', P=5 * 101325) ### System ### return bst.System(ID, (M101, T102, P102, T101, T104, T103, T106, M102, H101, R101, T105, P104, C101, P107))
def diagram(self, radius=0, file=None, format='png'): """Display a `Graphviz <https://pypi.org/project/graphviz/>`__ diagram of the unit and all neighboring units within given radius. **Parameters** **radius:** [int] Maxium number streams between neighbors. **file:** Must be one of the following: * [str] File name to save diagram. * [None] Display diagram in console. **format:** Format of file. """ if radius > 0: neighborhood = self._neighborhood(radius) neighborhood.add(self) sys = bst.System('', neighborhood) return sys.diagram('thorough', file, format) graphics = self._graphics # Make a Digraph handle f = Digraph(name='unit', filename='unit', format='svg') f.attr('graph', ratio='0.5', splines='normal', outputorder='edgesfirst', nodesep='1.1', ranksep='0.8', maxiter='1000') # Specifications f.attr(rankdir='LR') # Left to right # If many streams, keep streams close if (len(self.ins) >= 3) or (len(self.outs) >= 3): f.attr('graph', nodesep='0.4') # Initialize node arguments based on unit and make node type_ = graphics.node_function(self) or self.line name = self.ID + '\n' + type_ f.attr('node', **self._graphics.node) f.node(name) # Set stream node attributes f.attr('node', shape='rarrow', fillcolor='#79dae8', style='filled', orientation='0', width='0.6', height='0.6', color='black', peripheries='1') # Make nodes and edges for input streams di = 0 # Destination position of stream for stream in self.ins: if not stream: continue f.node(stream.ID) edge_in = self._graphics.edge_in if di >= len(edge_in): di = 0 f.attr('edge', arrowtail='none', arrowhead='none', tailport='e', **edge_in[di]) f.edge(stream.ID, name) di += 1 # Make nodes and edges for output streams oi = 0 # Origin position of stream for stream in self.outs: if not stream: continue f.node(stream.ID) edge_out = self._graphics.edge_out if oi >= len(edge_out): oi = 0 f.attr('edge', arrowtail='none', arrowhead='none', headport='w', **edge_out[oi]) f.edge(name, stream.ID) oi += 1 save_digraph(f, file, format)
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_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), )
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 EtOH_start_path = (S301, F301, P306, M301, H301, T305, R301, T301, C301, D301, M302, P301, H302, D302, P302, H302) (D302 - 0, U301 - 0) - M303 - 0 - D303 - 0 - H303 - U301 D303 - 1 - P303 ethanol_recycle_sys = bst.System('ethanol_recycle_sys', path=(M303, D303, H303, U301), recycle=M303 - 0) pure_ethanol = P304.outs[0] def adjust_denaturant(): denaturant.imol['Octane'] = 0.021 * pure_ethanol.F_mass / 114.232 PS4 = bst.ProcessSpecification('PS4', specification=adjust_denaturant) U301 - 1 - H304 - 0 - T302 - 0 - P304 - 0 - PS4 denaturant - T303 - P305 (P305 - 0, PS4 - 0) - M304 - T304 (P303 - 0, F301 - 1) - M305
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 create_system(ID='LAOs_sys', stainless_steel=True): checks = {'check_atomic_balance': True} # LAOs production LAOs_production = ParallelRxn([ Rxn('Hexanol -> Hexene + H2O', 'Hexanol', 1, **checks), Rxn('Octanol -> Octene + H2O', 'Octanol', 1, **checks), Rxn('Decanol -> Decene + H2O', 'Decanol', 1, **checks), Rxn('Dodecanol -> Dodecene + H2O', 'Dodecanol', 1, **checks), Rxn('Tetradecanol -> Tetradecene + H2O', 'Tetradecanol', 1, **checks), Rxn('Hexadecanol -> Hexadecene + H2O', 'Hexadecanol', 1, **checks), ]) stream_data_path = os.path.join(os.path.dirname(__file__), 'streams.yaml') stream_data = tmo.ThermoData.from_yaml(stream_data_path) hexene, octene, decene = stream_data.create_streams(['hexene', 'octene', 'decene']) fattyalcohol_production_sys = create_fattyalcohol_production_sys() F.unit.C101.liquids_isplit['Hexene', 'Octene', 'Decene', 'Dodecene', 'Tetradecene'] = 1.0 H102 = units.HXprocess('H102', ins=F.unit.P107-0, dT=10, phase0='g', phase1='l') M103 = units.Mixer('M103', ins=(H102-0, None)) H103 = units.HXutility('H103', ins=M103-0, V=1, T=623.15) R102 = LAOs_units.AdiabaticFixedbedGasReactor('R102', ins=H103-0, WHSV=1., vessel_type='Vertical', P=5 * 101325, reaction=LAOs_production, length_to_diameter=2, catalyst_price=9.69, catalyst_density=2650 ) R102-0-1-H102 R102.dehydration_reactor_mass_fraction = 0.10 # A proces specification H105 = units.HXutility('H105', ins=H102-1, T=320) T107 = LAOs_units.SurgeTank('T107', H105-0, ('recycle_nitrogen', 'LAOs_to_separations'), tau=0.1) P105 = units.Pump('P105', T107-1) C102 = units.LiquidsSplitCentrifuge('C102', P105-0, split=dict(Water=0.1, Tridecane=1.0, Hexene=1.0, Octene=1.0, Decene=1.0, Dodecene=1.0, Tetradecene=1.0)) P106 = units.Pump('P106', C102-0) H104 = units.HXprocess('H104', ins=(P106-0, None), phase0='l', phase1='l') # Lab mass fraction is 0.0242 g / g # Baseline mass fraction is 0.1 g /g def adjust_nitrogen_flow(): T107._run() recycle_nitrogen = T107.outs[0] feed = H102.outs[0] F_mass = feed.F_mass F_mass_alcohols = feed.imass[LAOs.fermentation_products].sum() x_alcohol = R102.dehydration_reactor_mass_fraction # Math to get amount of nitrogen flow: # x_alcohol = F_mass_alcohols / (F_mass + F_mass_N2) # F_mass_N2 * x_alcohol + x_alcohol * F_mass = F_mass_alcohols # F_mass_N2 * x_alcohol = F_mass_alcohols - x_alcohol * F_mass F_mass_N2 = F_mass_alcohols / x_alcohol - F_mass if F_mass_N2 < 0: F_mass_N2 = 0 recycle_nitrogen.imass['Nitrogen'] = F_mass_N2 recycle_nitrogen.phase = 'g' T107.specification = adjust_nitrogen_flow T107 - 0 - 1 - M103 D101 = units.ShortcutColumn('D101', H104-0, LHK=('Decene', 'Tridecane'), k=1.05, Lr=0.999, Hr=0.90, is_divided=True) def distillate_recoveries_hook(IDs, recoveries): light_keys = ('Water', 'Hexene', 'Octene') index = [n for n, i in enumerate(IDs) if i in light_keys] recoveries[index] = 1.0 D102 = units.ShortcutColumn('D102', D101-0, LHK=('Decene', 'Tridecane'), k=1.05, y_top=0.98, x_bot=1e-9, is_divided=True) D102._distillate_recoveries_hook = distillate_recoveries_hook M105 = units.Mixer('M105', (D101-1, D102-1)) P108 = units.Pump('P108', M105-0) P108-0-1-H104 settler_data = {'split': dict(Water=0.000227, Tridecane=0.999999, Hexanol=0.342, Octanol=0.859, Decanol=0.932, Dodecanol=0.999, Hexene=1.0, Octene=1.0, Decene=1.0, Dodecene=1.0, Tetradecene=1.0, Tetradecanol=1.0) } M104 = units.MixerSettler('M104', (H104-1, F.unit.C101-1, C102-1, ''), ('', 'wastewater'), settler_data=settler_data, model='split') def adjust_price_per_organics(): M104._run() wastewater = M104.outs[1] wastewater.price = -0.33 * (1 - wastewater.imass['Water', 'NaCl'].sum() / wastewater.F_mass) M104.specification = adjust_price_per_organics M104.outs[1].price = -0.33 # USD / kg organics P109 = units.Pump('P109', M104-0, 1**F.unit.M101) H109 = units.HXutility('H109', D102-0, T=320, V=0) C103 = units.LiquidsSplitCentrifuge('C103', H109-0, outs=('', 3**M104), split=dict(Water=0.01, Hexene=0.995, Octene=0.999, Decene=1.0, Dodecene=1.0, Tetradecene=1.0)) D103 = units.ShortcutColumn('D103', LHK=('Hexene', 'Octene'), k=1.05, y_top=0.98, x_bot=1e-6) H106 = units.HXutility('H106', D103-0, T=320, V=0) D104 = units.BinaryDistillation('D104', D103-1, LHK=('Octene', 'Decene'), k=1.05, y_top=0.98, x_bot=1e-6) H107 = units.HXutility('H107', D104-0, T=320, V=0) H108 = units.HXprocess('H108', (C103-0, D104-1), dT=5, phase1='l') H108-0-D103 T108 = units.StorageTank('T108', H106-0, hexene, vessel_type='Floating roof', tau=7*24) T109 = units.StorageTank('T109', H107-0, octene, vessel_type='Floating roof', tau=7*24) T110 = units.StorageTank('T110', H108-1, decene, vessel_type='Floating roof', tau=7*24) ### Facilities ### *other_agents, high_pressure_steam = bst.HeatUtility.heating_agents BT = bst.BoilerTurbogenerator('BT', ins=(None, None, 'boiler_makeup_water', 'natural_gas', 'lime', 'boiler_chemicals'), boiler_efficiency = 0.80, turbogenerator_efficiency=0.85, agent=high_pressure_steam, other_agents=other_agents) CT = bst.CoolingTower('CT') CWP = bst.ChilledWaterPackage('CWP') CCI = bst.ChemicalCapitalInvestment('CCI', 'Tridecane', 644.05) ### System ### sys = bst.System('LAOs_sys', [bst.System('tridecane_recycle', [fattyalcohol_production_sys, bst.System('reaction_heat_integration_sys', [H102, H105, T107, M103, H103, R102], recycle=R102-0), P105, C102, P106, bst.System('distillation_heat_integration_sys', [H104, D101, D102, C103, H109, M104, M105, P108], recycle=P108-0), P109], recycle=1**F.unit.M101), H108, D103, D104, H106, H107, H108, T108, T109, T110], facilities=[CWP, BT, CT, BT, CCI]) if stainless_steel: isa = isinstance for i in sys.units: if isa(i, bst.HX): i.material = 'Stainless steel/stainless steel' elif isa(i, bst.BinaryDistillation): i.boiler.material = i.condenser.material = 'Stainless steel/stainless steel' i.vessel_material = 'Stainless steel 304' i.tray_material = 'Stainless steel 304' elif isa(i, bst.Pump): i.material = 'Stainless steel' return sys
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
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), )