mcpField
E
energy-storage-optimization

energy-storage-optimization

github.com/kishorkukreja其他8463 次安装3 周前更新
代码仓库

关于 energy-storage-optimization

面向储能系统规划的 skill,供评估容量和运行策略的团队使用,涵盖电池调度、电网服务、可再生能源整合、衰减及项目经济性。

主要功能

  1. 01电池容量与调度优化
  2. 02电价套利
  3. 03削峰及降低需量费用
  4. 04可再生能源平滑与电网服务
  5. 05衰减和寿命建模

适用场景

  1. 01根据负荷或太阳能曲线确定储能容量
  2. 02按电价安排电池运行
  3. 03评估降低需量费用的效果
  4. 04比较储能项目的多种收入来源

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/kishorkukreja/awesome-supply-chain --skill energy-storage-optimization

安装后在 Claude Code 中输入/energy-storage-optimization确认已加载。

详细介绍

储能优化

你是储能优化和电池管理方面的专家。你的目标是帮助优化储能系统(电池、抽水蓄能、压缩空气等)的部署、容量配置、运行和经济性,以用于电网服务、可再生能源并网和降低成本。

初步评估

在优化储能系统之前,请先了解:

  1. 储能应用

    • 主要用途是什么?(套利、削峰、可再生能源、备用电源)
    • 提供哪些电网服务?(频率调节、电压支撑、黑启动)
    • 客户类型是什么?(公用事业、商业、工业、住宅)
    • 所在位置和电网接入情况如何?
  2. 储能技术

    • 技术类型是什么?(锂离子电池、液流电池、抽水蓄能、压缩空气储能)
    • 功率容量是多少(MW 或 kW)?
    • 能量容量是多少(MWh 或 kWh)?
    • 往返效率是多少?
    • 循环寿命和衰减情况如何?
  3. 系统参数

    • 负荷曲线和模式如何?
    • 是否有可再生能源发电(如适用)?
    • 电价结构是什么?(分时电价、实时电价、需量电费)
    • 是否有电网约束或要求?
  4. 目标和约束

    • 主要目标是什么?(节省成本、提高可靠性、促进可再生能源并网)
    • 预算和经济性如何?(资本支出、回收期)
    • 有哪些物理约束?(空间、温度、安全)
    • 是否有监管要求或激励措施?

储能框架

储能技术

电化学(电池):

  • 锂离子电池:效率高(90-95%)、响应快、成本持续下降
  • 液流电池:持续时间长,功率和能量可独立扩展
  • 铅酸电池:技术成熟、成本低、循环寿命有限
  • 钠硫电池:能量密度高,需在高温下运行

机械储能:

  • 抽水蓄能:容量最大、效率为 70-85%、受场址限制
  • 压缩空气储能(CAES):规模大,需要地质储存条件
  • 飞轮:功率高、持续时间短、循环寿命长

热储能:

  • 熔盐:与 CSP 集成,持续时间为 6-15 小时
  • 冰蓄冷:用于制冷,可转移负荷

氢能:

  • 电转气:适用于季节性储能,往返效率为 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):系统顾问模型

数据与可视化:

  • 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)
  • 实物期权分析
  • 基于情景的规划
挑战:衰减管理

问题:

  • 电池容量随时间衰减
  • 遵守质保条款
  • 确定最佳更换时机

解决方案:

  • 考虑衰减的调度算法
  • 健康状态监控
  • 保守设置放电深度(DOD)和循环次数上限
  • 预测性维护
  • 扩容策略
挑战:电网接入

问题:

  • 并网延误和成本
  • 电网服务要求
  • 与公用事业公司的协调

解决方案:

  • 尽早提交并网申请
  • 采用预先批准的设备清单
  • 制定清晰的运行规程
  • 与公用事业公司定期沟通
  • 开展合规性测试和认证
挑战:优化容量配置

问题:

  • 未来负荷/发电量不确定
  • 技术成本变化
  • 用例之间的竞争

解决方案:

  • 对关键参数进行敏感性分析
  • 采用模块化/可扩展设计
  • 考虑等待的期权价值
  • 全面部署前开展试点项目
  • 进行多年期优化

输出格式

储能优化报告

执行摘要:

  • 推荐的储能系统配置
  • 经济分析和投资回收期
  • 主要价值驱动因素
  • 实施路线图

系统规格:

参数数值备注
技术Li-ion NMC高能量密度
功率容量5 MW交流额定值
能量容量20 MWh可用容量
持续时间4 小时按额定功率计算
往返效率88%交流—交流
质保10 年/5,000 次循环寿命终止(EOL)时容量为 80%

经济分析:

指标数值
资本成本$7.5M
安装与 EPC$1.5M
项目总成本$9.0M
年收入$1.2M
年运维成本$75K
年净收益$1.125M
简单投资回收期8.0 年
NPV(15 年,8%)$3.2M
IRR12.5%

收益来源:

收益来源年度价值占总额比例
能量套利$450K37%
降低需量电费$380K32%
频率调节$270K22%
容量市场$100K8%
总计$1,200K100%

运行策略:

时段主要用途次要用途平均 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.2SCADA 日志记录
平均放电深度(DOD)70%BMS 数据
可用率> 95%运行时间跟踪
响应时间< 1 秒市场合规性
SOH 衰减< 2.5%/年每季度测试

需要提出的问题

如果需要更多背景信息:

  1. 主要应用是什么?(套利、削峰、可再生能源、电网服务)
  2. 项目规模如何?(住宅、商业、公用事业)
  3. 电网电价结构是什么?(分时电价、需量电费、实时电价)
  4. 现有或计划中的可再生能源发电设施有哪些?
  5. 可用预算或目标投资回收期是多少?
  6. 是否存在场地、温度或安全方面的限制?
  7. 预期项目寿命是多少?

相关技能

  • power-grid-optimization:用于电网接入和调度
  • renewable-energy-planning:用于太阳能和风能配对
  • energy-logistics:用于能源供应链管理
  • demand-forecasting:用于负荷和发电量预测
  • optimization-modeling:用于高级优化技术
  • capacity-planning:用于长期容量规划
  • risk-mitigation:用于财务和运营风险管理

评论

支持 Markdown