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