Commit 43bc660c authored by Georgios Dagkakis's avatar Georgios Dagkakis

allocation routine added to simulation route

parent 5c41dfd5
'''
Created on 21 Aug 2013
@author: Anna
'''
import math
import numpy
import random
from Globals import G
class Allocation():
def __init__(self, itemList, week, altRoutes, excBuffer):
self.week = week
self.altRoutes = altRoutes
self.itemList = itemList
self.excBuffer = excBuffer
def Run(self):
for CurrentMA in self.itemList:
# call the allocation methods based on the step (e.g. allocation on same route or allocation on alternative routes)
if self.altRoutes == 1:
self.alternativeRoutes(CurrentMA)
else:
self.allocationStd(CurrentMA)
# put items in output buffer (temporary buffer for excess units to be allocated)
if CurrentMA.qty > 0:
self.excBuffer.append(CurrentMA)
# allocate item on its own route
def allocationStd(self, MA):
sufficient=True #flag that shows if we have sufficient capacity
# read the capacity that the MA requires
requiredCapacity={}
for x in G.RouteDict[MA.MAid]['route']:
requiredCapacity[x]=G.RouteDict[MA.MAid]['route'][x]*MA.qty
print '-'*100
print 'MA',MA.MAid,'week',self.week
print 'Quantity to allocate=', MA.qty
print 'required',requiredCapacity
print 'available', G.CurrentCapacityDict
# read the remaining capacity for thegiven week and subtract the required from it
remainingCapacity={}
for bottleneck in G.CurrentCapacityDict:
remainingCapacity[bottleneck]=G.CurrentCapacityDict[bottleneck][self.week]-requiredCapacity[bottleneck]
# if we dropped below zero then the capacity is not sufficient
if remainingCapacity[bottleneck]<0:
sufficient=False
#remainingCapacity = numpy.array(G.currentCapacity[self.week]) - numpy.array(requiredCapacity)
#remainingCapacity = remainingCapacity.tolist()
print 'remaining',remainingCapacity
print sufficient
# check if there is sufficient capacity to process the order
if sufficient:
# update remaining capacity
allocableQty = MA.qty
if MA.qty >= G.minPackingSize:
for bottleneck in G.CurrentCapacityDict:
G.CurrentCapacityDict[bottleneck][self.week]=remainingCapacity[bottleneck]
print 'allocation performed fully! remaining:'
print G.CurrentCapacityDict
# if the capacity available is not sufficient, the max allocable qty is derived
else:
# calculate max qty allocable
#excessUnits = [0 for i in range(len(requiredCapacity))]
excessUnits={}
excess=0
for bottleneck in remainingCapacity:
if requiredCapacity[bottleneck]>0 and remainingCapacity[bottleneck]<0:
excessUnits= remainingCapacity[bottleneck]/G.RouteDict[MA.MAid]['route'][bottleneck]
if math.ceil(math.fabs(excessUnits))>excess:
excess = math.ceil(math.fabs(excessUnits))
print 'excess', excess
# for i in range(len(remainingCapacity)):
# if requiredCapacity[i]>0 and remainingCapacity[i]<0:
# excessUnits[i] = remainingCapacity[i]/G.route[MA.MAid][i]
# excess = math.ceil(math.fabs(min(excessUnits)))
# update remaining capacity
assert(excess <= MA.qty or MA.qty < G.minPackingSize)
allocableQty = MA.qty - excess
print 'MA quantity', MA.qty
print 'allocableQty', allocableQty
if allocableQty >= G.minPackingSize:
#rCap = numpy.array(G.currentCapacity[self.week]) - numpy.multiply(allocableQty,G.route[MA.MAid])
for bottleneck in G.CurrentCapacityDict:
G.CurrentCapacityDict[bottleneck][self.week]-=allocableQty*G.RouteDict[MA.MAid]['route'][bottleneck]
print 'allocation performed partially! remaining:'
print G.CurrentCapacityDict
# update attributes/variables affected by allocation
if allocableQty >= G.minPackingSize:
MA.qty -= allocableQty
MA.minQty = max([0, MA.minQty - allocableQty])
# update allocation output variable
# distinguish case of FutureDemand from PPOSdemand
if MA.future == 1:
G.AllocationFuture[G.replication].append([MA.orderID, MA.MAid, allocableQty, self.week+1])
G.FutureLateness[G.replication] += max([0, self.week - MA.originalWeek])*allocableQty
G.FutureEarliness[G.replication] += max([0, MA.originalWeek - self.week])*allocableQty
else:
G.AllocationPPOS[G.replication].append([MA.orderID, MA.MAid, allocableQty, self.week+1])
G.PPOSLateness[G.replication] += max([0, self.week - MA.originalWeek])*allocableQty
G.PPOSEarliness[G.replication] += max([0, MA.originalWeek - self.week])*allocableQty
print '-'*100
def alternativeRoutes(self, MA):
sufficient=False #flag that shows if we have sufficient capacity
print '='*100
print 'MA',MA.MAid,'week',self.week
print 'Quantity to allocate=', MA.qty
# identify MAs with the same SP as the MA investigated
alternativeMADict={} #FIXME: the PPOS attribute can be used instead for the current MA
# loop through the MAinfo
for alernativeMA in G.RouteDict:
# if it is the same MA do not consider it
if alernativeMA==MA.MAid:
continue
# if the alternative MA is of the same SP add it to the list
PPOS=G.RouteDict[alernativeMA]['PPOS']
SP=G.RouteDict[alernativeMA]['SP']
if PPOS==MA.PPOSid and SP==MA.SPid:
alternativeMADict[alernativeMA]=G.RouteDict[alernativeMA]
print 'alternativeRoutes',alternativeMADict
print 'available',G.CurrentCapacityDict
# calculate max number of units for each alternative MA
maxUnits = {}
for alternativeMA in alternativeMADict:
MAunits=[]
for routeElement in alternativeMADict[alternativeMA]['route']:
units=alternativeMADict[alternativeMA]['route'][routeElement]
if units!= 0:
MAunits.append(G.CurrentCapacityDict[routeElement][self.week]/units)
sufficient=True
maxUnits[alternativeMA]=math.floor(min(MAunits))
print 'print units that can be allocated in alternative routes:', maxUnits
# choose MA with max number of units
if maxUnits and sufficient:
maxU=0
maxID=[]
for MAid in maxUnits:
if maxUnits[MAid]>maxU:
maxU=maxUnits[MAid]
maxID = [MAid]
if maxUnits[MAid]==maxU:
maxID.append(MAid)
# choose MA randomly among those with max number of units
chosenMAId = random.choice(maxID)
print 'chose to allocate in MA with id =', chosenMAId
allocableQty = min([maxU, MA.qty])
print 'in this route we can allocate ',allocableQty
if allocableQty >= G.minPackingSize:
for bottleneck in G.CurrentCapacityDict:
G.CurrentCapacityDict[bottleneck][self.week]-=allocableQty*G.RouteDict[chosenMAId]['route'][bottleneck]
print 'allocation performed in the alternative route! remaining:'
print G.CurrentCapacityDict
# update attributes/variables affected by allocation
MA.qty -= allocableQty
MA.minQty = max([0, MA.minQty - allocableQty])
# update allocation output variable
# distinguish case of FutureDemand from PPOSdemand
if MA.future == 1:
G.AllocationFuture[G.replication].append([MA.orderID, chosenMAId, allocableQty, self.week+1])
G.FutureLateness[G.replication] += max([0, self.week - MA.originalWeek])*allocableQty
G.FutureEarliness[G.replication] += max([0, MA.originalWeek - self.week])*allocableQty
else:
G.AllocationPPOS[G.replication].append([MA.orderID, chosenMAId, allocableQty, self.week+1])
G.PPOSLateness[G.replication] += max([0, self.week - MA.originalWeek])*allocableQty
G.PPOSEarliness[G.replication] += max([0, MA.originalWeek - self.week])*allocableQty
print '='*100
\ No newline at end of file
'''
Created on 3 Oct 2013
@author: Anna
Basic implementation: runs the allocation routine for the future demand first (one week at a time for the whole planning horizon) and the
PPOS after
Equivalent to M2 in MATLAB functions
'''
import xlwt
import xlrd
from AllocationRoutine import AllocationRoutine
from CoreObject import CoreObject
from Globals import G
from SimPy.Simulation import hold
from ObjectInterruption import ObjectInterruption
from FutureDemandCreator import FutureDemandCreator
class AllocationManagement(ObjectInterruption):
def __init__(self, id=id, name=None, argumentDict={}):
ObjectInterruption.__init__(self)
self.id=id
self.name=name
self.argumentDict=argumentDict #the arguments of the method given in a dict
self.readData()
self.FDC=FutureDemandCreator()
def run(self):
self.FDC.run()
yield hold,self,0
for kWeek in range(int(G.maxSimTime)):
# activate allocation procedure for future items at target week
procedureFuture = AllocationRoutine(initialWeek=kWeek, itemType=1)
procedureFuture.Run()
# activate allocation procedure for PPOS items at target week
procedurePPOS = AllocationRoutine(initialWeek=G.TargetPPOSweek, itemType=0)
procedurePPOS.Run()
G.reCapacity.append(G.currentCapacity)
self.writeOutput()
def readData(self):
G.CapacityDict=self.argumentDict['capacity']
G.CurrentCapacityDict=G.CapacityDict
G.RouteDict=self.argumentDict['MAList']
G.TargetPPOS=self.argumentDict['currentPPOS']['id']
G.TargetPPOSqty=self.argumentDict['currentPPOS']['quantity']
G.TargetPPOSweek=self.argumentDict['currentPPOS']['targetWeek']
G.maxEarliness=self.argumentDict['allocationData']['maxEarliness']
G.maxLateness=self.argumentDict['allocationData']['maxLateness']
G.minPackingSize=self.argumentDict['allocationData']['minPackingSize']
def writeOutput(self):
wbin = xlwt.Workbook()
for k in range(G.ReplicationNo):
#export info on lateness
sheet1=wbin.add_sheet('Lateness'+str(k+1))
sheet1.write(0,0,'replication')
sheet1.write(0,1,k+1)
sheet1.write(2,0,'PPOS Lateness')
sheet1.write(2,1,G.PPOSLateness[k])
sheet1.write(3,0,'PPOS Earliness')
sheet1.write(3,1,G.PPOSEarliness[k])
sheet1.write(1,3,'Unconstrained Excess Units')
sheet1.write(1,4,'Min Excess Units')
excessPPOS = sum([i.qty for i in G.ExcessPPOSBuffer[k]])
minExcessPPOS = sum([i.qty for i in G.ExcessPPOSminBuffer[k]])
sheet1.write(2,3,excessPPOS)
sheet1.write(2,4, minExcessPPOS)
print 'excess future', [i.orderID for i in G.ExcessFutureBuffer[k]], [i.qty for i in G.ExcessFutureBuffer[k]]
print 'excess ppos', [i.orderID for i in G.ExcessPPOSBuffer[k]], [i.qty for i in G.ExcessPPOSBuffer[k]]
excessFuture = sum([i.qty for i in G.ExcessFutureBuffer[k]])
minExcessFuture = sum([i.qty for i in G.ExcessFutureMinBuffer[k]])
sheet1.write(1,6,'% Unconstrained Excess')
sheet1.write(1,7,'% Min Excess')
sheet1.write(4,3,excessFuture)
sheet1.write(4,4,minExcessFuture)
sheet1.write(4,0,'Future Demand Lateness')
sheet1.write(4,1,G.FutureLateness[k])
sheet1.write(5,0,'Future Demand Earliness')
sheet1.write(5,1,G.FutureEarliness[k])
# Export PPOS/Future allocation Results
for z in range(2):
if z==0:
shName = 'PPOSAllocation'+str(k+1)
itemName = 'Initial PPOS Demand Disaggregation'
profile = G.PPOSprofile[k]
alloc = G.AllocationPPOS[k]
else:
shName = 'FutureAllocation'+str(k+1)
itemName = 'Initial Future Demand Disaggregation'
profile = G.FutureProfile[k]
alloc = G.AllocationFuture[k]
sheet = wbin.add_sheet(shName)
sheet.write_merge(0,0,0,4,itemName)
sheet.write(1,0,'Order ID')
sheet.write(1,1,'MA ID')
sheet.write(1,2,'Total # Units')
sheet.write(1,3,'Min # Units')
sheet.write(1,4,'Planned Week')
for i in range(len(profile)):
for j in range(len(profile[i])):
sheet.write(i+2,j,profile[i][j])
totQty = sum([i[2] for i in profile])
if z==0:
#pposQty = totQty
sheet1.write(2,6,excessPPOS*100.0/totQty)
sheet1.write(2,7,minExcessPPOS*100.0/totQty)
else:
sheet1.write(4,6,excessFuture*100.0/totQty)
sheet1.write(4,7,minExcessFuture*100.0/totQty)
print 'allocation', alloc
counterCols = [5 for i in range(len(profile))]
# TODO the below crashes, got to check
for i in range(len(alloc)):
for j in range(3):
sheet.write(alloc[i][0]+2,counterCols[alloc[i][0]]+j,alloc[i][j+1])
counterCols[alloc[i][0]] += 3
attempts = (max(counterCols)-5)/3
for i in range(attempts):
sheet.write_merge(0,0,5+(i*3),5+(i*3)+2,'Allocation Attempt No.'+str(i+1))
sheet.write(1,5+(i*3),'MA ID')
sheet.write(1,5+(i*3)+1,'# Allocated Units')
sheet.write(1,5+(i*3)+2,'Week')
# Excess units
for z in range(2):
for y in range(2):
if z==0:
if y == 0:
shName = 'PPOSExcess'+str(k+1)
buf = G.ExcessPPOSBuffer[k]
else:
shName = 'PPOSminExcess'+str(k+1)
buf = G.ExcessPPOSminBuffer[k]
else:
if y == 0:
shName = 'FutureExcess'+str(k+1)
buf = G.ExcessFutureBuffer[k]
else:
shName = 'FutureMinExcess'+str(k+1)
buf = G.ExcessFutureMinBuffer[k]
row = 1
if len(buf):
sheet = wbin.add_sheet(shName)
sheet.write(0,0,'Order ID')
sheet.write(0,1,'MA ID')
sheet.write(0,2,'excess Units')
for i in buf:
sheet.write(row,0,i.orderID+1)
sheet.write(row,1,i.MAid)
sheet.write(row,2,i.qty)
row +=1
# remaining capacity
sheet = wbin.add_sheet('Capacity'+str(k+1))
sheet.write_merge(0,0,1,G.planningHorizon,'Weeks')
for i in range(G.planningHorizon):
sheet.write(1,i+1,i+1)
sheet.write_merge(0,1,0,0,'Bottlenecks')
i=2
for record in G.CurrentCapacityDict:
sheet.write(i,0,record)
sheet.write(i,1,G.CurrentCapacityDict[record][0])
sheet.write(i,2,G.CurrentCapacityDict[record][1])
sheet.write(i,3,G.CurrentCapacityDict[record][2])
i+=1
wbin.save("allocationManagement.xls") # temporary have a file for verification
'''
Created on 5 Sep 2013
@author: Anna
'''
from Globals import G
from Allocation import Allocation
from JobMA import JobMA
class AllocationRoutine():
def __init__(self, initialWeek, itemType):
self.initialWeek = initialWeek
self.itemType = itemType
self.week = self.initialWeek
self.internalBuffer = []
self.minBuffer = []
def Run(self):
#verify if there is any item to be allocated at self.weeek (originally)
noItems = self.checkNumberOfItems()
# if there are items of that week, start the allocation routine
if noItems:
#====================================
# step 1: same route, same week
#====================================
# get all the items of self.week and activate the allocation process
#G.filterItem = self.itemType
#G.filterWeek = self.week
#itemsToBeAssigned = filterWeek(G.Buffer[G.replication])
#itemsToBeAssigned = [x for x in G.Buffer[G.replication] if (x.originalWeek == self.week and x.future == self.itemType)]
itemsToBeAssigned = self.filterWeek(G.Buffer[G.replication])
assert len(itemsToBeAssigned) == noItems
sameRouteSameWeek = Allocation(itemList=itemsToBeAssigned, week=self.week, altRoutes=0, excBuffer=self.internalBuffer)
sameRouteSameWeek.Run()
#==========================================
# step 2: same route, previous weeks
#==========================================
# proceed only if there are excess items
self.week -= 1
while self.week >= 0 and (self.initialWeek - self.week) <= G.maxEarliness and len(self.internalBuffer):
itemsToBeAssigned = self.internalBuffer
self.internalBuffer = []
sameRoutePreviousWeeks = Allocation(itemList=itemsToBeAssigned, week=self.week, altRoutes=0, excBuffer=self.internalBuffer)
sameRoutePreviousWeeks.Run()
self.week -= 1
#===============================================================
# step 3: separate min quantity of excess demand from unconstrained qty
#===============================================================
# proceed only if there are excess items
if len(self.internalBuffer):
for item in self.internalBuffer:
# if the item presents min qty then create a new item and store it into minBuffer
if item.minQty:
newJob = JobMA(item.orderID, item.MAid, item.SPid, item.PPOSid, item.minQty, item.minQty, item.originalWeek, item.future)
self.minBuffer.append(newJob)
item.qty = item.qty - item.minQty
item.minQty = 0
if item.qty == 0:
self.internalBuffer.remove(item)
#============================================
# step 4: allocate min qty to later weeks
#============================================
self.week = self.initialWeek + 1
while self.week < G.planningHorizon and (self.week - self.initialWeek) <= G.maxLateness and len(self.minBuffer):
itemsToBeAssigned = self.minBuffer
self.minBuffer = []
sameRouteLaterWeeks = Allocation(itemList=itemsToBeAssigned, week=self.week, altRoutes=0, excBuffer=self.minBuffer)
sameRouteLaterWeeks.Run()
self.week += 1
# any excess items left in the minBuffer should be transferred into the global minBuffer
if len(self.minBuffer):
if self.itemType == 1:
G.ExcessFutureMinBuffer[G.replication] = self.minBuffer
else:
G.ExcessPPOSminBuffer[G.replication] = self.minBuffer
self.minBuffer = []
#========================================
# step 5: alternative route, same week
#========================================
self.week = self.initialWeek
if len(self.internalBuffer):
itemsToBeAssigned = self.internalBuffer
self.internalBuffer = []
altRouteSameWeek = Allocation(itemList=itemsToBeAssigned, week=self.week, altRoutes=1, excBuffer=self.internalBuffer)
altRouteSameWeek.Run()
#==============================================
# step 6: alternative route, previous weeks
#==============================================
self.week = self.initialWeek - 1
while self.week >= 0 and (self.initialWeek - self.week) <= G.maxEarliness and len(self.internalBuffer):
itemsToBeAssigned = self.internalBuffer
self.internalBuffer = []
altRoutePreviousWeeks = Allocation(itemList=itemsToBeAssigned, week=self.week, altRoutes=1, excBuffer=self.internalBuffer)
altRoutePreviousWeeks.Run()
self.week -= 1
#===================================================
# step 7: later weeks, same and alternative routes
#===================================================
self.week = self.initialWeek + 1
while self.week < G.planningHorizon and (self.week - self.initialWeek) <= G.maxLateness and len(self.internalBuffer):
# same route
itemsToBeAssigned = self.internalBuffer
self.internalBuffer = []
sameRouteLaterWeeks = Allocation(itemList=itemsToBeAssigned, week=self.week, altRoutes=0, excBuffer=self.internalBuffer)
sameRouteLaterWeeks.Run()
if len(self.internalBuffer):
itemsToBeAssigned = self.internalBuffer
self.internalBuffer = []
sameRouteLaterWeeks = Allocation(itemList=itemsToBeAssigned, week=self.week, altRoutes=1, excBuffer=self.internalBuffer)
sameRouteLaterWeeks.Run()
self.week += 1
#================================================
# transfer excess items into global buffers
#================================================
if len(self.internalBuffer):
if self.itemType == 1:
G.ExcessFutureBuffer[G.replication] = self.internalBuffer
print 'end allocation routine - excessBuffer', [i.orderID for i in G.ExcessFutureBuffer[G.replication]], [i.qty for i in G.ExcessFutureBuffer[G.replication]]
else:
G.ExcessPPOSBuffer[G.replication] = self.internalBuffer
self.internalBuffer = []
# go through the initial buffer and counts the number of items to be assigned at self.week
def checkNumberOfItems(self):
counter = 0
for item in G.Buffer[G.replication]:
if item.originalWeek == self.week and item.future == self.itemType:
counter += 1
return counter
def filterWeek(self,buf):
result = [x for x in buf if (x.originalWeek == self.week and self.itemType == x.future)]
return result
# filter function to get items of a specific week (self.week) from the initial buffer
#def filterWeek(buf):
#
# result = [x for x in buf if (x.originalWeek == G.filterWeek and G.filterItem == x.future)]
# return result
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment