Unit クラスの継承(1)
- Visual Studio Code バージョン: 1.104.2
- 拡張機能:Jupyter バージョン 2025.8.0
- Python 3.12.10
- biosteam 2.52.13
- graphviz-14.0.2
Unit クラスの継承
共通の設定
サブクラスの例
シミュレーション テスト
Graphvizで作図
装置のコスト計算
付随設備
参考文献
<< 本文 >>Unit クラスの継承
共通の設定
Unit クラスを継承する各機器のモデル(サブクラス)は、以下のクラス変数を持ち、特に指定のない時のデフォルトの値が決められています。
- _F_BM_default : [str, float]の組み合わせの辞書型配列で、設置に関するコストを見積るための係数のデフォルト値。
- _units :[str, float]の組み合わせの辞書型配列で、計算結果である辞書型配列 design_results の値の単位。
- _N_ins =1:[int]流入ストリームの数。デフォルトは1。
- _N_outs =1:[int]流出ストリームの数。デフォルトは1。
- _ins_size_is_fixed =True:[bool]流入ストリームの数が固定値かどうか。デフォルトはTrue。
- _out_size_is_fixed =True:[bool]流出ストリームの数が固定値かどうか。デフォルトはTrue。
- auxiliary_unit_names =():tuple[str]補助装置の名前。
- _graphics =():[biosteam Graphics]機器を表す図のグラフィック オブジェクト。デフォルトは四角。
- _default_equipment_lifetime =():[int] または [str, int]の組み合わせの辞書型配列 装置の寿命のデフォルト値。生産事業の存続期間。
- _line =():[str] 機器を図にするときのラベル。デフォルトはその機器のクラス名。
各機器の熱や物質収支、コストを計算するための抽象メソッドが設定されています。
- _run() システムの収束計算時に呼び出され、流出ストリームを決定します。
- _design() :システムの収束後にに呼び出され、設計要求を決定します。
- _cost() :_design()実行後に呼び出され、コストを決定します。
以下の抽象メソッドは、それぞれのインスタンス変数によって設定されます。
- ins [Stream] 流入ストリーム。
- outs:[Stream] 流出ストリーム。
- power_utilities:[Stream] 電力消費量。
- heat_utilities:list[Stream] 加熱、冷却要求。
- baseline_purchase_costs : dict [str, float] 設計、圧力、材料の係数を考慮する前の購入価格(ベースライン購入価格)の一覧。
- parallel: dict [str, int] 並列で配置するパーツの名前と個数。並列で設置されるパーツがない時は{}。
- F_BM : dict [str, float] 設備コスト計算時の設置に関する係数。
- F_D : dict [str, float] 設備コスト計算時の設計の係数。
- F_P : dict [str, float] 設備コスト計算時の圧力の係数。
- F_M : dict [str, float] 設備コスト計算時の材料の係数。
- equipment_lifetime:[dict] 各パーツの寿命。
- thermo:[Thermo] 機器で使用される成分の熱力学的特性
サブクラスの例
次の例では、新しい Boiler クラスを作成することで Unit クラスから継承する変数をどう設定するか示しています。
- 新しい Boiler クラスを作成
import biosteam as bst
from math import ceil
bst.nbtutorial()
class Boiler(bst.Unit):
"""
供給ストリームの一部を沸騰させるボイラー オブジェクトを生成
Parameters
----------
ins :
供給ストリーム
outs :
* [0] 蒸気
* [1] 液体
V : float
モル蒸気分率
P : float
操作圧力 [Pa].
"""
# ここでは`ID` や `thermo`パラメータを設定していませんが
# BioSTEAMのほとんどのサブクラスはこのように記載されていて、問題ありません。
# 全ての機器ユニットはインデックス付きのリスト化された流入/流出ストリームを指定しますが、
# 流入ストリームもしくは流出ストリームが1つの場合は、リスト化する必要はありません。
# `ins` や `outs`を書く必要もありません。
# BioSTEAM はしい型を自動的に追加します。
# 機器に対する追加の引数も列挙する必要があります(例:V や P)。
_N_ins = 1 # 流入ストリームの数
_N_outs = 2 # 流出ストリームの数
_units = {'Area': 'm^2'}
def _init(self, V, P):
# _init メソッドで 新規作成時に指定された引数が追加されます。
self.V = V #: モル蒸気分率
self.P = P #: 操作圧力 [Pa].
def _run(self):
# 流入ストリームの数が1つのときは、self.ins[0] と等価。
feed = self.feed
vap, liq = self.outs
# 気液平衡計算を実施
stream = feed.copy()
stream.vle(V=self.V, P=self.P)
# 流出ストリームを更新します。
vap.copy_like(stream['g'])
liq.copy_like(stream['l'])
def _design(self):
# 加熱要求を出入口のエンタルピー差から計算します。
T_operation = self.outs[0].T
duty = self.H_out - self.H_in
if duty < 0: raise RuntimeError(f'{repr(self)} is cooling.')
heat_utility = self.add_heat_utility(duty, T_operation) # 新しい加熱要求を self.heat_utilities に設定
# 熱交換器の入口温度の設定
T_utility = heat_utility.inlet_utility_stream.T
# 熱交換器の温度差の設定
dT = T_utility - T_operation
# 熱伝達係数 kJ/(hr*m2*K)
U = 8176.699
# 必要な熱交換面積 (m^2) = 要求熱量 / (熱伝達係数 * 出入口温度差)
A = duty /(U * dT)
# 1つの熱交換器の熱交換面積の上限
A_max = 743.224
# 必要な熱交換機の数 = A / A_max
N = ceil(A / A_max)
# 設計要求に計算結果を保存
self.design_results['Area'] = A / N
# ボイラー全体の設備費は、1つのボイラーの費用にボイラーの台数を掛けたものとします。
self.parallel['Boiler'] = N
def _cost(self):
A = self.design_results['Area']
# 長管式縦型ボイラーのコストは以下を参照しています。
# "Product process and design". Warren et. al. (2016) Table 22.32, pg 592
purchase_cost = bst.settings.CEPCI * 3.086 * A **0.55
# ベースライン購入価格として保存
self.baseline_purchase_costs['Boiler'] = purchase_cost # 材質の係数は未考慮
# 設計、圧力、材質による係数は1とします。
self.F_D['Boiler'] = self.F_P['Boiler'] = self.F_M['Boiler'] = 1.
# ボイラーの設置に関する係数を使用します。
self.F_BM['Boiler'] = 2.45
シミュレーション テスト
- オブジェクトの生成
- シミュレーションの実行
- 結果の確認 ユーティリティの要求は`design_results`に辞書型配列として保存されます。購入価格は自動的に結果一覧に追加されます。
- 供給量の変更
import biosteam as bst
bst.settings.set_thermo(['Water'])
water = bst.Stream('water', Water=300)
B1 = Boiler('B1', ins=water, outs=('gas', 'liq'),
V=0.5, P=101325)
B1.diagram(format='png')
B1.show()
Boiler: B1
ins...
[0] water
phase: 'l', T: 298.15 K, P: 101325 Pa
flow (kmol/hr): Water 300
outs...
[0] gas
phase: 'l', T: 298.15 K, P: 101325 Pa
flow: 0
[1] liq
phase: 'l', T: 298.15 K, P: 101325 Pa
flow: 0
B1.simulate()
B1.show()
Boiler: B1
ins...
[0] water
phase: 'l', T: 298.15 K, P: 101325 Pa
flow (kmol/hr): Water 300
outs...
[0] gas
phase: 'g', T: 373.12 K, P: 101325 Pa
flow (kmol/hr): Water 150
[1] liq
phase: 'l', T: 373.12 K, P: 101325 Pa
flow (kmol/hr): Water 150
B1.results()
| Boiler | Units | B1 | |
|---|---|---|---|
| Low pressure steam | Duty | kJ/hr | 8.21e+06 |
| Flow | kmol/hr | 212 | |
| Cost | USD/hr | 50.4 | |
| Design | Area | m^2 | 24.4 |
| Purchase cost | Boiler | USD | 1.02e+04 |
| Total purchase cost | USD | 1.02e+04 | |
| Utility cost | USD | 50.4 |
プラントの規模が大きくなった時には、ボイラーの個数は辞書型配列の`parallel`を使って自動的に個数が増えます。
B1.feed.scale(100) # 供給流量を 100倍にします。
B1.simulate()
B1.results()
| Boiler | Units | B1 | |
|---|---|---|---|
| Low pressure steam | Duty | kJ/hr | 8.21e+08 |
| Flow | kmol/hr | 2.12e+04 | |
| Cost | USD/hr | 5.04e+03 | |
| Design | Area | m^2 | 610 |
| Purchase cost | Boiler (x4) | USD | 2.38e+05 |
| Total purchase cost | USD | 2.38e+05 | |
| Utility cost | USD | 5.04e+03 |
B1.parallel
{'Boiler': 4}
となります。
Graphvizで作図
Graphvizで作られた図は_graphics
にグラフィック オブジェクトとして保存されています。
グラフィック オブジェクトはそれぞれの機器 サブクラスに一つ作成されます。
- 作図情報
エッジ イン
エッジ アウト
ノード
デフォルトの設定での図
- 設定の変更
設定の動的な変更
graphics = Boiler._graphics
edge_in = graphics.edge_in
edge_out = graphics.edge_out
node = graphics.node
流入ストリーム一つにつき、一つのエッジ イン
が生成されます。例えば、B1.ins[0]にはedge_in[0]が対応して生成されます。
edge_in
[{'headport': 'c'}]
流出ストリーム一つにつき、一つのエッジ アウト
が生成されます。例えば、B1.outs[0]にはedge_outs[0]が対応して生成されます。
edge_out
[{'tailport': 'c'}, {'tailport': 'c'}]
ノード
が機器に対応します。
node
{'shape': 'box',
'style': 'filled',
'gradientangle': '0',
'width': '0.6',
'height': '0.6',
'orientation': '0.0',
'peripheries': '1',
'margin': 'default',
'fontname': 'Arial'}
B1.diagram(format='png')
図の設定はユーザーの好みに応じて変更できます。
edge_out[0]['tailport'] = 'n'
edge_out[1]['tailport'] = 's'
node['width'] = '1'
node['height'] = '1.2'
B1.diagram(format='png')
図の設定はtailor_node_to_unit
の設定をすることで動的に変更できます。以下の例では、ストリームの流量がゼロの時に機器内に-empty-
という文字が追加されるようにしています。
def tailor_node_to_unit(node, unit):
if unit.feed.isempty(): node['label'] += '\n-empty-'
graphics.tailor_node_to_unit = tailor_node_to_unit
B1.ins[0].empty()
B1.diagram(format='png')
注意:tailor_node_to_unit
関数のサンプル実装は推奨されません。図はシンプルに保つのが最適です。



