Pyomo中双索引与多单索引变量的性能差异及选型咨询
Pyomo中电动汽车充电决策变量的两种创建方案对比
问题描述
在多辆电动汽车时段充电曲线优化工作中,Pyomo里有两种创建充电功率决策变量的方式:
- 方案一:为每辆电动汽车创建以时间步为索引的单独
pyo.Var变量 - 方案二:创建同时以时间步和电动汽车为索引的单一双索引
pyo.Var变量
需要明确二者是否存在差异,以及当电动汽车数量或时间步规模扩大时,哪种方案在性能和实用性上更优。
核心差异与选型建议
1. 模型维护与扩展性
- 方案一:变量分散为多个独立组件,新增电动汽车时需要手动添加新的
pyo.Var组件,遍历或修改所有充电变量时需要逐个处理,维护成本随EV数量增加而线性上升,容易出现漏处理或命名错误。 - 方案二:所有充电变量集中在一个双索引组件内,新增EV只需更新
electric_vehicles集合即可,变量的遍历、修改、查看操作统一,扩展性更强,模型结构更清晰。
2. 性能表现(大规模场景)
当EV数量或时间步规模扩大时,两种方案的性能差异会超出个人习惯范畴:
- Pyomo内部开销:每个
pyo.Var组件都有额外的元数据管理开销,方案一的多变量会累积这些开销,导致模型实例化、变量初始化的时间显著增加;方案二的单组件结构能减少这类冗余开销。 - 求解器交互效率:双索引变量的结构更紧凑,Pyomo向求解器传递模型数据时,能更高效地组织稀疏矩阵(优化问题的核心数据结构),减少数据传输和预处理的时间,求解器也能更好地利用索引结构进行优化计算。
3. 代码简洁性与可维护性
- 方案一需要通过字符串拼接(如
f"charging_profile_ev{ev.idx}")动态获取变量组件,代码冗余且容易出现命名错误,调试难度更高。 - 方案二的约束、目标函数写法更简洁,直接通过双索引访问变量,代码可读性和可调试性更强,减少人为出错的概率。
代码示例
基础EV类
import pandas as pd import pyomo.environ as pyo class EV: def __init__(self, idx: int, capacity_kwh: float, starting_soc: float, time_steps: pd.DatetimeIndex, driving_profile_km: list[float]): self.idx = idx self.capacity_kwh = capacity_kwh self.soc = starting_soc self.driving_profile_km = pd.Series(driving_profile_km, index=time_steps) def charge(self, power_kw: float): duration_h = 1.0 self.soc = self.soc + (power_kw * duration_h) / self.capacity_kwh def drive(self, distance_km: float): consumption_kwh_per_km = 0.15 self.soc = self.soc - (distance_km * consumption_kwh_per_km) / self.capacity_kwh
共同初始化代码
model = pyo.ConcreteModel() time_steps = pd.date_range("2023-11-15 10:00", "2023-11-15 13:00", freq="h") model.time_steps = pyo.Set(initialize=time_steps) ev1 = EV(1, capacity_kwh=40.0, starting_soc=0.5, time_steps=time_steps, driving_profile_km=[20.0, 0.0, 0.0, 20.0]) ev2 = EV(2, capacity_kwh=30.0, starting_soc=0.5, time_steps=time_steps, driving_profile_km=[10.0, 5.0, 0.0, 15.0]) ev3 = EV(3, capacity_kwh=20.0, starting_soc=0.5, time_steps=time_steps, driving_profile_km=[0.0, 30.0, 25.0, 5.0]) model.electric_vehicles = pyo.Set(initialize=[ev1, ev2, ev3])
方案一:单辆EV对应单索引变量
# 创建决策变量 for ev in model.electric_vehicles: model.add_component(f"charging_profile_ev{ev.idx}", pyo.Var(model.time_steps, bounds=[0, 11], initialize=0.0)) # SOC表达式 def expression_soc(m, ts, ev): charging_power_kw = m.component(f"charging_profile_ev{ev.idx}")[ts] driving_distance_km = ev.driving_profile_km.loc[ts] ev.charge(charging_power_kw) ev.drive(driving_distance_km) return ev.soc model.add_component(f"soc_expression", pyo.Expression(model.time_steps, model.electric_vehicles, rule=expression_soc)) # SOC约束 def constraint_soc(m, ts, ev): soc_value = m.component(f"soc_expression")[ts, ev] return 0.5, soc_value, 1.0 model.add_component(f"soc_constraint", pyo.Constraint(model.time_steps, model.electric_vehicles, rule=constraint_soc)) # 目标函数 def objective(m): value = 0 for ev in m.electric_vehicles: for ts in m.time_steps: value = value + m.component(f"charging_profile_ev{ev.idx}")[ts] return value model.objective = pyo.Objective(rule=objective, sense=pyo.minimize) # 求解与输出 results = pyo.SolverFactory("glpk").solve(model) model.objective.display() model.charging_profile_ev1.display() model.charging_profile_ev2.display() model.charging_profile_ev3.display()
方案二:双索引单一变量
# 创建决策变量 model.add_component(f"charging_profiles", pyo.Var(model.time_steps, model.electric_vehicles, bounds=[0, 11], initialize=0.0)) # SOC表达式 def expression_soc(m, ts, ev): charging_power_kw = m.component(f"charging_profiles")[ts, ev] driving_distance_km = ev.driving_profile_km.loc[ts] ev.charge(charging_power_kw) ev.drive(driving_distance_km) return ev.soc model.add_component(f"soc_expression", pyo.Expression(model.time_steps, model.electric_vehicles, rule=expression_soc)) # SOC约束 def constraint_soc(m, ts, ev): soc_value = m.component(f"soc_expression")[ts, ev] return 0.5, soc_value, 1.0 model.add_component(f"soc_constraint", pyo.Constraint(model.time_steps, model.electric_vehicles, rule=constraint_soc)) # 目标函数 def objective(m): value = 0 for ev in m.electric_vehicles: for ts in m.time_steps: value = value + m.component(f"charging_profiles")[ts, ev] return value model.objective = pyo.Objective(rule=objective, sense=pyo.minimize) # 求解与输出 results = pyo.SolverFactory("glpk").solve(model) model.objective.display() model.charging_profiles.display()
内容的提问来源于stack exchange,提问作者Schicko
相关产品推荐
相关产品推荐

