小能豆

如何在 Pyomo 约束内实现条件求和

py

我正在用 Pyomo 编写一个模型,该模型model.Biomass针对县级约 100 种不同生物质类型(= 玉米、小麦、木屑等)的生物质生产进行优化(model.SourceCounty)。我试图编写的约束之一要求我的模型的生物质输出等于我已经从另一个模型中获得的生产值。但是,这个其他模型不具备我的 Pyomo 模型所具有的粒度。它仅以区域(而非县级)为基础(model.Zone)具有更一般的生物质分组(model.SimpBiomass= 草本生物质、木质生物质)的生物质生产值。

我在约束中尝试做的是将model.x来自另一个模型的区域和更一般的生物量分组的生物量生产决策变量 () 相加,然后要求该总和等于另一个模型的输出,以便我的模型产生一致的结果。但是,我了解到我编写代码的当前方式(如下)不起作用,因为当决策变量的值尚未求解时,Pyomo 只调用一次约束。因此,我的带有 if 语句的 for 循环只返回 0 值。

from pyomo.environ import *

# initialize model -- can rewrite as Concrete Model if that's better for what I'm trying to do
model = AbstractModel()

# initialize indices, including some manually
model.SourceCounty = Set() # county
model.Biomass = Set() # biomass type in my model
model.Year = Set(initialize=[2022, 2024, 2026, 2028, 2030, 2032, 2035, 2040])  # year
model.SimpBiomass = Set(initialize=['herbaceous biomass', 'waste biomass', 'woody biomass'])  # type of feedstock resource - simplified (from separate model)
model.Zone = Set(initialize=['midwest','southeast'])  # zones from separate model

# Create data import structure
data = DataPortal()

# Load indices that require data frame
data.load(filename='fips.csv', set=model.SourceCounty)
data.load(filename='Resources.csv', set=model.Biomass)

# initialize parameters
model.EERF = Param(model.SimpBiomass, model.Zone, model.Year)  # biomass production from the other model that I'm trying to match in my model
model.QIJ = Param(model.SourceCounty)  # mapping of county FIPS code from my model to zones from other model
model.AC = Param(model.Biomass)  # mapping of specific resource type from my model into less specific from other model (values are those in SimpBiomass)

# load in parameters
data.load(filename="county_to_zone.csv", param=model.QIJ)
data.load(filename="BT16_to_zone_Resource.csv", param=model.AC)

# create decision variables (known as Var in Pyomo)
model.x = Var(model.Biomass, model.SourceCounty, model.Year, domain=PositiveReals)  # feedstock production indexed by feedstock, source county, year

# I leave out the objective function for brevity

# Constraint in question
def feedstock_prod_rule(model, c, q, t):
    expr2 = 0 # initialize summing variable

    # for each biomass type (a) in my model, check if it belongs to a biomass category (c) from the other model

    for a in model.Biomass:
        if model.AC[a] == c:

            # for each county (i) in my model, check if it belongs to a zone (q) from the other model
            for i in model.SourceCounty:
                if model.QIJ[i] == q:

                    # if it belongs to q and c from other model, add to expr2
                    expr2 += model.x[a, i, t]

    # Sum of all biomass production from my model within zone q and biomass type c (expr2 at end of looping) should equal the output of the other model (EERF).

    return expr2 == model.EERF[c, q, t]


# Add as constraint
model.feedstock_prod = Constraint(model.SimpBiomass, model.Zone, model.Year, rule=feedstock_prod_rule)

我需要帮助找出一种不同的方法来编写此约束,以便它不依赖于构建一个依赖于model.x尚未求解的决策变量值的表达式。有没有办法在一行代码中return完成相同的操作?


阅读 65

收藏
2025-02-27

共1个答案

小能豆

我不完全确定您是否正确诊断了问题,而且如果没有数据文件的一些片段,则很难重新创建。(这也是不必要的,因为即使可以重新创建,也有更好的方法。:))

总的来说,您说得对,您不能将条件语句嵌入到依赖于变量值的约束中,因为在将约束编码到模型中时,变量值是未知的。但是,您使用的是基于固定参数的条件,所以应该没问题。但是,您正在将参数与集合中的项目进行比较,这是……一个糟糕的计划,即使它有效……从未尝试过。这样做的结构问题是,您正在使用 1:1 配对,而不是标记一组东西,这导致了条件语句。您的结构如下:

beans : food
lettuce : food
paper : trash

您确实更乐意与下面这样的团体合作,并避免使用“if”语句:

food: { beans, lettuce }
trash: { paper }

因此可以做到这一点,然后您可以将其加载到抽象模型中。但是,我认为您不能从.csv文件中执行此操作,因为我认为没有办法indexed sets在中表达.csv。您可以轻松地在.yaml或中完成此操作.json。有关更多示例,请参阅 pyomo dox。您甚至可以混合您的数据源,这样您就可以保留其他 csv,只要它们是一致的,所以您需要注意这一点。这是一个有效的例子,我认为它会清理你的模型。特别注意最后约束中的索引和分组):

import pyomo.environ as pe

m = pe.AbstractModel()

# SETS
m.Group_names = pe.Set()
m.Items = pe.Set()
m.Groupings = pe.Set(m.Group_names, within=m.Items)

# PARAMS
m.Cost = pe.Param(m.Group_names)

# VARS
m.X = pe.Var(m.Items)

# Constraint "for each Group"
def C1(m, g_name):
    return sum(m.X[i] for i in m.Groupings[g_name]) <= 10

m.C1 = pe.Constraint(m.Group_names, rule=C1)

# load data from sources
data = pe.DataPortal()
data.load(filename='cost.csv', param=m.Cost)
data.load(filename='data.yaml')
data.load(filename='items.csv', set=m.Items)

instance = m.create_instance(data)
instance.pprint()

yaml文件(其他文件未显示,它们很简单):

Group_names: ['Waste', 'Food', 'Critters']

Groupings:
  'Waste': ['Trash', 'Coal']
  'Food': ['Salad', 'Compost', 'Fish']
  'Critters': ['Snails', 'Worms']

最终模型:

3 Set Declarations
    Group_names : Size=1, Index=None, Ordered=Insertion
        Key  : Dimen : Domain : Size : Members
        None :     1 :    Any :    3 : {'Waste', 'Food', 'Critters'}
    Groupings : Size=3, Index=Group_names, Ordered=Insertion
        Key      : Dimen : Domain : Size : Members
        Critters :     1 :  Items :    2 : {'Snails', 'Worms'}
            Food :     1 :  Items :    3 : {'Salad', 'Compost', 'Fish'}
           Waste :     1 :  Items :    2 : {'Trash', 'Coal'}
    Items : Size=1, Index=None, Ordered=Insertion
        Key  : Dimen : Domain : Size : Members
        None :     1 :    Any :    7 : {'Trash', 'Snails', 'Worms', 'Coal', 'Salad', 'Compost', 'Fish'}

1 Param Declarations
    Cost : Size=2, Index=Group_names, Domain=Any, Default=None, Mutable=False
        Key   : Value
         Food :   8.9
        Waste :   4.2

1 Var Declarations
    X : Size=7, Index=Items
        Key     : Lower : Value : Upper : Fixed : Stale : Domain
           Coal :  None :  None :  None : False :  True :  Reals
        Compost :  None :  None :  None : False :  True :  Reals
           Fish :  None :  None :  None : False :  True :  Reals
          Salad :  None :  None :  None : False :  True :  Reals
         Snails :  None :  None :  None : False :  True :  Reals
          Trash :  None :  None :  None : False :  True :  Reals
          Worms :  None :  None :  None : False :  True :  Reals

1 Constraint Declarations
    C1 : Size=3, Index=Group_names, Active=True
        Key      : Lower : Body                            : Upper : Active
        Critters :  -Inf :            X[Snails] + X[Worms] :  10.0 :   True
            Food :  -Inf : X[Salad] + X[Compost] + X[Fish] :  10.0 :   True
           Waste :  -Inf :              X[Trash] + X[Coal] :  10.0 :   True
2025-02-27