サトウキビ由来のバイオ・エタノール製造設備

BioSTEAM”のケーススタディとしてサトウキビ由来のバイオ・エタノール製造設備のモデルの解説をしています。 オリジナルのページはSugarcane ethanol biorefineryです。 ソースコードは以下の実行環境で確認しています。
  • Visual Studio Code バージョン: 1.104.2
  • 拡張機能:Jupyter バージョン 2025.8.0
  • Python 3.12.10
  • biosteam 2.52.13
  • graphviz-14.0.2

サトウキビ由来のバイオ・エタノール製造設備

このケーススタディでは、サトウキビ由来のバイオ・エタノール製造設備の経済的実現可能性(ポテンシャル)を評価し、経済性に影響を与える主要な前提条件を明らかにします。 まず、ゼロから完全なバイオ・エタノール製造モデルを構築し、その後、BioSTEAMの ProcessModel オブジェクトや既存の公開モデルがバイオ利用プロセスの構築をどのように支援できるかを示します。 このサトウキビ由来のバイオ・エタノール製造設備は、すべてのプロセス設定、価格、前提条件を含めて、Huangらの文献[1]に基づいて設計されています。

熱力学的特性の定義

最初に、このプロセスでの熱力学的特性を定義します。Thermosteam のドキュメントにあるに従って実行します。
  1. 熱力学的特性の設定
  2. import biosteam as bst
    bst.nbtutorial() # Ignore warnings and reset local BioSTEAM preferences
    bst.settings.set_thermo(
        ['Water', # Define chemicals by name
         'Ethanol',
         'Octane',
         'Glucose',
         'Sucrose',
         'H3PO4',
         'P4O10',
         'CO2',
         'O2',
         'N2',
         'CH4',
         'Cellulose',
         'Hemicellulose',
         'Lignin',
         'Flocculant',
         'Solids',
         'DryYeast',
         'CaO',
         'Ash'],
        # Use the BioSTEAM database (not the default ChEDL) for common
        # biorefinery chemicals like lignocellulosic components.
        db='BioSTEAM'
    )
    # Loading Cellulose from the BioSTEAM database is approximately
    # equal to the following code:
    # bst.Chemical(
    #     'Cellulose',
    #     Cp=1.364, # Heat capacity [kJ/kg]
    #     rho=1540, # Density [kg/m3]
    #     default=True, # Default other chemicals properties like viscosity to that of water at 25 C
    #     search_db=False, # Not in database, so do not search the database
    #     phase='s',
    #     formula="C6H10O5", # Glucose monomer minus water, molecular weight is computed based on formula
    #     Hf=-975708.8 # Heat of formation [J/mol]
    # )

プロセスの設定

シミュレーションをする前に、ユーティリティ(設備)の条件と、化学工学プラント費用指数(Chemical Engineering Plant Cost Index、CEPCI)、投入材料/薬品類の価格を設定します。
  1. CEPCI、低圧蒸気、投入材料/薬品類の価格を設定
  2. bst.settings.CEPCI = 567 # CEPCI: 2013
    bst.settings.electricity_price = 0.065
    # Steam is produced on-site by a boiler,
    # so make it the only available heating agent.
    steam_utility = bst.settings.get_agent('low_pressure_steam')
    bst.settings.heating_agents = [steam_utility]
    steam_utility.heat_transfer_efficiency = 0.9
    steam_utility.T = 529.2
    steam_utility.P = 44e5
    
    # Steam, cooling water, and chilled water are regenerated by
    # on-site facilities. The regeneration and heat transfer
    # prices are given accounted for by the capital cost and
    # electricity consumption of these facilities
    steam_utility.regeneration_price = 0.
    bst.settings.get_agent('cooling_water').regeneration_price = 0
    bst.settings.get_agent('chilled_water').heat_transfer_price = 0
    
    # Raw material price (USD/kg)
    price = {'Sugar cane': 0.03455, # 70% m.c
             'Water': 0.000353,
             'HCl': 0.205,
             'Lime': 0.077,
             'H3PO4': 0, # Not significant
             'NaOH':0.41,
             'Protease': 0.5,
             'Polymer': 0, # Not significant
             'Steam': 0.017,
             'Ethanol': 0.789,
             'Waste': -0.33,
             'Gasoline': 0.756} # 2 USD/gal

機器ユニットの設定とシミュレーション

最初に原材料の受入れ(エリア 100)と搾汁(エリア 200)の工程の設定をします。

  1. 物質のグループ化
  2. 'Fiber'、'Sugar'とまとめて指定したいときの構成の定義しておきます。
  3. 材料であるサトウキビ(sugracane)の成分、流量の定義
    • 水分、グルコース、スクロース、灰分(Ash)、'Fiber'、固形物(solids)の成分割合の定義
    • 供給流量の定義(370,396 kg/hr)の設定
  4. 酵素の設定
    • 酵素と言っても計算上はセルロースと水分となっています。
    • セルロース 100kg/hr、水分 900kg/hrです。
  5. 浸漬水の設定
    • 原料から糖分を抽出する際に加える水分。
    • 水分 87023.35kg/hr、温度65℃です。
  6. リン酸H3PO4の設定
    • サトウキビバガスのリグニンやヘミセルロースを部分的に分解し、酵素糖化、発酵効率向上を図る。
    • H3PO4 74.23kg/hr、水分 13.10kg/hrです。濃度は約85%ですね。
    • コストも設定あり。
  7. lime(主に水酸化カルシウムCa(OH)2または生石灰 CaO)の設定
    • pHを調整し、不純物を沈殿させて除去させるため。
    • CaO 333kg/hr、水分 2200kg/hr。
    • コストも設定あり。
  8. 高分子凝集剤(ポリマー)の設定
    • 糖汁や発酵液に含まれる微細な固形物やコロイド状不純物を凝集させ、 沈降・ろ過を容易にさせるために投入されるそうです。
    • 0.83kg/hr、コストも設定あり。
  9. 回転真空フィルター(RVF: Rotary Vacuum Filter)洗浄水の設定
    • サトウキビ圧搾汁を液体と固形分(フィルターケーキ)に分離する装置に供給される洗浄水。
    • 16770kg/hr、温度90℃。

次に、投入された原材料をベルトコンベアで輸送し、異物を除去、裁断する機器を並べます。これらはモデル上は流出ストリームがそのまま流出するような設定になっているものもありますが、消費電力やコストを見積もるときには必要となってきます。

  • U101:ベルトコンベアで投入原料を移送。
  • U102:磁力による異物除去
  • U103:シュレッダー
  • T201:酵素混合タンク。温度は50℃。
  • U201:裁断機
  • 浸漬水と混合し、原料を裁断する想定で、灰分の92%、繊維分の92%、固形分100%と糖分のうち4%をバガスとして分離。
  • U202:ベルトコンベア
  • 粉砕機で分離された繊維分等'Baggasse'という名前のストリームとして搬出。
  • S201:振動スクリーン
  • U201で分離された灰分、繊維分の少ない方のストリームから、さらに灰分の35%、繊維分の35%、糖分の88%、水分の88%を分離。
  • M201:S201で分離された残りと、浸漬水を混合し、'imbibition_water_recycle'として流出させる。
  • T202:S201で分離した液を貯蔵タンクに貯蔵する。
  • H201:タンクT202から取り出した液を酸と混合する前に70℃に加熱する。
  • T203:リン酸とを混合するタンク
  • P201:リン酸との混合液を石灰混合タンクに移送するポンプ
  • T204:石灰と混合するタンク
  • T205:さらに混合するタンク
  • P202:混合タンクT205から高分子凝縮材と混合するために移送するポンプ
  • M202:高分子凝縮材と混合する前に回転真空フィルターからの排出液と混合
  • H202:高分子凝縮材と混合する前に99℃まで加熱
  • T206:高分子凝縮材と混合するタンク
  • C201:高分子凝縮材と混合した液を固液分離させる沈降槽
  • 高分子凝集剤の52.2%、糖分の52.2%、リン酸の52.2%、水分の52.2%を液体分として分離し、灰分、石灰分、繊維分は個体分として分離。
  • C202:沈降槽の個体分を更に分離する回転真空フィルター
  • 含水率を80%とし、灰分85%、石灰分85%、繊維分85%、糖分1%を分離。
  • P203:回転真空フィルターC202で分離された液を排出液としてM202に移送するポンプ
  • S202:振動スクリーン
  • 沈降槽で分離した液体分に残る灰分、石灰分、繊維分の全てと糖分の99.8%、リン酸100%、水分の99.8%を分離。残りは糖分の0.2%と高分子凝集剤、水分の0.2%が回収されます。

最後に、シュレッダーU103とポンプP202の実行時に調整されるルールが仕様を満たすための関数(specification function)を、Pythonのデコレータ[2]を使って記載されています。U103は酵素ストリームの流量が投入される材料の質量流量の3%で、その内のセルロースと水分のモル数が1:9に調整されます。 P202の方は、回収される水分のモル数が固形分である灰分、石灰、繊維分のモル数の合計の5.74%に調整されます。

ここまでの工程を図で確認します。

  1. 原材料の受入れ(エリア 100)と搾汁(エリア 200)工程
  2. from biosteam import units
    import numpy as np
    
    bst.main_flowsheet.set_flowsheet('sugarcane_ethanol')
    
    # We can create streams and set component splits faster by defining chemical groups
    chemicals = bst.settings.chemicals
    chemicals.define_group(
        name='Fiber',
        IDs=['Cellulose', 'Hemicellulose', 'Lignin'],
        composition=[0.4704 , 0.2775, 0.2520],
        wt=True, # Composition is given as weight
    )
    chemicals.define_group(
        name='Sugar',
        IDs=['Sucrose', 'Glucose'],
        # Default composition as equimolar
    )
    
    sugarcane = bst.Stream(
        'sugarcane',
        Water=0.7,
        Glucose=0.01208,
        Sucrose=0.1369,
        Ash=0.006,
        Fiber=0.13,
        Solids=0.015,
        total_flow=370396,
        units='kg/hr',
        price=price['Sugar cane']
    )
    
    enzyme = bst.Stream('enzyme',
                        Cellulose=100, Water=900, units='kg/hr',
                        price=price['Protease'])
    
    imbibition_water = bst.Stream('imbibition_water',
                                  Water=87023.35, units='kg/hr',
                                  T = 338.15)
    
    H3PO4 = bst.Stream('H3PO4',
                       H3PO4=74.23, Water=13.10, units='kg/hr',
                       price=price['H3PO4'])  # to T203
    
    lime = bst.Stream('lime',
                      CaO=333.00, Water=2200.00, units='kg/hr',
                      price=price['Lime'])  # to P5
    
    polymer = bst.Stream('polymer',
                         Flocculant=0.83, units='kg/hr',
                         price=price['Polymer'])  # to T205
    
    rvf_wash_water = bst.Stream('rvf_wash_water',
                                Water=16770, units='kg/hr',
                                T=363.15)  # to C202
    
    ### Unit operations ###
    
    # Feed from storage
    U101 = units.ConveyingBelt('U101', sugarcane)
    
    # Separate metals
    U102 = units.MagneticSeparator('U102', U101-0)
    
    # Shredded cane
    U103 = units.Shredder('U103', U102-0)
    
    # Hydrolyze starch
    T201 = units.EnzymeTreatment('T201', [U103-0, enzyme], T=323.15)  # T=50
    
    # Finely crush lipid cane
    imbibition_water_recycle = bst.Stream() # To connect later
    U201 = units.CrushingMill('U201', [T201-0, imbibition_water_recycle],
                              split=dict(Ash=0.92,
                                         Fiber=0.92,
                                         Sugar=0.04,
                                         Solids=1),
                              moisture_content=0.5)
    
    # Convey out bagasse
    U202 = units.ConveyingBelt('U202', U201-0, outs='Bagasse')
    
    # Screen out fibers
    S201 = units.VibratingScreen('S201', U201-1,
                                 split=dict(Ash=0.35,
                                            Fiber=0.35,
                                            Sugar=0.88,
                                            Water=0.88,
                                            Solids=0))
    
    # Mix in water
    M201 = units.Mixer('M201', [S201-1, imbibition_water], imbibition_water_recycle)
    
    # Store juice before treatment
    T202 = units.StorageTank('T202', S201-0, tau=4, vessel_material='Carbon steel')
    
    # Heat up before adding acid
    H201 = units.HXutility('H201', T202-0, T=343.15)
    
    # Mix in acid
    T203 = units.MixTank('T203', [H201-0, H3PO4])
    
    # Pump acid solution
    P201 = units.Pump('P201', T203-0)
    
    # Mix lime solution
    T204 = units.MixTank('T204', [P201-0, lime], tau=0.10)
    
    # Blend acid lipid solution with lime
    T205 = units.MixTank('T205', T204-0, tau=0.10)
    P202 = units.Pump('P202', T205-0)
    
    # Mix recycle
    RVF_recycle = bst.Stream() # From rotary vacuum filter; connect later
    M202 = units.Mixer('M202', [P202-0, RVF_recycle])
    
    # Heat before adding flocculant
    H202 = units.HXutility('H202', M202-0, T=372.15)
    
    # Mix in flocculant
    T206 = units.MixTank('T206', [H202-0, polymer])
    T206.tau = 0.10
    
    # Separate residual solids
    C201 = units.Clarifier('C201', T206-0,
                           split=dict(Ash=0,
                                      CaO=0,
                                      Fiber=0,
                                      Flocculant=0.522,
                                      Sugar=0.522,
                                      H3PO4=0.522,
                                      Water=0.522))
    
    # Remove solids as filter cake
    C202 = units.RVF('C202', [C201-1, rvf_wash_water],
                     outs=('filter_cake', ''),
                     moisture_content=0.80,
                     split=dict(Ash=0.85,
                                CaO=0.85,
                                Fiber=0.85,
                                Sugar=0.01))
    P203 = units.Pump('P203', C202-1, RVF_recycle)
    
    
    # Screen out small fibers from sugar stream
    S202 = units.VibratingScreen('S202', C201-0,
                                 outs=('', 'fiber_fines'),
                                 split=dict(Ash=1.0,
                                            CaO=1.0,
                                            Fiber=1.0,
                                            Flocculant=0.0,
                                            Sugar=0.998,
                                            H3PO4=1.0,
                                            Water=0.998))
    S202.mesh_opening = 2
    
    ### Process specifications ###
    
    # Specifications dependent on lipid cane flow rate
    @U103.add_specification(run=True) # Run unit operation afterwords
    def correct_flows():
        feedstock = U101.ins[0]
        F_mass = feedstock.F_mass
        # correct enzyme, lime, phosphoric acid, and imbibition water
        enzyme.imass['Cellulose', 'Water'] = 0.003 * F_mass * np.array([0.1, 0.9])
        lime.imass['CaO', 'Water'] = 0.001 * F_mass * np.array([0.046, 0.954])
        H3PO4.imass['H3PO4', 'Water'] = 0.00025 * F_mass
        imbibition_water.imass['Water'] = 0.25* F_mass
    
    # Specifications within a system
    @P202.add_specification(run=True)
    def correct_wash_water():
        solids = P202.ins[0].imol['Ash', 'CaO', 'Fiber'].sum()
        rvf_wash_water.imol['Water'] = 0.0574 * solids
    
    bst.main_flowsheet.diagram(format='png') # Flow sheet up until now

    次に搾汁液からエタノールを生成するエリア300の機器を配置します。

    • 使用するストリームの定義
      • 洗浄水
      • 発酵後の排気ガスを洗浄する時に使用する水。
      • 変性剤
      • 燃料エタノールを飲用できないようにするための薬剤。ここではガソリンを使用。
      • 酵母
      • 発酵槽で使用する、糖類からエタノールを生成する酵母。
      • エタノール
      • エタノール生成量が格納されるストリーム。
    • S301:分流器
    • 前工程でふるい分けられた材料をさらに1:9に分け、後者を多重効用蒸発装置に供給します。
    • F301:多重効用蒸発装置。
    • 糖分の濃度が23wt%になるまで蒸発させます。
    • P306:次工程にポンプで移送します。
    • M301:混合器
    • 多重効用蒸発装置から圧送されてきたストリームと、分配器で分配して多重効用蒸発装置に送られなかったストリームを混合する。
    • H301:発酵タンクに送る前に冷却します。
    • T305:酵母をタンクに貯蔵します。
    • R301:酵母を混合し、発酵させます。
    • T301:発酵液を貯蔵するタンク
    • D301:ベントスクラバー(ベントガス洗浄装置)
    • CO2を分離します。その際、ストリームで定義した洗浄水と混合します。
    • C301:酵母を遠心分離機で分離します。
    • M302:混合器
    • 酵母を分離した後のストリームと、ベントスクラバー(ベントガス洗浄装置)でCO2を除去したストリームと混合します。
    • P301:混合ストリームをポンプで移送します。
    • H302:蒸留前の加熱
    • 混合したストリームを蒸留に備えて加熱します。熱源は蒸留塔の塔底流出物を使います。
    • D302:蒸留(1回目、ビール塔)。
    • P302:塔底流出物を熱交換器H302にポンプで圧送します。
    • M303:混合器
    • 2回目の蒸留後に通すモレキュラーシーブ(分子ふるい)の分離水を混合します。
    • D303:蒸留(2回目、精留塔)。
    • P303:2回目の蒸留の塔底流出物をポンプで取り出します。
    • H303: モレキュラーシーブ(吸着材)を通す前に、115℃まで加熱します。
    • U301:モレキュラーシーブ(分子ふるい)
    • モレキュラーシーブ(分子ふるい)を通します。エタノールのうち16.21%、水分の92.5%が除去される設定です。除去された分は先程の混合器に送られます。
    • H304:66.85℃に冷却します。
    • T302:タンクに貯蔵します。
    • P304:エタノールを貯蔵タンクよりポンプで取り出します。
    • T303:変性剤をタンクに貯蔵します。
    • P305:変性剤をタンクよりポンプで取り出します。
    • T304:変性剤の添加、混合するタンク
    • 変性剤を添加します。燃料用のエタノールには、変性剤(denaturant)を混合することで飲用できないようにすることが米国などでは決められているそうで、例えば米国規格ASTM D4806では天然ガソリンやガソリン成分を最低1.96%、最大4.76%混合することが義務付けられているそうです。このモデルでは、エタノールの2.1 wt%のオクタンを混合するようになっています。
    • M305:混合器
    • 2回目の蒸留時の塔底流出物P303と発酵時の排液を混合し、'wastewater'として排出します。

    ここまでの工程を図で確認します。見えないですよね...。VS Code等で実行して出力されるpngファイルをビューアー(viewer)等で拡大して見て頂くのが良いかと思います。

  3. エタノール生成(エリア 300)工程
  4. ### Streams ###
    
    # Fresh water
    stripping_water = bst.Stream('stripping_water', Water=5000, units='kg/hr')
    
    # Gasoline
    denaturant = bst.Stream('denaturant', Octane=230.69,
                            units='kg/hr', price=price['Gasoline'])
    
    # Yeast
    yeast = bst.Stream('yeast', Water=24700, DryYeast=10300, units='kg/hr')
    
    # Ethanol product
    ethanol = bst.Stream('ethanol', price=price['Ethanol'])
    
    ### Units ###
    
    # Split sugar solution
    S301 = units.Splitter('S301',  S202-0, split=0.10)
    
    # Concentrate sugars
    F301 = units.MultiEffectEvaporator('F301', S301-1,
                                       P=(101325, 73581, 50892, 32777),
                                       V_definition='First-effect',
                                       V=0.1) # fraction evaporated
    P306 = units.Pump('P306', F301-0)
    
    # Mix sugar solutions
    M301 = units.Mixer('M301', [P306-0, S301-0])
    
    F301.target_sugar_concentration = 0.23 # wt. % sugar
    @F301.add_bounded_numerical_specification(x0=0, x1=1, xtol=1e-5, ytol=1e-2)
    def sugar_concentration_at_fraction_evaporated(V):
        F301.V = V
        F301.run_until(M301, inclusive=True) # Run all units starting from F301 to M301
        sugar_concentration = M301.outs[0].get_mass_fraction('Sugar')
        return F301.target_sugar_concentration - sugar_concentration
    
    # Cool for fermentation
    H301 = units.HXutility('H301', M301-0, T=295.15)
    
    # Yeast preparation
    T305 = units.MixTank('T305', yeast)
    T305.tau = 0.1
    
    # Ethanol Production
    R301 = units.Fermentation('R301', [H301-0, T305-0], outs=('CO2', ''), tau=9, efficiency=0.90, N=4)
    R301.cell_growth_reaction.X = 0. # Ignore for simplicity
    T301 = units.StorageTank('T301', R301-1, tau=4, vessel_material='Carbon steel')
    T301.line = 'Beer tank' # Changes name on the diagram
    
    D301 = units.VentScrubber('D301', ins=(stripping_water, R301-0),
                              outs=('vent', ''),
                              gas=('CO2',))
    
    # Separate 99% of yeast
    C301 = units.SolidsCentrifuge('C301', T301-0, outs=('recycle_yeast', ''),
                                moisture_content=0.5,
                                split=(1, 0.99999, 0.99), # This gets reverted in the next line
                                order=('Ethanol', 'Glucose',  'DryYeast'),
                                solids=('DryYeast',))
    C301.split[:] = 1. - C301.split
    
    # Add bottoms from scrubber
    M302 = units.Mixer('M302', [C301-1, D301-1])
    P301 = units.Pump('P301', M302-0)
    
    # Heat up before beer column
    # Exchange heat with stillage
    bottoms_product = bst.Stream() # Bottoms product from beer column, connect later
    H302 = units.HXprocess('H302', [P301-0, bottoms_product], U=1.28)
    
    # Beer column
    D302 = units.BinaryDistillation(
        'D302', H302-0, P=2.1 * 101325,
        Lr=0.99993, Hr=0.8735, # Light and heavy key recoveries
        LHK=('Ethanol', 'Water'), # Light and heavy key
        k=1.1, # Ratio of actual reflux over minimum reflux
        Rmin=0.001, # Minimum allowable reflux ratio
    )
    D302.tray_material = 'Stainless steel 304'
    D302.vessel_material = 'Stainless steel 304'
    D302.reboiler.U = 1.85
    P302 = units.Pump('P302', D302-1, bottoms_product)
    
    # Mix ethanol Recycle (Set-up)
    molecular_sieve_recycle = bst.Stream()
    M303 = units.Mixer('M303', [D302-0, molecular_sieve_recycle])
    
    D303 = units.BinaryDistillation(
        'D303', M303-0,
        P=10 * 101325, # Higher pressure to enable heat exchanger between condenser and multi-effect evaporator
        y_top=0.80805, x_bot=3.91e-06,  # Molar fraction of light key in the distillate and bottoms product
        k=1.15,
        LHK=('Ethanol', 'Water'),
        tray_material='Stainless steel 304',
        vessel_material='Stainless steel 304',
        is_divided=True
    )
    D303.reboiler.U = 1.85
    P303 = units.Pump('P303', D303-1)
    
    # Superheat vapor for mol sieve
    H303 = units.HXutility('H303', D303-0, T=115+273.15, V=1, heat_only=True)
    
    # Molecular sieve
    U301 = units.MolecularSieve('U301', H303-0, [molecular_sieve_recycle, ''],
                                split=dict(Ethanol=0.1621,
                                           Water=0.925))
    
    # Condense ethanol product
    H304 = units.HXutility('H304', U301-1, V=0, T=340.)
    T302 = units.StorageTank('T302', H304-0, tau=7*24, # 1 week storage capacity
                             vessel_type='Floating roof',
                             vessel_material='Carbon steel')
    P304 = units.Pump('P304', T302-0)
    
    # Storage for denaturant
    T303 = units.StorageTank('T303', denaturant, tau=7*24,
                             vessel_type='Floating roof',
                             vessel_material='Carbon steel')
    P305 = units.Pump('P305', T303-0)
    
    # Denatured ethanol product
    T304 = units.MixTank('T304', [P305-0, P304-0], outs=ethanol)
    T304.tau = 0.10 # 6 min residence time
    
    # Waste water
    M305 = units.Mixer('M305', [P303-0, F301-1], outs='wastewater')
    
    @P304.add_specification(run=True)
    def adjust_denaturant():
        pure_ethanol = P304.ins[0]
        denaturant.imol['Octane'] = 0.021*pure_ethanol.F_mass/114.232
    
    bst.main_flowsheet.diagram('thorough', format='png') # Flowsheet up until now
    次に、機器ユニットのデータを使ってユーティリティの要求を解くために設備側(ファシリティ)を設定します。蒸気、電気をボイラー・ターボ発電機で供給し、冷却水塔チルド水生成装置で冷却、プロセス水供給センター、熱交換ネットワークを使用しています。
  5. 設備(ファシリティ)側の設定
  6. s = bst.main_flowsheet.stream
    BT = bst.BoilerTurbogenerator('BT',
        (U202-0, '', 'boiler_makeup_water', 'natural_gas', '', ''),
        boiler_efficiency=0.80, turbogenerator_efficiency=0.85
    )
    CT = bst.CoolingTower('CT')
    makeup_water = bst.Stream('makeup_water', price=0.000254)
    CWP = bst.ChilledWaterPackage('CWP')
    PWC = bst.ProcessWaterCenter('PWC',
        ins=(bst.Stream(), makeup_water, P303-0), outs=(),
        makeup_water_streams=(s.cooling_tower_makeup_water, s.boiler_makeup_water),
        process_water_streams=(imbibition_water, rvf_wash_water, stripping_water,
                               s.cooling_tower_makeup_water, s.boiler_makeup_water)
    )
    HXN = bst.HeatExchangerNetwork('HXN', units=[F301, D303.condenser])
    システムとして生成し、シミュレーションを実行します。最後のsugarcane_sys.print()はシミュレーションの実行順序を表示できます。
  7. システムの生成とシミュレーションの実行
  8. sugarcane_sys = bst.main_flowsheet.create_system('sugarcane_sys')
    sugarcane_sys.simulate()
    sugarcane_sys.diagram('cluster', format='png') # Red streams are recycles (i.e. tear streams)
    sugarcane_sys.print() # Show system simulation order
    System('sugarcane_sys',
        [U101,
         U102,
         U103,
         T201,
         System('SYS1',
            [U201,
             S201,
             M201],
            recycle=M201-0),
         T202,
         H201,
         T203,
         P201,
         T204,
         T205,
         P202,
         System('SYS2',
            [M202,
             H202,
             T206,
             C201,
             C202,
             P203],
            recycle=P203-0),
         S202,
         S301,
         F301,
         P306,
         M301,
         H301,
         T305,
         R301,
         T301,
         C301,
         D301,
         M302,
         P301,
         System('SYS3',
            [H302,
             D302,
             P302],
            recycle=P302-0),
         System('SYS4',
            [M303,
             D303,
             H303,
             U301],
            recycle=U301-0),
         H304,
         T302,
         P304,
         T303,
         P305,
         T304,
         P303,
         M305,
         U202],
        facilities=[HXN,
         CWP,
         CT,
         BT,
         PWC])

    特性評価

    バイオ・エタノール製造設備のどの工程が設備費や運転コストに影響を及ぼしているか、を分かりやすくするためをいくつかのエリア(例えば材料受入れ搾汁エタノール製造)に分けて計算結果をまとめます。BioSteamの製造システムのエリア分けにはUnitGroupオブジェクトを使います。
  9. グループ分けと集計結果
  10. UnitGroup = bst.process_tools.UnitGroup
    unit_groups = UnitGroup.group_by_area(sugarcane_sys.units)
    # The code above returns the following by grouping units by "area" number
    # unit_groups = [
    #     UnitGroup('Feedstock handling', [U101, U102, U103]),
    #     UnitGroup('Juicing',
    #         [T201, U201, S201, M201, T202, H201, T203, P201, T204,
    #         T205, P202, M202, H202, T206, C201, C202, P203, S202]
    #     ),
    #     UnitGroup('Ethanol production',
    #         [S301, F301, P306, M301, H301, T305, R301, T301, C301,
    #          M302, P301, H302, D302, P302, M303, D303, H303, U301,
    #          H304, T302, P304, T303, P305, T304, D301, P303, M305]
    #     ),
    #     UnitGroup('Facilities', sugarcane_sys.facilities)
    # ]
    
    # Add key metrics and use short names (like elec. cons. for electricty consumption).
    for group in unit_groups: group.autofill_metrics(shorthand=True)
    
    area_names = {'0': 'Facilities',
                  '100': 'Feedstock handling',
                  '200': 'Juicing',
                  '300': 'Ethanol production'}
    for i in unit_groups: i.name = area_names[i.name]
    UnitGroup.df_from_groups(unit_groups)
    Inst. eq. cost [MM$] Cooling [GJ/hr] Heating [GJ/hr] Elec. cons. [MW] Mat. cost [USD/hr]
    Facilities 84.7 74.6 0 5.86 16.9
    Feedstock handling 5.39 0 0 2.23 1.28e+04
    Juicing 16.7 0 553 2.67 584
    Ethanol production 18.2 236 228 0.695 402
  11. グループ分けと集計結果
  12. さらに見やすくするために BioSTEAM には UnitGroup オブジェクトを中心としたプロット(描画)メソッドが用意されています。
    # For convinience, BioSTEAM has plotting methods centered on UnitGroup objects
    bst.plots.plot_unit_groups(unit_groups, fraction=True)
    (<Figure size 1920x1440 with 3 Axes>,
     array([<Axes: ylabel='Cost and Utility Breakdown [%]'>], dtype=object))
    表とプロットから、エタノール製造工程が全体の冷却および加熱需要の大部分に寄与していることが分かります。これは、発酵槽および蒸留塔の運転に必要な冷却と加熱によるものと考えられます。

    技術経済性評価(TEA:Techno-Economic Analysis)

    正味現在価値(NPV:Net Present Value)がゼロとなる内部収益率 (IRR:Internal Rate of Return) を算出するために、Huangらの文献[1]にあるデフォルトのTEA設定(技術経済性評価の初期設定)を使います。
  13. IRRの計算例
  14. import biorefineries.sugarcane as sc
    sugarcane_tea = sc.create_tea(sugarcane_sys)
    sugarcane_tea.solve_IRR()
    0.027991701191763016

    (※オリジナルのページとは違う結果になりました。)

    逆に、指定したIRRからNPVゼロでブレークイーブンになるエタノールの最低販売価格も計算できます。

  15. エタノールの最低販売価格 [USD/ガロン]
  16. sugarcane_tea.IRR = 0.10
    ethanol_price = sugarcane_tea.solve_price(ethanol) # USD/kg
    ethanol_price * 2.98668849 # to USD/gal
    2.723203081233419

    (※オリジナルのページとは違う結果になりました。)

    NPVゼロでブレークイーブンになる最高原料価格も計算できます。

  17. 最高原料価格 [USD/kg]
  18. sugarcane_tea.solve_price(sugarcane)
    0.02597031951600984

    (※オリジナルのページとは違う結果になりました。)

    この結果は、酵母の生産に関するいくつかの単純化のため、Huangらの文献[1]と若干異なる可能性があります。より厳密なモデルは biorefineries.sugarcane で利用可能です。また、さらにTEAの詳細な説明は、TEAの章をご確認ください。

    感度分析

    原材料価格、プロセスの処理能力、発酵効率などの感度分析を検討してみます。入力条件と出力指標はModelオブジェクトを使い、単一条件での感度分析を実施します。
  19. 感度分析
  20. model = bst.Model(sugarcane_sys)
    
    @model.parameter(
        element='feedstock', units='USD/kg', # Metadata for pretty tables/plots
        baseline=sugarcane.price, # Baseline price
        bounds=(sugarcane.price * 0.9, sugarcane.price * 1.1) # Min/max price
    )
    def set_feed_price(feedstock_price):
        sugarcane.price = feedstock_price
    
    @model.parameter(
        element='feedstock', units='kg/h',
        baseline=sugarcane.F_mass,
        bounds=(sugarcane.F_mass * 0.9, sugarcane.F_mass * 1.1)
    )
    def set_crushing_capacity(capacity):
        sugarcane.F_mass = capacity
    
    @model.parameter(
        element='fermentation', units='%',
        baseline=90, bounds=(85, 95)
    )
    def set_fermentation_efficiency(efficiency):
        R301.efficiency = efficiency / 100
    
    @model.metric(units='%')
    def IRR():
        return 100 * sugarcane_tea.solve_IRR()
    
    baseline, lower, upper = model.single_point_sensitivity()
    metric_index = IRR.index
    index = [i.describe(distribution=False) # Instead of displaying distribution, it displays lower, baseline, and upper values
             for i in model.parameters]
    bst.plots.plot_single_point_sensitivity(baseline[metric_index],
                                            lower[metric_index],
                                            upper[metric_index],
                                            name='IRR [%]',
                                            index=index)
    (<Figure size 1920x1440 with 3 Axes>, <Axes: xlabel='IRR [%]'>)
    (※感度は近い結果になりましたが、横軸がかなりオリジナルのページとは違う結果になりました。) この図から、指定された最小値/最大値の範囲において原料価格が最も経済成立性にインパクトがあることが分かります。さらに詳しい説明やサンプルは不確実性と感度の章をご確認ください。

参考文献

このブログの人気の投稿

さあ、始めよう!

蒸留塔

機器ユニットの計算結果