5.7. Pyomo Examples#

5.7.1. Pyomo Model#

A Pyomo implementation of this blending model is shown in the next cell. The model is contained within a Python function so that it can be more easily reused for additional calculations, or eventually for use by the process operator.

Note that the pyomo library has been imported with the prefix pyomo. This is good programming practive to avoid namespace collisions with problem data.

import pyomo.environ as pyomo

vol = 100
abv = 0.040

def brew_blend(vol, abv, data):
    
    C = data.keys()
    
    model = pyomo.ConcreteModel()
    
    model.x = pyomo.Var(C, domain=pyomo.NonNegativeReals)
    
    model.cost = pyomo.Objective(expr = sum(model.x[c]*data[c]['cost'] for c in C))
    
    model.vol = pyomo.Constraint(expr = vol == sum(model.x[c] for c in C))
    model.abv = pyomo.Constraint(expr = 0 == sum(model.x[c]*(data[c]['abv'] - abv) for c in C))

    solver = pyomo.SolverFactory('glpk')
    solver.solve(model)

    print('Optimal Blend')
    for c in data.keys():
        print('  ', c, ':', model.x[c](), 'gallons')
    print()
    print('Volume = ', model.vol(), 'gallons')
    print('Cost = $', model.cost())
    
brew_blend(vol, abv, data)
WARNING: Could not locate the 'glpsol' executable, which is required for
    solver 'glpk'
---------------------------------------------------------------------------
ApplicationError                          Traceback (most recent call last)
<ipython-input-222-f1ef07ecaf09> in <module>
     27     print('Cost = $', model.cost())
     28 
---> 29 brew_blend(vol, abv, data)

<ipython-input-222-f1ef07ecaf09> in brew_blend(vol, abv, data)
     18 
     19     solver = pyomo.SolverFactory('glpk')
---> 20     solver.solve(model)
     21 
     22     print('Optimal Blend')

~/opt/anaconda3/lib/python3.8/site-packages/pyomo/opt/base/solvers.py in solve(self, *args, **kwds)
    516         """ Solve the problem """
    517 
--> 518         self.available(exception_flag=True)
    519         #
    520         # If the inputs are models, then validate that they have been

~/opt/anaconda3/lib/python3.8/site-packages/pyomo/opt/solver/shellcmd.py in available(self, exception_flag)
    116             if exception_flag:
    117                 msg = "No executable found for solver '%s'"
--> 118                 raise ApplicationError(msg % self.name)
    119             return False
    120         return True

ApplicationError: No executable found for solver 'glpk'

5.7.2. Example: Nonlinear Optimization of Series Reaction in a Continuous Stirred Tank Reactor#

from pyomo.environ import *

V = 40     # liters
kA = 0.5   # 1/min
kB = 0.1   # l/min
CAf = 2.0  # moles/liter

# create a model instance
model = ConcreteModel()

# create x and y variables in the model
model.q = Var()

# add a model objective
model.objective = Objective(expr = model.q*V*kA*CAf/(model.q + V*kB)/(model.q + V*kA), sense=maximize)

# compute a solution using ipopt for nonlinear optimization
results = SolverFactory('ipopt').solve(model)
model.pprint()


# print solutions
qmax = model.q()
CBmax = model.objective()
print('\nFlowrate at maximum CB = ', qmax, 'liters per minute.')
print('\nMaximum CB =', CBmax, 'moles per liter.')
print('\nProductivity = ', qmax*CBmax, 'moles per minute.')
1 Var Declarations
    q : Size=1, Index=None
        Key  : Lower : Value             : Upper : Fixed : Stale : Domain
        None :  None : 8.944271909985442 :  None : False : False :  Reals

1 Objective Declarations
    objective : Size=1, Index=None, Active=True
        Key  : Active : Sense    : Expression
        None :   True : maximize : 40.0*q/(q + 4.0)/(q + 20.0)

2 Declarations: q objective

Flowrate at maximum CB =  8.944271909985442 liters per minute.

Maximum CB = 0.954915028125263 moles per liter.

Productivity =  8.541019662483748 moles per minute.

5.7.3. Example 19.3: Linear Programming Refinery#

import pandas as pd

PRODUCTS = ['Gasoline', 'Kerosine', 'Fuel Oil', 'Residual']
FEEDS = ['Crude 1', 'Crude 2']

products = pd.DataFrame(index=PRODUCTS)
products['Price'] = [72, 48, 42, 20]
products['Max Production'] = [24000, 2000, 6000, 100000]

crudes = pd.DataFrame(index=FEEDS)
crudes['Processing Cost'] = [1.00, 2.00]
crudes['Feed Costs'] = [48, 30]

yields = pd.DataFrame(index=PRODUCTS)
yields['Crude 1'] = [0.80, 0.05, 0.10, 0.05]
yields['Crude 2'] = [0.44, 0.10, 0.36, 0.10]

print('\n', products)
print('\n', crudes)
print('\n', yields)
           Price  Max Production
Gasoline     72           24000
Kerosine     48            2000
Fuel Oil     42            6000
Residual     20          100000

          Processing Cost  Feed Costs
Crude 1              1.0          48
Crude 2              2.0          30

           Crude 1  Crude 2
Gasoline     0.80     0.44
Kerosine     0.05     0.10
Fuel Oil     0.10     0.36
Residual     0.05     0.10
price = {}
price['Gasoline'] = 72
price['Kerosine'] = 48
price
{'Gasoline': 72, 'Kerosine': 48}
products.loc['Gasoline','Price']
72
# model formulation
model = ConcreteModel()

# variables
model.x = Var(FEEDS,    domain=NonNegativeReals)
model.y = Var(PRODUCTS, domain=NonNegativeReals)

# objective
income = sum(products.ix[p, 'Price'] * model.y[p] for p in PRODUCTS)
raw_materials_cost = sum(crudes.ix[f,'Feed Costs'] * model.x[f] for f in FEEDS)
/Users/jeff/opt/anaconda3/lib/python3.7/site-packages/ipykernel_launcher.py:9: FutureWarning: 
.ix is deprecated. Please use
.loc for label based indexing or
.iloc for positional indexing

See the documentation here:
http://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#ix-indexer-is-deprecated
  if __name__ == '__main__':
/Users/jeff/opt/anaconda3/lib/python3.7/site-packages/ipykernel_launcher.py:10: FutureWarning: 
.ix is deprecated. Please use
.loc for label based indexing or
.iloc for positional indexing

See the documentation here:
http://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#ix-indexer-is-deprecated
  # Remove the CWD from sys.path while we load stuff.
processing_cost = sum(processing_costs[f] * model.x[f] for f in FEEDS)
profit = income - raw_materials_cost - processing_cost
model.objective = Objective(expr = profit, sense=maximize)

# constraints
model.constraints = ConstraintList()
for p in PRODUCTS:
    model.constraints.add(0 <= model.y[p] <= max_production[p])
for p in PRODUCTS:
    model.constraints.add(model.y[p] == sum(model.x[f] * product_yield[(f,p)] for f in FEEDS))

solver = SolverFactory('glpk')
solver.solve(model)
model.pprint()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-6-54aa9b28c7ab> in <module>
----> 1 processing_cost = sum(processing_costs[f] * model.x[f] for f in FEEDS)
      2 profit = income - raw_materials_cost - processing_cost
      3 model.objective = Objective(expr = profit, sense=maximize)
      4 
      5 # constraints

<ipython-input-6-54aa9b28c7ab> in <genexpr>(.0)
----> 1 processing_cost = sum(processing_costs[f] * model.x[f] for f in FEEDS)
      2 profit = income - raw_materials_cost - processing_cost
      3 model.objective = Objective(expr = profit, sense=maximize)
      4 
      5 # constraints

NameError: name 'processing_costs' is not defined
from pyomo.environ import *
import numpy as np

# problem data
FEEDS = ['Crude #1', 'Crude #2']
PRODUCTS = ['Gasoline', 'Kerosine', 'Fuel Oil', 'Residual']

# feed costs
feed_costs = {'Crude #1': 48,
              'Crude #2': 30}

# processing costs
processing_costs = {'Crude #1': 1.00,
                    'Crude #2': 2.00}

# yield data
product_yield = 
product_yield = {('Crude #1', 'Gasoline'): 0.80,
                 ('Crude #1', 'Kerosine'): 0.05,
                 ('Crude #1', 'Fuel Oil'): 0.10,
                 ('Crude #1', 'Residual'): 0.05,
                 ('Crude #2', 'Gasoline'): 0.44,
                 ('Crude #2', 'Kerosine'): 0.10,
                 ('Crude #2', 'Fuel Oil'): 0.36,
                 ('Crude #2', 'Residual'): 0.10}

# product sales prices
sales_price = {'Gasoline': 72,
               'Kerosine': 48,
               'Fuel Oil': 42,
               'Residual': 20}

# production limits
max_production = {'Gasoline': 24000,
                  'Kerosine': 2000,
                  'Fuel Oil': 6000,
                  'Residual': 100000}

# model formulation
model = ConcreteModel()

# variables
model.x = Var(FEEDS, domain=NonNegativeReals)
model.y = Var(PRODUCTS, domain=NonNegativeReals)

# objective
income = sum(sales_price[p] * model.y[p] for p in PRODUCTS)
raw_materials_cost = sum(feed_costs[f] * model.x[f] for f in FEEDS)
processing_cost = sum(processing_costs[f] * model.x[f] for f in FEEDS)

profit = income - raw_materials_cost - processing_cost
model.objective = Objective(expr = profit, sense=maximize)

# constraints
model.constraints = ConstraintList()
for p in PRODUCTS:
    model.constraints.add(0 <= model.y[p] <= max_production[p])
for p in PRODUCTS:
    model.constraints.add(model.y[p] == sum(model.x[f] * product_yield[(f,p)] for f in FEEDS))

solver = SolverFactory('glpk')
solver.solve(model)
model.pprint()
  File "<ipython-input-7-9d0bbdfd689e>", line 17
    product_yield =
                    ^
SyntaxError: invalid syntax
profit()
573517.2413793121
income()
2078344.8275862068
raw_materials_cost()
1464827.5862068948
processing_cost()
39999.99999999996

5.7.4. Example: Making Change#

One of the important modeling features of Pyomo is the ability to index variables and constraints. The

from pyomo.environ import pyo

def make_change(amount, coins):
    model = ConcreteModel()
    model.x = Var(coins.keys(), domain=NonNegativeIntegers)
    model.total = Objective(expr = sum(model.x[c] for c in coins.keys()), sense=minimize)
    model.amount = Constraint(expr = sum(model.x[c]*coins[c] for c in coins.keys()) == amount)
    SolverFactory('glpk').solve(model)
    return {c: int(model.x[c]()) for c in coins.keys()} 
            
# problem data
coins = {
    'penny': 1,
    'nickel': 5,
    'dime': 10,
    'quarter': 25
}
            
make_change(1034, coins)
{'dime': 0, 'nickel': 1, 'penny': 4, 'quarter': 41}

5.7.5. Example: Linear Production Model with Constraints with Duals#

from pyomo.environ import *
model = ConcreteModel()

# for access to dual solution for constraints
model.dual = Suffix(direction=Suffix.IMPORT)

# declare decision variables
model.x = Var(domain=NonNegativeReals)
model.y = Var(domain=NonNegativeReals)

# declare objective
model.profit = Objective(expr = 40*model.x + 30*model.y, sense = maximize)

# declare constraints
model.demand = Constraint(expr = model.x <= 40)
model.laborA = Constraint(expr = model.x + model.y <= 80)
model.laborB = Constraint(expr = 2*model.x + model.y <= 100)

# solve
SolverFactory('glpk').solve(model).write()
# ==========================================================
# = Solver Results                                         =
# ==========================================================
# ----------------------------------------------------------
#   Problem Information
# ----------------------------------------------------------
Problem: 
- Name: unknown
  Lower bound: 2600.0
  Upper bound: 2600.0
  Number of objectives: 1
  Number of constraints: 4
  Number of variables: 3
  Number of nonzeros: 6
  Sense: maximize
# ----------------------------------------------------------
#   Solver Information
# ----------------------------------------------------------
Solver: 
- Status: ok
  Termination condition: optimal
  Statistics: 
    Branch and bound: 
      Number of bounded subproblems: 0
      Number of created subproblems: 0
  Error rc: 0
  Time: 0.01401972770690918
# ----------------------------------------------------------
#   Solution Information
# ----------------------------------------------------------
Solution: 
- number of solutions: 0
  number of solutions displayed: 0
str = "   {0:7.2f} {1:7.2f} {2:7.2f} {3:7.2f}"

print("Constraint  value  lslack  uslack    dual")
for c in [model.demand, model.laborA, model.laborB]:
    print(c, str.format(c(), c.lslack(), c.uslack(), model.dual[c]))
Constraint  value  lslack  uslack    dual
demand      20.00    -inf   20.00    0.00
laborA      80.00    -inf    0.00   20.00
laborB     100.00    -inf    0.00   10.00