Product mix problem
considered here incorporates engineering bill of material structure in the
model. In actual situation, the flow of such system encompasses of input,
production and output. The input is typically, raw material capacity. The
production is a complex network of processes, workstations with equipment,
machines and human workforce. Since, we want to analyze a problem for product
mix without delving deep into the scheduling and machines, we would consider
processes as part of the production system.
Production system has
engineering bill of material that ties up raw material requirement for each of
end product. The concept of equivalent unit is used for each of the product
produced in sequential processes yielding different output amount. We will use minimum
capacity of each product among all the process as the daily capacity. We
compute the capacity in terms of both number of units and time. In the output
end we have demand and constraint imposed by market.
The production data represents crux of our problem. Each shift has four hundred and eighty minutes. There are three processes with planned downtime of eight percent, fourteen percent and twelve percent for each shift. The processing time for each product on each process is known with high accuracy. Along with these data, we have the bill of material matrix representing engineering requirement of each raw material for each end product. The next step is to compute equivalent units and minimum capacity which is the pre-processed data. The shift available time for each process is calculated by deducting downtime from the total available time. These time duration are then divided by respective processing time for product on the processes. The divided numbers are rounded down for accommodating practical consideration in production. The minimum capacity for each product is the minimum capacity among all the processes. The product with least capacity is the standard product and we compute equivalent units of other products.
The output data encompasses of daily demand for each product and market constraint for amount to be sold. Such market constraint might require producing minimum amount of a product or negative correlated demand for supplementary and positive correlated demand for complementary products. The product P3 and P2 are positively correlated and requires maintaining P3 product at least fifty units above P2.
The algebraic notations
for the problem are:
The formulation of
product mix problem is
The formulation involves
redundancies in certain calculation for process times. The objective is to
illustrate the approach for such problems.
Python Code:
(Youtube link:https://www.youtube.com/watch?v=0qlX2YVb4bM&t=3s)
# import required packages
from pulp import*
import pandas as pd
import math
# Input data lists and parameter
rawmtrl = ['RM1','RM2']
product = ['P1','P2','P3']
process = [1,2,3]
rmcap = [['RM1',30000],['RM2',40000]]
bom = [['RM1','P1',1],['RM1','P2',3],['RM1','P3',1],['RM2','P1',2],['RM2','P2',2],['RM2','P3',1]]
shifttime = 480
downtime = [[1,0.08],[2,0.14],[3,0.12]]
prodproctime = [['P1',1,0.3],['P1',2,0.4],['P1',3,0.4],['P2',1,0.3],['P2',2,0.2],['P2',3,0.4],['P3',1,0.4],['P3',2,0.5],['P3',3,0.4]]
demand = [['P1',800],['P2',300],['P3',400]]
profit = [['P1',5500],['P2',3000],['P3',2300]]
# Preprocessing of data for OR model
shiftprod = []
for i in downtime:
prod = shifttime * (1 - i[1])
shiftprod.append([i[0],prod])
prodproccap = []
for i in prodproctime:
for j in shiftprod:
if i[1] == j[0]:
c = math.floor(j[1]/i[2])
prodproccap.append([i[0],i[1],c])
prodproccapdf = pd.DataFrame(prodproccap,columns=['Product','Process','MinCapacity'])
prodproccapdf = prodproccapdf.groupby(['Product'], as_index=False)['MinCapacity'].min()
prodproccapdf = prodproccapdf.set_index(['Product'])
mincap = int(prodproccapdf.min())
equnitdf = mincap/prodproccapdf
equnitdf = equnitdf.rename(columns={'MinCapacity':'EqUnits'})
# Converting other input data into dataframe for OR model
rmcapdf = pd.DataFrame(rmcap,columns=['Raw Material','Capacity'])
rmcapdf = rmcapdf.set_index(['Raw Material'])
bomdf = pd.DataFrame(bom,columns=['Raw Material','Product','ReqUnits'])
bomdf = bomdf.set_index(['Raw Material','Product'])
prodproctimedf = pd.DataFrame(prodproctime,columns=['Product','Process','PT'])
prodproctimedf = prodproctimedf.set_index(['Product','Process'])
shiftproddf = pd.DataFrame(shiftprod,columns=['Process','ShiftTime'])
shiftproddf = shiftproddf.set_index(['Process'])
demanddf = pd.DataFrame(demand,columns=['Product','Demand'])
demanddf = demanddf.set_index(['Product'])
profitdf = pd.DataFrame(profit,columns=['Product','Profit'])
profitdf = profitdf.set_index(['Product'])
# Lists for OR model
rmprod = []
for i in rawmtrl:
for j in product:
rmprod.append([i,j])
prodproc = []
for j in product:
for k in process:
prodproc.append([j,k])
class ProductMixApplication:
def __init__(self,rawmtrl,product,process,rmprod,prodproc,rmcapdf,bomdf,equnitdf,mincap,prodproctimedf,shiftproddf,
demanddf,profitdf):
self.rawmtrl = rawmtrl
self.product = product
self.process = process
self.rmprod = rmprod
self.prodproc = prodproc
self.rmcapdf = rmcapdf
self.bomdf = bomdf
self.equnitdf = equnitdf
self.mincap = mincap
self.prodproctimedf = prodproctimedf
self.shiftproddf = shiftproddf
self.demanddf = demanddf
self.profitdf = profitdf
self.prob = LpProblem("Product Mix Application",LpMaximize)
self.x = LpVariable.dicts("Raw_Material_amount_variable", ((i) for i in self.rawmtrl),lowBound=0,cat='Integer')
self.y = LpVariable.dicts("Product_amount_variable", ((j) for j in self.product),lowBound=0,cat='Integer')
self.s = LpVariable.dicts("Idle_Time_variable", ((k) for k in self.process),lowBound=0,cat='Continuous')
self.prob += lpSum([self.profitdf.loc[(j),'Profit'] * self.y[j] for j in self.product])
# Raw material capacity constraint
for i in self.rawmtrl:
self.prob += self.x[i] <= self.rmcapdf.loc[(i),'Capacity']
# BOM requirement constraint
for i in self.rawmtrl:
self.prob += lpSum([self.y[j] * self.bomdf.loc[(a,j),'ReqUnits'] for a,j in self.rmprod if a == i]) == self.x[i]
# Production capacity constraint in equivalent units
self.prob += lpSum([self.y[j] * self.equnitdf.loc[(j),'EqUnits'] for j in self.product]) <= self.mincap
# Processing time constraint
for k in self.process:
self.prob += lpSum([self.prodproctimedf.loc[(j,a),'PT'] * self.y[j] for j,a in self.prodproc if a == k]) + self.s[k] == self.shiftproddf.loc[(k),'ShiftTime']
# Demand constraint
for j in self.product:
self.prob += self.y[j] <= self.demanddf.loc[(j),'Demand']
# Market constraint for correlated product demands
self.prob += self.y['P3'] - self.y['P2'] >= 50
def solve(self):
self.prob.solve()
self.prob.writeLP("ProductMixApplication.lp")
def status(self):
return LpStatus[self.prob.status]
def objective(self):
return value(self.prob.objective)
def returnVar(self):
var = []
for i,v in enumerate(self.prob.variables()):
var.append([v.name,v.varValue])
return var
mod = ProductMixApplication(rawmtrl,product,process,rmprod,prodproc,rmcapdf,bomdf,equnitdf,mincap,prodproctimedf,shiftproddf,
demanddf,profitdf)
mod.solve()
status = mod.status()
variables = mod.returnVar()
objective = mod.objective()



No comments:
Post a Comment