Friday, 1 September 2017

A primer for operations research formulation: Analysis of product mix problem with bill of material (BOM)


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:



# 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