energy-storage-optimization について
蓄電システムの計画を支援するスキルです。容量や運用方法を検討するチーム向けに、電池の運用、系統サービス、再生可能エネルギー連携、劣化、経済性を扱います。
主な機能
- 01バッテリー容量と運用の最適化
- 02電力価格に基づく裁定取引
- 03ピークカットとデマンド料金削減
- 04再生可能エネルギーの変動緩和と系統サービス
- 05劣化と寿命のモデリング
活用シーン
- 01負荷や太陽光発電のデータに基づく容量設計
- 02電力価格に応じたバッテリー運用の計画
- 03デマンド料金削減効果の評価
- 04蓄電プロジェクトの収益源の比較
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ kishorkukreja/ awesome-supply-chain --skill energy-storage-optimizationインストール後、Claude Code で/energy-storage-optimizationを入力して読み込まれたか確認します。
詳細
エネルギー貯蔵の最適化
エネルギー貯蔵の最適化とバッテリー管理の専門家として、エネルギー貯蔵システム(バッテリー、揚水発電、圧縮空気など)の導入、容量設計、運用、経済性を最適化し、系統サービス、再生可能エネルギーの統合、コスト削減を支援します。
初期評価
エネルギー貯蔵を最適化する前に、以下を把握してください。
-
貯蔵の用途
- 主な用途は何ですか?(裁定取引、ピークカット、再生可能エネルギー、バックアップ)
- 系統サービスは何ですか?(周波数調整、電圧サポート、ブラックスタート)
- 顧客の種類は何ですか?(電力会社、商業施設、工場、住宅)
- 設置場所と系統への接続状況はどうなっていますか?
-
貯蔵技術
- 技術の種類は何ですか?(リチウムイオン、フローバッテリー、揚水発電、CAES)
- 出力容量はどのくらいですか(MW または kW)?
- エネルギー容量はどのくらいですか(MWh または kWh)?
- 往復効率はどのくらいですか?
- サイクル寿命と劣化の状況はどうなっていますか?
-
システムパラメーター
- 負荷プロファイルとパターンはどうなっていますか?
- 再生可能エネルギーの発電量はどのくらいですか(該当する場合)?
- 電力料金体系はどうなっていますか?(時間帯別料金、リアルタイム料金、デマンド料金)
- 系統の制約や要件はありますか?
-
目的と制約
- 主な目標は何ですか?(コスト削減、信頼性、再生可能エネルギーの統合)
- 予算と経済性はどうなっていますか?(設備投資額、投資回収期間)
- 物理的な制約はありますか?(設置スペース、温度、安全性)
- 規制上の要件やインセンティブはありますか?
エネルギー貯蔵のフレームワーク
貯蔵技術
電気化学(バッテリー):
- リチウムイオン:高効率(90-95%)、高速応答、コスト低下が進行中
- フローバッテリー:長時間放電に対応、出力とエネルギー容量を独立して拡張可能
- 鉛蓄電池:成熟した技術、低コスト、サイクル寿命に制限あり
- ナトリウム硫黄電池:高エネルギー密度、高温で動作
機械式:
- 揚水発電:最大級の容量、効率70-85%、設置場所に依存
- 圧縮空気(CAES):大規模に対応、地質条件を利用した貯蔵が必要
- フライホイール:高出力、短時間放電、長いサイクル寿命
熱:
- 溶融塩:CSPとの統合、6-15時間の放電時間
- 氷蓄熱:冷房用途、負荷シフト
水素:
- Power-to-Gas:季節間貯蔵、往復効率30-40%
- 燃料電池:バックアップ電源、長時間放電に対応
エネルギー貯蔵の容量最適化
最適なバッテリー容量の設計
import numpy as np
import pandas as pd
from pulp import *
def optimize_battery_sizing(load_profile, solar_generation, electricity_prices,
max_power_mw=10, max_energy_mwh=40):
"""
Determine optimal battery size for load shifting and solar firming
Parameters:
- load_profile: array of load (MW) by hour
- solar_generation: array of solar output (MW) by hour
- electricity_prices: array of prices ($/MWh) by hour
- max_power_mw: maximum power capacity to consider
- max_energy_mwh: maximum energy capacity to consider
"""
# Evaluate different battery sizes
power_sizes = np.linspace(1, max_power_mw, 10)
energy_sizes = np.linspace(2, max_energy_mwh, 10)
results = []
for power_mw in power_sizes:
for energy_mwh in energy_sizes:
# Energy/Power ratio (hours)
duration = energy_mwh / power_mw
if duration < 0.5 or duration > 10:
continue # Skip infeasible ratios
# Calculate annual value
annual_value = optimize_battery_dispatch(
load_profile,
solar_generation,
electricity_prices,
power_mw,
energy_mwh
)['annual_value']
# Calculate cost
power_cost = power_mw * 150000 # $/MW
energy_cost = energy_mwh * 300000 # $/MWh
total_capex = power_cost + energy_cost
# Calculate NPV over 15 years
discount_rate = 0.08
years = 15
npv = -total_capex + sum([annual_value / (1 + discount_rate)**y
for y in range(1, years + 1)])
results.append({
'power_mw': power_mw,
'energy_mwh': energy_mwh,
'duration_hours': duration,
'capex': total_capex,
'annual_value': annual_value,
'npv': npv,
'payback_years': total_capex / annual_value if annual_value > 0 else 999
})
# Find optimal size
results_df = pd.DataFrame(results)
optimal = results_df.loc[results_df['npv'].idxmax()]
return {
'optimal_size': {
'power_mw': optimal['power_mw'],
'energy_mwh': optimal['energy_mwh'],
'duration_hours': optimal['duration_hours']
},
'economics': {
'capex': optimal['capex'],
'annual_value': optimal['annual_value'],
'npv': optimal['npv'],
'payback_years': optimal['payback_years']
},
'all_results': results_df
}
def optimize_battery_dispatch(load, solar, prices, power_mw, energy_mwh,
efficiency=0.92):
"""
Optimize battery dispatch for given size
"""
T = len(load)
prob = LpProblem("Battery_Dispatch", LpMaximize)
# Variables
charge = [LpVariable(f"Charge_{t}", lowBound=0, upBound=power_mw)
for t in range(T)]
discharge = [LpVariable(f"Discharge_{t}", lowBound=0, upBound=power_mw)
for t in range(T)]
soc = [LpVariable(f"SOC_{t}", lowBound=0, upBound=energy_mwh)
for t in range(T)]
# Objective: maximize arbitrage value
prob += lpSum([(discharge[t] * prices[t] - charge[t] * prices[t])
for t in range(T)])
# Constraints
for t in range(T):
# SOC dynamics
if t == 0:
prob += soc[t] == energy_mwh * 0.5 + \
charge[t] * efficiency - discharge[t] / efficiency
else:
prob += soc[t] == soc[t-1] + \
charge[t] * efficiency - discharge[t] / efficiency
# Cyclic constraint (end at same SOC as start)
prob += soc[T-1] == energy_mwh * 0.5
# Solve
prob.solve(PULP_CBC_CMD(msg=0))
# Calculate annual value
daily_value = value(prob.objective)
annual_value = daily_value * 365
return {
'daily_value': daily_value,
'annual_value': annual_value,
'charge_schedule': [charge[t].varValue for t in range(T)],
'discharge_schedule': [discharge[t].varValue for t in range(T)],
'soc_profile': [soc[t].varValue for t in range(T)]
}
# Example usage
# Generate sample data
hours = 24
load = [100, 95, 90, 85, 80, 85, 100, 120, 140, 150, 155, 160,
158, 155, 150, 145, 150, 160, 155, 145, 130, 120, 110, 105]
solar = [0, 0, 0, 0, 0, 5, 30, 60, 90, 110, 120, 125,
120, 110, 95, 70, 40, 15, 0, 0, 0, 0, 0, 0]
prices = [40, 38, 35, 32, 30, 35, 50, 80, 120, 140, 150, 145,
140, 135, 130, 140, 160, 180, 170, 150, 120, 90, 60, 50]
result = optimize_battery_sizing(load, solar, prices,
max_power_mw=10, max_energy_mwh=40)
print(f"Optimal size: {result['optimal_size']['power_mw']:.1f} MW / "
f"{result['optimal_size']['energy_mwh']:.1f} MWh")
print(f"NPV: ${result['economics']['npv']:,.0f}")
print(f"Payback: {result['economics']['payback_years']:.1f} years")
エネルギー裁定取引の最適化
価格ベースの裁定取引
def optimize_energy_arbitrage(prices, power_capacity, energy_capacity,
efficiency=0.9, cycles_per_day=1):
"""
Optimize battery operation for energy arbitrage
Buy low, sell high based on electricity prices
"""
from pulp import *
T = len(prices)
prob = LpProblem("Arbitrage", LpMaximize)
# Variables
charge = [LpVariable(f"Charge_{t}", lowBound=0, upBound=power_capacity)
for t in range(T)]
discharge = [LpVariable(f"Discharge_{t}", lowBound=0, upBound=power_capacity)
for t in range(T)]
soc = [LpVariable(f"SOC_{t}", lowBound=0, upBound=energy_capacity)
for t in range(T)]
# Binary variables for charge/discharge (prevent simultaneous)
u_charge = [LpVariable(f"U_charge_{t}", cat='Binary') for t in range(T)]
u_discharge = [LpVariable(f"U_discharge_{t}", cat='Binary') for t in range(T)]
# Objective: maximize profit
revenue = lpSum([discharge[t] * prices[t] for t in range(T)])
cost = lpSum([charge[t] * prices[t] for t in range(T)])
prob += revenue - cost
# Constraints
for t in range(T):
# SOC dynamics
if t == 0:
initial_soc = energy_capacity * 0.5
prob += soc[t] == initial_soc + \
charge[t] * efficiency - discharge[t] / efficiency
else:
prob += soc[t] == soc[t-1] + \
charge[t] * efficiency - discharge[t] / efficiency
# Cannot charge and discharge simultaneously
prob += u_charge[t] + u_discharge[t] <= 1
# Link binary variables to continuous
prob += charge[t] <= power_capacity * u_charge[t]
prob += discharge[t] <= power_capacity * u_discharge[t]
# Cycle limit (battery degradation)
max_throughput = energy_capacity * cycles_per_day
prob += lpSum([discharge[t] for t in range(T)]) <= max_throughput
# Solve
prob.solve(PULP_CBC_CMD(msg=0))
return {
'profit': value(prob.objective),
'revenue': sum([discharge[t].varValue * prices[t] for t in range(T)]),
'cost': sum([charge[t].varValue * prices[t] for t in range(T)]),
'charge_schedule': [charge[t].varValue for t in range(T)],
'discharge_schedule': [discharge[t].varValue for t in range(T)],
'soc_profile': [soc[t].varValue for t in range(T)],
'cycles_used': sum([discharge[t].varValue for t in range(T)]) / energy_capacity
}
ピークカットとデマンド料金の削減
商業施設・工場のピークカット
def optimize_peak_shaving(load_forecast, demand_charge_rate,
energy_rate, power_capacity, energy_capacity):
"""
Optimize battery to reduce demand charges
Demand charges based on monthly peak demand
"""
from pulp import *
T = len(load_forecast)
prob = LpProblem("Peak_Shaving", LpMinimize)
# Variables
charge = [LpVariable(f"Charge_{t}", lowBound=0, upBound=power_capacity)
for t in range(T)]
discharge = [LpVariable(f"Discharge_{t}", lowBound=0, upBound=power_capacity)
for t in range(T)]
soc = [LpVariable(f"SOC_{t}", lowBound=0, upBound=energy_capacity)
for t in range(T)]
# Net load from grid
net_load = [LpVariable(f"NetLoad_{t}", lowBound=0) for t in range(T)]
# Peak demand (maximum of net load)
peak_demand = LpVariable("PeakDemand", lowBound=0)
# Objective: minimize total cost
demand_cost = demand_charge_rate * peak_demand
energy_cost = lpSum([net_load[t] * energy_rate[t] for t in range(T)])
prob += demand_cost + energy_cost
# Constraints
for t in range(T):
# Net load = original load - discharge + charge
prob += net_load[t] == load_forecast[t] - discharge[t] + charge[t]
# Peak constraint
prob += peak_demand >= net_load[t]
# SOC dynamics
if t == 0:
prob += soc[t] == energy_capacity * 0.5 + \
charge[t] * 0.92 - discharge[t] / 0.92
else:
prob += soc[t] == soc[t-1] + \
charge[t] * 0.92 - discharge[t] / 0.92
# Solve
prob.solve(PULP_CBC_CMD(msg=0))
original_peak = max(load_forecast)
new_peak = peak_demand.varValue
return {
'total_cost': value(prob.objective),
'peak_demand_kw': new_peak,
'original_peak_kw': original_peak,
'peak_reduction_kw': original_peak - new_peak,
'peak_reduction_pct': (original_peak - new_peak) / original_peak * 100,
'monthly_savings': demand_charge_rate * (original_peak - new_peak),
'charge_schedule': [charge[t].varValue for t in range(T)],
'discharge_schedule': [discharge[t].varValue for t in range(T)],
'net_load_profile': [net_load[t].varValue for t in range(T)]
}
# Example
load_forecast = [800, 750, 700, 680, 700, 750, 850, 920, 980, 1020, 1050, 1080,
1100, 1090, 1070, 1050, 1080, 1100, 1050, 980, 920, 880, 850, 820]
demand_charge = 15 # $/kW
energy_rate = [0.08] * 24 # $/kWh
result = optimize_peak_shaving(load_forecast, demand_charge, energy_rate,
power_capacity=200, energy_capacity=400)
print(f"Peak reduction: {result['peak_reduction_kw']:.0f} kW "
f"({result['peak_reduction_pct']:.1f}%)")
print(f"Monthly savings: ${result['monthly_savings']:,.0f}")
再生可能エネルギーの出力安定化
太陽光発電+貯蔵の最適化
def optimize_solar_plus_storage(solar_generation, load, power_capacity,
energy_capacity, tariff_structure):
"""
Optimize storage with solar to maximize self-consumption
and minimize grid imports
"""
from pulp import *
T = len(solar_generation)
prob = LpProblem("Solar_Storage", LpMinimize)
# Variables
charge = [LpVariable(f"Charge_{t}", lowBound=0, upBound=power_capacity)
for t in range(T)]
discharge = [LpVariable(f"Discharge_{t}", lowBound=0, upBound=power_capacity)
for t in range(T)]
soc = [LpVariable(f"SOC_{t}", lowBound=0, upBound=energy_capacity)
for t in range(T)]
grid_import = [LpVariable(f"Import_{t}", lowBound=0) for t in range(T)]
grid_export = [LpVariable(f"Export_{t}", lowBound=0) for t in range(T)]
# Objective: minimize electricity cost
import_cost = lpSum([grid_import[t] * tariff_structure['import_rate'][t]
for t in range(T)])
export_revenue = lpSum([grid_export[t] * tariff_structure['export_rate'][t]
for t in range(T)])
prob += import_cost - export_revenue
# Constraints
for t in range(T):
# Energy balance
# Load = Solar + Discharge - Charge + Import - Export
prob += load[t] == solar_generation[t] + discharge[t] - charge[t] + \
grid_import[t] - grid_export[t]
# Battery can only charge from solar
prob += charge[t] <= solar_generation[t]
# SOC dynamics
if t == 0:
prob += soc[t] == energy_capacity * 0.2 + \
charge[t] * 0.92 - discharge[t] / 0.92
else:
prob += soc[t] == soc[t-1] + \
charge[t] * 0.92 - discharge[t] / 0.92
# Solve
prob.solve(PULP_CBC_CMD(msg=0))
# Calculate metrics
total_import = sum([grid_import[t].varValue for t in range(T)])
total_export = sum([grid_export[t].varValue for t in range(T)])
total_load = sum(load)
total_solar = sum(solar_generation)
self_consumption = total_solar - total_export
self_consumption_rate = self_consumption / total_solar * 100
return {
'total_cost': value(prob.objective),
'grid_import_kwh': total_import,
'grid_export_kwh': total_export,
'self_consumption_kwh': self_consumption,
'self_consumption_rate_pct': self_consumption_rate,
'charge_schedule': [charge[t].varValue for t in range(T)],
'discharge_schedule': [discharge[t].varValue for t in range(T)],
'soc_profile': [soc[t].varValue for t in range(T)]
}
系統サービスと周波数調整
周波数調整の入札
def optimize_frequency_regulation(regulation_prices, energy_prices,
power_capacity, energy_capacity,
performance_score=0.95):
"""
Optimize battery participation in frequency regulation market
Parameters:
- regulation_prices: dict with 'reg_up' and 'reg_down' prices ($/MW)
- energy_prices: real-time energy prices ($/MWh)
- power_capacity: battery power rating (MW)
- energy_capacity: battery energy capacity (MWh)
- performance_score: historical performance (affects payment)
"""
from pulp import *
T = len(regulation_prices['reg_up'])
prob = LpProblem("Frequency_Regulation", LpMaximize)
# Variables
reg_up_capacity = [LpVariable(f"RegUp_{t}", lowBound=0, upBound=power_capacity)
for t in range(T)]
reg_down_capacity = [LpVariable(f"RegDown_{t}", lowBound=0, upBound=power_capacity)
for t in range(T)]
energy_position = [LpVariable(f"Energy_{t}") for t in range(T)]
# Objective: maximize regulation revenue
reg_up_revenue = lpSum([reg_up_capacity[t] * regulation_prices['reg_up'][t] *
performance_score for t in range(T)])
reg_down_revenue = lpSum([reg_down_capacity[t] * regulation_prices['reg_down'][t] *
performance_score for t in range(T)])
prob += reg_up_revenue + reg_down_revenue
# Constraints
for t in range(T):
# Power capacity constraint
prob += reg_up_capacity[t] + reg_down_capacity[t] <= power_capacity
# Energy capacity constraint (must be able to sustain regulation)
# Assume 50% deployment over hour
prob += energy_position[t] + 0.5 * reg_up_capacity[t] <= energy_capacity
prob += energy_position[t] - 0.5 * reg_down_capacity[t] >= 0
# Energy neutrality (return to same SOC)
if t > 0:
prob += energy_position[t] == energy_position[t-1]
prob += energy_position[0] == energy_capacity * 0.5
# Solve
prob.solve(PULP_CBC_CMD(msg=0))
return {
'total_revenue': value(prob.objective),
'reg_up_capacity': [reg_up_capacity[t].varValue for t in range(T)],
'reg_down_capacity': [reg_down_capacity[t].varValue for t in range(T)],
'avg_capacity_offered': np.mean([reg_up_capacity[t].varValue + reg_down_capacity[t].varValue
for t in range(T)])
}
バッテリーの劣化と寿命管理
劣化モデリング
class BatteryDegradationModel:
"""
Model battery degradation from cycling and calendar aging
"""
def __init__(self, initial_capacity_mwh, chemistry='li-ion'):
self.initial_capacity = initial_capacity_mwh
self.current_capacity = initial_capacity_mwh
self.chemistry = chemistry
self.total_cycles = 0
self.total_throughput = 0
def calculate_cycle_aging(self, depth_of_discharge, num_cycles):
"""
Calculate capacity loss from cycling
Cycle life depends on DOD:
- 10% DOD: ~100,000 cycles
- 50% DOD: ~10,000 cycles
- 80% DOD: ~5,000 cycles
- 100% DOD: ~3,000 cycles
"""
if self.chemistry == 'li-ion':
# Empirical cycle life model
cycle_life_100 = 3000 # cycles at 100% DOD
# Cycle life increases with lower DOD (roughly exponential)
cycle_life = cycle_life_100 * (1 / depth_of_discharge) ** 0.5
# Capacity loss per cycle
loss_per_cycle = 0.2 / cycle_life # 20% loss at EOL
capacity_loss = loss_per_cycle * num_cycles
elif self.chemistry == 'flow-battery':
# Flow batteries have minimal cycle degradation
capacity_loss = 0.0001 * num_cycles
else:
capacity_loss = 0
self.current_capacity -= capacity_loss
self.total_cycles += num_cycles
return capacity_loss
def calculate_calendar_aging(self, days, avg_soc=0.5, avg_temp_c=25):
"""
Calculate capacity loss from calendar aging
Higher SOC and temperature accelerate aging
"""
if self.chemistry == 'li-ion':
# Base calendar aging: 2-3% per year at 25°C, 50% SOC
base_rate = 0.025 / 365 # per day
# Temperature factor (Arrhenius-like)
temp_factor = np.exp((avg_temp_c - 25) * 0.05)
# SOC stress factor
soc_factor = 1 + (avg_soc - 0.5)
daily_loss_rate = base_rate * temp_factor * soc_factor
capacity_loss = daily_loss_rate * days * self.initial_capacity
else:
capacity_loss = 0
self.current_capacity -= capacity_loss
return capacity_loss
def calculate_state_of_health(self):
"""Return current state of health (%)"""
return (self.current_capacity / self.initial_capacity) * 100
def estimate_remaining_life(self, daily_cycles, avg_dod):
"""
Estimate remaining useful life
Returns days until 80% capacity threshold
"""
target_capacity = self.initial_capacity * 0.8
remaining_capacity_to_lose = self.current_capacity - target_capacity
if remaining_capacity_to_lose <= 0:
return 0 # Already below threshold
# Daily degradation from cycling
cycle_loss_per_day = self.calculate_cycle_aging(avg_dod, daily_cycles)
# Daily calendar aging
calendar_loss_per_day = self.calculate_calendar_aging(1)
total_daily_loss = cycle_loss_per_day + calendar_loss_per_day
if total_daily_loss <= 0:
return float('inf')
remaining_days = remaining_capacity_to_lose / total_daily_loss
return remaining_days
# Example usage
battery = BatteryDegradationModel(initial_capacity_mwh=10, chemistry='li-ion')
# Simulate 1 year of operation
for day in range(365):
# 1 cycle per day at 80% DOD
battery.calculate_cycle_aging(depth_of_discharge=0.8, num_cycles=1)
# Calendar aging
battery.calculate_calendar_aging(days=1, avg_soc=0.6, avg_temp_c=25)
print(f"State of Health after 1 year: {battery.calculate_state_of_health():.1f}%")
print(f"Estimated remaining life: {battery.estimate_remaining_life(1, 0.8)/365:.1f} years")
複数用途による価値の積み上げ
収益の積み上げの最適化
def optimize_multi_use_storage(use_cases, power_capacity, energy_capacity,
time_horizon=24):
"""
Optimize battery across multiple revenue streams (value stacking)
Use cases can include:
- Energy arbitrage
- Demand charge reduction
- Frequency regulation
- Capacity market
- Renewable firming
"""
from pulp import *
prob = LpProblem("Value_Stacking", LpMaximize)
T = time_horizon
# Variables: power allocation to each use case
allocation = {}
for use_case in use_cases:
for t in range(T):
allocation[use_case['name'], t] = LpVariable(
f"{use_case['name']}_{t}",
lowBound=0,
upBound=power_capacity
)
# Objective: maximize total revenue
total_revenue = []
for use_case in use_cases:
for t in range(T):
value_rate = use_case['value_per_mw_hour'][t]
total_revenue.append(allocation[use_case['name'], t] * value_rate)
prob += lpSum(total_revenue)
# Constraints
# Total power allocation cannot exceed capacity
for t in range(T):
prob += lpSum([allocation[uc['name'], t] for uc in use_cases]) <= power_capacity
# Use case specific constraints
for use_case in use_cases:
if 'min_commitment' in use_case:
# Some services require minimum commitment
for t in range(T):
prob += allocation[use_case['name'], t] >= \
use_case['min_commitment'] * power_capacity
if 'max_hours' in use_case:
# Some services limited to certain hours per day
prob += lpSum([allocation[use_case['name'], t]
for t in range(T)]) <= use_case['max_hours'] * power_capacity
# Solve
prob.solve(PULP_CBC_CMD(msg=0))
# Calculate revenue by use case
revenue_breakdown = {}
for use_case in use_cases:
revenue = sum([allocation[use_case['name'], t].varValue *
use_case['value_per_mw_hour'][t]
for t in range(T)])
revenue_breakdown[use_case['name']] = revenue
return {
'total_revenue': value(prob.objective),
'revenue_breakdown': revenue_breakdown,
'allocation_schedule': {
use_case['name']: [allocation[use_case['name'], t].varValue
for t in range(T)]
for use_case in use_cases
}
}
# Example
use_cases = [
{
'name': 'Energy_Arbitrage',
'value_per_mw_hour': [5, 4, 3, 2, 2, 3, 8, 15, 20, 22, 24, 23,
22, 21, 20, 22, 25, 28, 26, 22, 18, 12, 8, 6]
},
{
'name': 'Frequency_Regulation',
'value_per_mw_hour': [12] * 24,
'min_commitment': 0.5, # Must commit at least 50% if participating
},
{
'name': 'Demand_Response',
'value_per_mw_hour': [0]*14 + [50, 50, 50, 50] + [0]*6,
'max_hours': 4 # Only 4 hours per day
}
]
result = optimize_multi_use_storage(use_cases, power_capacity=10,
energy_capacity=40, time_horizon=24)
print(f"Total daily revenue: ${result['total_revenue']:,.0f}")
for uc, rev in result['revenue_breakdown'].items():
print(f" {uc}: ${rev:,.0f} ({rev/result['total_revenue']*100:.1f}%)")
ツールとライブラリ
Python ライブラリ
最適化:
PuLP:線形計画法Pyomo:高度な最適化モデリングcvxpy:凸最適化gurobipy:Gurobi オプティマイザー
バッテリーモデリング:
PyBaMM:物理ベースのバッテリーモデリングscikit-learn:劣化予測のための機械学習
エネルギー分析:
pvlib:太陽光発電のモデリングpandapower:電力システム分析SAM(NREL):System Advisor Model
データと可視化:
pandas、numpy:データ操作matplotlib、plotly:可視化
商用ソフトウェア
エネルギー貯蔵:
- Energy Toolbase:バッテリー貯蔵の分析と提案
- Homer Energy:ハイブリッド再生可能エネルギーシステムの設計
- CAISO BESS:バッテリーエネルギー貯蔵システム用ツール
- Stem Athena:AI を活用した貯蔵の最適化
系統統合:
- AutoGrid Flex:分散型エネルギーリソースの最適化
- Greensmith GEMS:系統エネルギー管理システム
- Fluence Mosaic:バッテリーエネルギー貯蔵プラットフォーム
- Tesla Autobidder:自動エネルギー取引
財務分析:
- Aurora:エネルギー市場のモデリング
- Ascend Analytics:貯蔵の価値評価
- NREL REopt:再生可能エネルギーの統合
よくある課題と解決策
課題:収益の不確実性
問題:
- 電力価格の変動
- 市場ルールの変更
- 技術コストの動向
解決策:
- 保守的な財務予測
- 収益源の多様化(バリュースタッキング)
- 柔軟な契約とPPA
- リアルオプション分析
- シナリオに基づく計画
課題:劣化管理
問題:
- バッテリー容量の経時的な低下
- 保証条件の順守
- 最適な交換時期
解決策:
- 劣化を考慮したディスパッチアルゴリズム
- 健全度(SOH)のモニタリング
- 保守的なDODとサイクル上限
- 予知保全
- 増設戦略
課題:系統連系
問題:
- 系統連系の遅延とコスト
- 系統サービス要件
- 電力会社との調整
解決策:
- 早期の系統連系申請
- 事前承認済み機器リスト
- 明確な運用手順
- 電力会社との定期的な連絡
- 適合性試験と認証
課題:最適な規模の決定
問題:
- 将来の需要/発電量が不確実
- 技術コストの変化
- 用途間の競合
解決策:
- 主要パラメーターの感度分析
- モジュール式/拡張可能な設計
- 待つことによるオプション価値
- 本格導入前のパイロットプログラム
- 複数年にわたる最適化
出力形式
エネルギー貯蔵最適化レポート
エグゼクティブサマリー:
- 推奨する蓄電システムの構成
- 経済性分析と投資回収
- 主な価値創出要因
- 導入ロードマップ
システム仕様:
| パラメーター | 値 | 備考 |
|---|---|---|
| 技術 | Li-ion NMC | 高エネルギー密度 |
| 出力容量 | 5 MW | AC 定格 |
| エネルギー容量 | 20 MWh | 使用可能容量 |
| 放電時間 | 4時間 | 定格出力時 |
| 往復効率 | 88% | AC-AC |
| 保証 | 10 years / 5,000 cycles | EOL容量80% |
経済性分析:
| 指標 | 値 |
|---|---|
| 資本コスト | $7.5M |
| 設置・EPC | $1.5M |
| プロジェクト総コスト | $9.0M |
| 年間収益 | $1.2M |
| 年間O&M | $75K |
| 年間純便益 | $1.125M |
| 単純投資回収期間 | 8.0年 |
| NPV(15年、8%) | $3.2M |
| IRR | 12.5% |
収益源:
| 収益源 | 年間価値 | 全体に占める割合 |
|---|---|---|
| エネルギー裁定取引 | $450K | 37% |
| デマンドチャージ削減 | $380K | 32% |
| 周波数調整 | $270K | 22% |
| 容量市場 | $100K | 8% |
| 合計 | $1,200K | 100% |
運用戦略:
| 時間帯 | 主な用途 | 副次的な用途 | 平均SOC目標 |
|---|---|---|---|
| 00:00-06:00 | 充電(裁定取引) | - | 80% |
| 06:00-10:00 | 周波数調整 | - | 50% |
| 10:00-14:00 | 太陽光発電の出力安定化 | 周波数調整 | 40% |
| 14:00-20:00 | ピークカット | 裁定取引 | 20% |
| 20:00-24:00 | 放電(裁定取引) | - | 40% |
性能指標:
| 指標 | 目標 | モニタリング |
|---|---|---|
| 1日あたりのサイクル数 | 1.2 | SCADAロギング |
| 平均DOD | 70% | BMSデータ |
| 稼働率 | > 95% | 稼働時間の追跡 |
| 応答時間 | < 1 sec | 市場要件への適合 |
| SOHの劣化 | < 2.5%/年 | 四半期ごとの試験 |
確認事項
追加の情報が必要な場合:
- 主な用途は何ですか?(裁定取引、ピークカット、再生可能エネルギー、系統サービス)
- 規模はどの程度ですか?(住宅、商業、電力会社)
- 系統の料金体系はどのようなものですか?(TOU、デマンドチャージ、リアルタイム料金)
- 既存または計画中の再生可能エネルギー発電設備はありますか?
- 利用可能な予算、または目標とする投資回収期間はどの程度ですか?
- 用地、温度、安全に関する制約はありますか?
- 想定されるプロジェクトの寿命はどのくらいですか?
関連スキル
- power-grid-optimization:系統連系と給電のため
- renewable-energy-planning:太陽光と風力の組み合わせのため
- energy-logistics:エネルギーサプライチェーンの管理のため
- demand-forecasting:需要と発電量の予測のため
- optimization-modeling:高度な最適化手法のため
- capacity-planning:長期的な設備容量計画のため
- risk-mitigation:財務リスクと運用リスクの管理のため
コメント
Markdown 対応