Keyword: 水資源管理, 最適化, スケジューリング, 線形計画法, LP
水資源管理分野の最適化問題:貯水池の放流スケジュール最適化
問題の概要

水力発電用の貯水池では、発電収益を最大化しつつ、ダムの安全性と下流域の水需要を満たすために適正な水位を維持することが求められます。この問題では、直近の2週間という短期的な期間において、適正水位範囲の維持、絶対的な容量制約の遵守、そして水力発電収益の最大化を両立する最適な放流スケジュールを求めることを目的としています。
主要な制約条件としては、貯水池の水位が物理的な最大容量を超えないこと、最小容量を下回らないこと、そして適正水位範囲内に収まることが挙げられます。また、放流量についても、ダムの放流設備の能力による上限と下限の制約を考慮する必要があります。
貯水池の放流スケジュール最適化問題(具体例)
この問題では、水力発電用貯水池の今後14日間の放流スケジュールを最適化します。目的は、貯水池の水位を適正範囲内に維持しつつ、水力発電収益を最大化することです。最適化にあたっては、予測流入量データを基に、放流量を適切に調整します。制約条件として、貯水池の水位が物理的な最大容量と最小容量の範囲内に収まること、放流量がダムの放流設備の能力の上限と下限の範囲内であること、そして貯水池の水位が適正範囲から逸脱した場合にペナルティが発生することを考慮します。最適な放流計画を立てることで、ダムの安全性と水力発電による収益の最大化を両立することを目指します。
以下の表には、次の14日間の予測流入量が記載されており、これを基に放流スケジュールを最適化します。
日付 | 予測流入量(m³/日) |
---|---|
1日目 | 3,000 |
2日目 | 4,000 |
3日目 | 15,000 |
4日目 | 50,000 |
5日目 | 30,000 |
6日目 | 12,000 |
7日目 | 8,000 |
8日目 | 5,000 |
9日目 | 4,000 |
10日目 | 3,000 |
11日目 | 3,000 |
12日目 | 4,000 |
13日目 | 3,000 |
14日目 | 5,000 |
問題の定式化
パラメータ
パラメータ | 説明 | 値 |
---|---|---|
\(T\) | 計画期間(日数) | 14 |
\(I_t\) | \(t\)日目の予測流入量(m³/日) | 上記の表を参照 |
\(V_{max}\) | ダムの物理的な最大容量(m³) | 2,000,000 |
\(V_{min}\) | ダムの物理的な最小容量(m³) | 200,000 |
\(V_{max\_opt}\) | 適正範囲の上限(m³) | 1,820,000 |
\(V_{min\_opt}\) | 適正範囲の下限(m³) | 1,780,000 |
\(V_{start}\) | 初期貯水量(m³) | 1,800,000 |
\(R_{max}\) | 1日あたりの最大放流量(m³/日) | 15,000 |
\(R_{min}\) | 1日あたりの最小放流量(m³/日) | 1,000 |
\(P\) | 1m³あたりの発電収益(円/m³) | 10 |
\(C\) | 貯水量が適正範囲を外れた場合のペナルティ(円/m³) | 12 |
決定変数
変数 | 説明 | 範囲 |
---|---|---|
\(R_t\) | \(t\)日目の放流量(m³/日) | \(R_{min} \leq R_t \leq R_{max}\) |
\(V_t\) | \(t\)日目終了時点の貯水量(m³) | \(V_{min} \leq V_t \leq V_{max}\) |
\(O_t\) | \(t\)日目の貯水量が適正範囲を外れた量(m³) | \(O_t \geq 0\) |
目的関数
目的 | 式 |
---|---|
発電収益を最大化しつつ、貯水量が適正範囲を外れた際のペナルティを最小化 | \(\max \sum_{t=1}^{T} P \cdot R_t - \sum_{t=1}^{T} C \cdot O_t\) |
制約条件
制約 | 式 | 説明 |
---|---|---|
貯水量の物理的範囲 | \(V_{min} \leq V_t \leq V_{max}, \forall t\) | 貯水量はダムの物理的な最小容量以上、最大容量以下でなければならない |
放流量の範囲 | \(R_{min} \leq R_t \leq R_{max}, \forall t\) | 放流量は最小値以上、最大値以下でなければならない |
初期貯水量 | \(V_0 = V_{start}\) | 初日の貯水量は初期貯水量に等しい |
貯水量の遷移 | \(V_t = V_{t-1} + I_t - R_t, \forall t\) | 貯水量は前日の貯水量に流入量を加え、放流量を引いたものになる |
貯水量の適正範囲上限逸脱 | \(O_t \geq V_t - V_{max\_opt}, \forall t\) | 貯水量が適正範囲の上限を超えた分だけ逸脱量が発生する |
貯水量の適正範囲下限逸脱 | \(O_t \geq V_{min\_opt} - V_t, \forall t\) | 貯水量が適正範囲の下限を下回った分だけ逸脱量が発生する |
非負制約 | \(O_t \geq 0, \forall t\) | 逸脱量は非負でなければならない |
コード例
以下に、この線形計画問題を nAG Library for Python の スパース線形計画問題に適したソルバー関数 handle_solve_lp_ipm を用いて解くコード例を示します。
import numpy as np
from naginterfaces.library import opt
# パラメータ
= np.array([3000, 4000, 15000, 50000, 30000, 12000, 8000, 5000, 4000, 3000, 3000, 4000, 3000, 5000]) # 各日の予測流入量(m³/日)
I = 15000 # 1日あたりの最大放流量(m³/日)
R_max = 1000 # 1日あたりの最小放流量(m³/日)
R_min = 2000000 # 貯水池の最大容量(m³)
V_max = 200000 # 貯水池の最小容量(m³)
V_min = 1820000 # 貯水池の最適容量上限(m³)
V_max_opt = 1780000 # 貯水池の最適容量下限(m³)
V_min_opt = 10 # 1m³あたりの発電収益(円/m³)
P = 12 # 貯水量が最適範囲を外れた場合のペナルティ(円/m³)
C
# 初期貯水量
= (V_min_opt+V_max_opt)/2
V_start
# オプティマイザーの初期化
= len(I) # 計画期間(日数)
T = 3 * T
nvar = opt.handle_init(nvar)
handle
# 目的関数の係数(発電収益の最大化とペナルティの最小化)
= [P] * T + [0] * T + [-C] * T # R, V, O の順番、Vは目的関数には含まれないのでゼロ
cvec
opt.handle_set_linobj(handle, cvec)
# 変数の上下限
= np.zeros(nvar) # 下限の初期化
bl = np.zeros(nvar) # 上限の初期化
bu = R_min # R_t の下限
bl[:T] = R_max # R_t の上限
bu[:T] 2*T] = V_min # V_t の下限
bl[T:2*T] = V_max # V_t の上限
bu[T:2*T:3*T] = 0 # O_t の下限
bl[2*T:3*T] = np.inf # O_t の上限
bu[
opt.handle_set_simplebounds(handle, bl, bu)
# 初日の貯水量遷移制約
= [V_start + I[0]]
bl_1st_day = [V_start + I[0]]
bu_1st_day = [1, 1]
irowb_1st_day = [1, T+1] # R1, V1
icolb_1st_day = [1, 1] # R1 + V1
coeff_1st_day
opt.handle_set_linconstr(handle, bl_1st_day, bu_1st_day, irowb_1st_day, icolb_1st_day, coeff_1st_day)
# 2日目以降の貯水量遷移制約
=I[1:]
bl_2nd_day_on =I[1:]
bu_2nd_day_on = []
irowb_2nd_day_on = []
icolb_2nd_day_on = [1, -1, 1] * (T-1)
coeff_2nd_day_on for t in range(1, T):
-1] = I[t]
bl_2nd_day_on[t-1] = I[t]
bu_2nd_day_on[t
irowb_2nd_day_on.extend([t,t,t])+1,T+t,T+t+1]) # Rt, Vt-1, Vt
icolb_2nd_day_on.extend([t
opt.handle_set_linconstr(handle, bl_2nd_day_on, bu_2nd_day_on, irowb_2nd_day_on, icolb_2nd_day_on, coeff_2nd_day_on)
# 貯水適正量上限逸脱
= np.full(T, -V_max_opt)
bl_excess_upper = np.full(T, np.inf)
bu_excess_upper = []
irowb_excess_upper = []
icolb_excess_upper = [1, -1] * T # O_t - V_t
coeff_excess_upper for t in range(T):
+1, t+1])
irowb_excess_upper.extend([t2*T+t+1, T+t+1]) # O_t, V_t
icolb_excess_upper.extend([
opt.handle_set_linconstr(handle, bl_excess_upper, bu_excess_upper, irowb_excess_upper, icolb_excess_upper, coeff_excess_upper)
# 貯水適正量下限逸脱
= np.full(T, V_min_opt)
bl_excess_lower = np.full(T, np.inf)
bu_excess_lower = []
irowb_excess_lower = []
icolb_excess_lower = [1, 1] * T
coeff_excess_lower for t in range(T):
+1, t+1])
irowb_excess_lower.extend([t2*T+t+1, T+t+1]) # O_t, V_t
icolb_excess_lower.extend([
opt.handle_set_linconstr(handle, bl_excess_lower, bu_excess_lower, irowb_excess_lower, icolb_excess_lower, coeff_excess_lower)
# ソルバーオプションの設定
'Print Level = 0')
opt.handle_opt_set(handle, 'Task = Maximize')
opt.handle_opt_set(handle,
# 初期値
= [R_min]*T + [V_min]*T + [0]*T
x_init
# ソルバーの実行
= opt.handle_solve_lp_ipm(handle,x_init)
optimal_solution, _, result_info, _
# 結果を表示
= optimal_solution[:T]
R_opt = optimal_solution[T:2*T]
V_opt = [max(0, v - V_max_opt) for v in V_opt] # 上限超過量
O_opt_upper = [max(0, V_min_opt - v) for v in V_opt] # 下限未満量
O_opt_lower
# 目的関数の値を取得
= result_info[1]
obj_value
# 表示
print("\n最適解:")
print("日\t流入量\t\t放流量\t\t貯水量\t\t上限超過量\t下限未満量")
print(f"0\t-\t\t-\t\t{V_start:10.0f}\t-\t\t-")
for t in range(T):
print(f"{t+1}\t{I[t]:<10}\t{R_opt[t]:10.0f}\t{V_opt[t]:10.0f}\t{O_opt_upper[t]:10.0f}\t{O_opt_lower[t]:10.0f}")
# 発電収益とペナルティー表示
= sum(P * R_opt[t] for t in range(T))
revenue = sum(C * O_opt_upper[t] for t in range(T)) # 上限超過ペナルティ
penalty_upper = sum(C * O_opt_lower[t] for t in range(T)) # 下限未満ペナルティ
penalty_lower = penalty_upper + penalty_lower # 総ペナルティ
penalty print(f"-------------------------------")
print(f"総発電収益: {revenue:.0f}円")
print(f"上限超過ペナルティ: {penalty_upper:.0f}円")
print(f"下限未満ペナルティ: {penalty_lower:.0f}円")
print(f"総利益: {obj_value:.0f}円")
結果例
上記のコードを実行すると、以下のような出力が得られます。
最適解:
日 流入量 放流量 貯水量 上限超過量 下限未満量
0 - - 1800000 - -
1 3000 14927 1788073 0 0
2 4000 14874 1777199 0 2801
3 15000 15000 1777199 0 2801
4 50000 15000 1812199 0 0
5 30000 15000 1827199 7199 0
6 12000 15000 1824199 4199 0
7 8000 13125 1819074 0 0
8 5000 10301 1813773 0 0
9 4000 9005 1808768 0 0
10 3000 8220 1803548 0 0
11 3000 7910 1798637 0 0
12 4000 8282 1794355 0 0
13 3000 9654 1787701 0 0
14 5000 12701 1780000 0 0
-------------------------------
総発電収益: 1690000円
上限超過ペナルティ: 136766円
下限未満ペナルティ: 67234円
総利益: 1486000円
この結果から、最適放流スケジュールに従うことで、14日間の発電収益が1,690,000円、上限超過ペナルティが136,766円、下限を下回ったことによるペナルティが67,234円で、総利益が1,486,000円となる事がわかります。また、適正水位範囲からの逸脱も少ないことがわかります。具体的には、上限を超えたのは5日目と6日目で、それぞれ7,199m³と4,199m³の逸脱、下限未満の逸脱は2日目と3日目にそれぞれ2,801m³が発生しています。
まとめ
ここでは、水力発電用貯水池の短期的な放流スケジュールを最適化することで、発電収益の最大化と適正水位の維持を両立するアプローチを示しました。線形計画法を用いることで、現実的な制約条件の下で最適解を効率的に求めることができました。これに環境流量の確保といった生態系保全の制約や、発電収益だけでなく下流域の水需要の満足度も考慮した目的関数を含めるなど、より現実的な応用も可能です。