3

I am trying to formulate some data into a PYOMO model for an optimization problem.

materials = ['steel', 'alum', 'carbon', 'cheese']

Each material has 2 properties - density and conductivity and their values are defined as follows.

density =   {   'steel' : 1.2,
            'alum'  : 0.8,
            'carbon': 1.8,
            'cheese': 0.7}

conductivity = {'steel' : 6.4,
               'alum'  : 3.1,
               'carbon': 4.4,
               'cheese': 0.3}

The objective function calculates the weight of 2 rectangular plates as given below:

Objective function = Area_1 * thickness_1 * density_1 + Area_2 * thickness_2 * density_2

Where, the Area_1, thickness_1, and density_1 are area, thickness and density of plate 1.

Area and thickness are fixed for each plates. But the density value depends on the material chosen by the solver to get the best results. The model also have a constraint defined as follows:

(conductivity_1/thickness_1) + (conductivity_2/thickness_2)  => 22

So, when the solver chooses a density value for a plate, it must also choose the conductivity value of the same material.

I would appreciate it if someone can help me with this problem. I also welcome if you have different ideas to solve this problem. Thank you.

Rua Goa
  • 127
  • 9
  • 1
    This is not much of an effort on figuring this out. You just recycled much of my example from your other question. Take a look at the pyomo dox or several of the other pyomo questions on this site for examples. I have posted several that use double indexing that might help you get started. Let's see a minimum functioning example update to this. Right now your question is too broad. – AirSquid Dec 09 '20 at 00:38
  • A good first step would be to determine how to mathematically represent your problem (what are your decision variables and how can you represent your constraints and objective function in terms of the decision variables and parameters?) – cookesd Dec 09 '20 at 13:26
  • @AirSquid Thank you for the information. I will try that way. – Rua Goa Dec 10 '20 at 11:32
  • @cookesd My decision variables are area, thickness, conductivity, and density. Among the 4 variables, area and thickness are fixed values for the plates. But, the conductivity and density are determined by the solver to obtain the best solution. So when the solver chooses a density value for a plate, it must also choose the conductivity value of the same material. Thank you. – Rua Goa Dec 10 '20 at 11:41

1 Answers1

2

Here is an example model that I think meets all of your questions.

Once you set up the second index to be the plates P = {1, 2, 3} in this case for 3 plates, then we need to double index our decision variable to represent the assignment of material m to plate p. In this example, 4 materials, 3 plates.

Many other variations of constraints are possible here, but the ones I added answer your question about conductivity in aggregate. Note that I have also added a constraint to ensure that 1 and only 1 material is assigned to each plate. You may/may not need this depending on other constraints in your model, but it is good insurance against bogus answers. This is also an example of the "for every" style of constraint using the function - rule combo in pyomo.

The result... an aluminum and cheese sandwich... :)

# material selection model

import pyomo.environ as pyo

# data
materials = ['steel', 'alum', 'carbon', 'cheese']

density =   {   'steel' : 1.2,
                'alum'  : 0.8,
                'carbon': 1.8,
                'cheese': 0.7}

conductivity = {'steel' : 40.8,
                'alum'  : 30.1,
                'carbon': 42.4,
                'cheese': 15.3}

price =     {   'steel' : 2.3,
                'alum'  : 3.5,
                'carbon': 5.8,
                'cheese': 6.0}

                  # t     area
plate_dims = {  1: (10,   150), 
                2: (12.5, 200),
                3: (8,    125)}

mdl = pyo.ConcreteModel('material selector')

# SETS (used to index the decision variable and the parameters)
mdl.M = pyo.Set(initialize=materials)
mdl.P = pyo.Set(initialize=plate_dims.keys())

# VARIABLES
mdl.x = pyo.Var(mdl.M, mdl.P, domain=pyo.Binary)  # select material M for plate P

# PARAMETERS
mdl.density =       pyo.Param(mdl.M, initialize=density)
mdl.conductivity =  pyo.Param(mdl.M, initialize=conductivity)
mdl.price =         pyo.Param(mdl.M, initialize=price)
mdl.p_thickness =   pyo.Param(mdl.P, initialize= {k:v[0] for k,v in plate_dims.items()})
mdl.p_area =        pyo.Param(mdl.P, initialize= {k:v[1] for k,v in plate_dims.items()})

# OBJ (minimize total density)
mdl.obj = pyo.Objective(expr=sum(mdl.x[m, p] * mdl.p_thickness[p] 
                        * mdl.p_area[p] * mdl.density[m] 
                        for m in mdl.M for p in mdl.P))

# CONSTRAINTS
# minimum conductivity
mdl.c1 = pyo.Constraint(expr=sum(mdl.x[m, p] * mdl.conductivity[m]/mdl.p_thickness[p]
                        for m in mdl.M for p in mdl.P) >= 5.0)

# must populate all plates with 1 material
def c2(model, plate):
    return sum(mdl.x[m, plate] for m in mdl.M) == 1
mdl.c2 = pyo.Constraint(mdl.P, rule=c2)

# solve it
solver = pyo.SolverFactory('glpk')
result = solver.solve(mdl)
mdl.display()

Yields:

Model material selector

  Variables:
    x : Size=12, Index=x_index
        Key           : Lower : Value : Upper : Fixed : Stale : Domain
          ('alum', 1) :     0 :   0.0 :     1 : False : False : Binary
          ('alum', 2) :     0 :   0.0 :     1 : False : False : Binary
          ('alum', 3) :     0 :   1.0 :     1 : False : False : Binary
        ('carbon', 1) :     0 :   0.0 :     1 : False : False : Binary
        ('carbon', 2) :     0 :   0.0 :     1 : False : False : Binary
        ('carbon', 3) :     0 :   0.0 :     1 : False : False : Binary
        ('cheese', 1) :     0 :   1.0 :     1 : False : False : Binary
        ('cheese', 2) :     0 :   1.0 :     1 : False : False : Binary
        ('cheese', 3) :     0 :   0.0 :     1 : False : False : Binary
         ('steel', 1) :     0 :   0.0 :     1 : False : False : Binary
         ('steel', 2) :     0 :   0.0 :     1 : False : False : Binary
         ('steel', 3) :     0 :   0.0 :     1 : False : False : Binary

  Objectives:
    obj : Size=1, Index=None, Active=True
        Key  : Active : Value
        None :   True : 3600.0

  Constraints:
    c1 : Size=1
        Key  : Lower : Body              : Upper
        None :   5.0 : 6.516500000000001 :  None
    c2 : Size=3
        Key : Lower : Body : Upper
          1 :   1.0 :  1.0 :   1.0
          2 :   1.0 :  1.0 :   1.0
          3 :   1.0 :  1.0 :   1.0
AirSquid
  • 10,214
  • 2
  • 7
  • 31
  • I have a question on your use of Sets and Params. Have you seen any advantage of using them in concrete models instead of having the model data in other python objects (lists, numpy arrays, pandas dataframes)? I generally use the latter, but I wasn't sure if I'm missing out on functionality – cookesd Dec 10 '20 at 19:15
  • @AirSquid Thank you very much for the detailed explanation and ideas. I have few doubts with your code. I would like to seek your advice for the same. I am sorry if am wrong with my questions. 1) mdl.x is defined as a binary variable. In fact, how does it help choosing a material from the set? The same variable is used in the objective function and constraint by multiplying with the variables. Could you please explain how it makes a meaning? – Rua Goa Dec 10 '20 at 21:33
  • 2) The objective function seems the summation of the expression for all combination of plates and materials. We are supposed to choose among the options. The same applies for the constraint. Kindly clarify these doubts. Thank you! – Rua Goa Dec 10 '20 at 21:34
  • 1
    @cookesd I don't know about missing out on too much by not putting the params and sets into the model, I just think it is easier to troubleshoot (especially with the sets) in the model and using the `mdl.pprint()` and see *everything* is nice and to declare subsets & sparse sets `within` larger sets is good for error detection too. – AirSquid Dec 10 '20 at 23:20
  • 1
    @RuaGoa I think you need to find some tutorial material or a text on linear programming/integer programming as you seem to not have a pyomo question, but basic questions on math programming. This is how it is done and the use of a binary variable is correct. Write out the math and check it. The solver engine will select 1/0 values for `x[m, p]` to minimize the objective. If you want to see the expression of the objective, you can add the line `mdl.obj.pprint()` to the code and see the expression and plug in binary values to see the math. – AirSquid Dec 10 '20 at 23:25
  • @AirSquid Thank you for your extended help and kindness. I am new to programming, and I am constantly improving. Your answer will do a lot to my project. – Rua Goa Dec 10 '20 at 23:59
  • 1
    @AirSquid And, could you please explain how to print the minimum value obtained for each plates from the objective function? – Rua Goa Dec 11 '20 at 20:27
  • 2
    @AirSquid I am skeptic about the "sum()" command in the expression. Does it mean the summation of all the combinations or it simply means an array? – Rua Goa Dec 11 '20 at 21:01