Pyomo 5.7.3及以上版本AbstractModel依赖组件实例化失败问题
Pyomo 5.7.3+中用Param定义RangeSet报错的解决办法
问题重现
以下代码在Pyomo 5.7.0可正常运行,但升级到5.7.3及以上版本时,构造time_steps集合会抛出ValueError: Error retrieving immutable Param value (battery.n_time_steps):
import pyomo.environ as pyo def test_param_pyomo5_7(): def create_battery(): block = pyo.Block() block.n_time_steps = pyo.Param(within=pyo.NonNegativeIntegers, doc='Number of time steps') block.time_steps = pyo.RangeSet(1, block.n_time_steps, doc='Time steps') return block def create_model() -> pyo.AbstractModel: model = pyo.AbstractModel() model.battery = create_battery() return model data = { None: { 'battery': { 'n_time_steps': {None: 24}, }, } } model = create_model() instance = model.create_instance(data=data)
完整报错回溯:
..\..\opt\Miniconda3\envs\tesca_optimizer\lib\site-packages\pyomo\core\base\PyomoModel.py:697: in create_instance instance.load( data, ..\..\opt\Miniconda3\envs\tesca_optimizer\lib\site-packages\pyomo\core\base\PyomoModel.py:734: in load self._load_model_data(dp, ..\..\opt\Miniconda3\envs\tesca_optimizer\lib\site-packages\pyomo\core\base\PyomoModel.py:787: in _load_model_data self._initialize_component(modeldata, namespaces, component_name, profile_memory) ..\..\opt\Miniconda3\envs\tesca_optimizer\lib\site-packages\pyomo\core\base\PyomoModel.py:825: in _initialize_component declaration.construct(data) ..\..\opt\Miniconda3\envs\tesca_optimizer\lib\site-packages\pyomo\core\base\block.py:2207: in construct obj.construct(data.get(name, None)) ..\..\opt\Miniconda3\envs\tesca_optimizer\lib\site-packages\pyomo\core\base\disable_methods.py:116: in construct return base.construct(self, data) ..\..\opt\Miniconda3\envs\tesca_optimizer\lib\site-packages\pyomo\core\base\set.py:2792: in construct args = tuple(value(arg) for arg in args) ..\..\opt\Miniconda3\envs\tesca_optimizer\lib\site-packages\pyomo\core\base\set.py:2792: in <genexpr> args = tuple(value(arg) for arg in args) pyomo\core\expr\numvalue.pyx:153: in pyomo.core.expr.numvalue.value ??? pyomo\core\expr\numvalue.pyx:138: in pyomo.core.expr.numvalue.value ??? ..\..\opt\Miniconda3\envs\tesca_optimizer\lib\site-packages\pyomo\core\base\param.py:853: in __call__ return self[None] ..\..\opt\Miniconda3\envs\tesca_optimizer\lib\site-packages\pyomo\core\base\indexed_component.py:577: in __getitem__ return self._getitem_when_not_present(index)
问题原因
Pyomo 5.7.3版本调整了组件构造的执行顺序:创建实例时,RangeSet会提前触发构造逻辑,尝试立即解析传入的上限参数block.n_time_steps,但此时这个Param还未从数据字典中加载具体值,导致访问未初始化的不可变Param报错。而5.7.0版本中RangeSet的构造会延迟到Param加载完成后执行,因此不会触发错误。
解决方案
方案1:用Lambda表达式延迟Param值的获取
修改RangeSet的定义,用lambda表达式延迟对Param值的读取,确保构造时Param已经完成数据加载:
def create_battery(): block = pyo.Block() block.n_time_steps = pyo.Param(within=pyo.NonNegativeIntegers, doc='Number of time steps') # 使用lambda延迟获取Param的值,直到构造阶段Param已初始化 block.time_steps = pyo.RangeSet(1, lambda m: m.n_time_steps, doc='Time steps') return block
方案2:重写Block的construct方法,手动控制构造顺序
通过自定义Block的construct方法,在Param加载完成后再创建time_steps集合:
def create_battery(): block = pyo.Block() block.n_time_steps = pyo.Param(within=pyo.NonNegativeIntegers, doc='Number of time steps') # 自定义构造逻辑,确保先加载Param再创建集合 def _construct_block(block_data): # 此时n_time_steps已经从数据中获取到值 block_data.time_steps = pyo.RangeSet(1, block_data.n_time_steps, doc='Time steps') block.construct = _construct_block return block
两种方案都能确保time_steps集合在n_time_steps Param完成数据初始化后再构造,避免访问未初始化的Param导致的报错。
内容的提问来源于stack exchange,提问作者phaebz
相关产品推荐
相关产品推荐

