3.3 Organization Model Implementation

This section contains the complete descriptions of the implementations related to the proposed maintenance organization model presented in Section 2.3.

3.3.1 Organization Model Example

This section contains the code listing for the example organization model discussed in Section 2.3.3.

  #!/usr/bin/python
  
  import OrgModel
  
  # Simulation setup
  class ComponentTypes:
      A = OrgModel.EquipmentComponentType("A", 200, 1.5, 2, 10000)
      B = OrgModel.EquipmentComponentType("B", 400, 1.5, 4, 5000)
      C = OrgModel.EquipmentComponentType("C", 600, 3.0, 6, 40000)
  
  class UnitTypes:
      ARCHER = OrgModel.EquipmentUnitType("ARCHER",
                                          [(1, ComponentTypes.A),
                                           (2, ComponentTypes.B),
                                           (2, ComponentTypes.C)])
  
  g_evaluationScore = OrgModel.EvaluationScore()
  g_components = OrgModel.EquipmentComponentObjects(g_evaluationScore)
  g_units = OrgModel.EquipmentUnitObjects(g_evaluationScore,g_components)
  
  # Setup for downstream location 1
  policyDict1 = {
      ComponentTypes.A : { "Max Stock Level" : 5,
                           "Reorder Level"   : 2,
                           "Preventive Hazard Threshold" : 0.008},
      ComponentTypes.B : { "Max Stock Level" : 5,
                           "Reorder Level"   : 2,
                           "Preventive Hazard Threshold" : 0.008},
      ComponentTypes.C : { "Max Stock Level" : 5,
                           "Reorder Level"   : 2,
                           "Preventive Hazard Threshold" : 0.005} }
  
  policy = OrgModel.MaintenancePolicyObject(policyDict1)
  
  units = [ g_units.new(UnitTypes.ARCHER),
            g_units.new(UnitTypes.ARCHER),
            g_units.new(UnitTypes.ARCHER),
            g_units.new(UnitTypes.ARCHER) ]
  
  maintainers = [ OrgModel.MaintainerAgent("M1", g_evaluationScore, 200),
                  OrgModel.MaintainerAgent("M2", g_evaluationScore, 200) ]
  
  spares = []
  for k,v in policyDict1.iteritems():
      n = v["Max Stock Level"]
      while n > 0:
          spares.append(g_components.new(k))
          n -= 1
  
  location1 = OrgModel.LocationManagerAgent("L1",
                                            g_evaluationScore,
                                            spares,
                                            units,
                                            maintainers,
                                            policy,
                                            1.0, 10.0)
  
  # Setup for downstream location 2
  policyDict2 = {
      ComponentTypes.A : { "Max Stock Level" : 5,
                           "Reorder Level"   : 2,
                           "Preventive Hazard Threshold" : 0.008},
      ComponentTypes.B : { "Max Stock Level" : 5,
                           "Reorder Level"   : 2,
                           "Preventive Hazard Threshold" : 0.008},
      ComponentTypes.C : { "Max Stock Level" : 5,
                           "Reorder Level"   : 2,
                           "Preventive Hazard Threshold" : 0.005} }
  
  units = [ g_units.new(UnitTypes.ARCHER),
            g_units.new(UnitTypes.ARCHER) ]
  
  maintainers = [ OrgModel.MaintainerAgent("M3", g_evaluationScore, 200) ]
  
  policy = OrgModel.MaintenancePolicyObject(policyDict2)
  spares = []
  for k,v in policyDict2.iteritems():
      n = v["Max Stock Level"]
      while n > 0:
          spares.append(g_components.new(k))
          n -= 1
  location2 = OrgModel.LocationManagerAgent("L2",
                                            g_evaluationScore,
                                            spares,
                                            units,
                                            maintainers,
                                            policy,
                                            1.0, 10.0)
  
  
  # Setup for upstream location 
  spares = []
  for t in [ComponentTypes.A,ComponentTypes.B,ComponentTypes.C]:
      for i in range(policyDict1[t]["Max Stock Level"] +
                     policyDict2[t]["Max Stock Level"], 30):
          spares.append(g_components.new(t))
  upstream_loc = OrgModel.LocationManagerAgent("L0", g_evaluationScore, spares)
  
  
  # Distribution Channels
  channel1 = OrgModel.DistributionChannelObject("DC1",
                                                g_evaluationScore,
                                                upstream_loc,
                                                location1,
                                                72.0,1000)
  channel2 = OrgModel.DistributionChannelObject("DC2",
                                                g_evaluationScore,
                                                upstream_loc,
                                                location2,
                                                2.0,100)
  
  # Simulation loop
  t = 0
  while t < 720:
      dt = 1.0
      location1.step(dt)
      location2.step(dt)
      upstream_loc.step(dt)
      channel1.step(dt)
      channel2.step(dt)
      t += 1
  
  # Print results
  print "Spares status:"
  for l in [upstream_loc,location1,location2]:
      print l.toStr()
  for c in [channel1,channel2]:
      print c.toStr()
  
  print g_evaluationScore.toStr()
  print "Done after %d iterations"%(t,)

3.3.2 Organization Model Framework

This section contains the code listing for the proof of concept maintenance organization model framework produced to examine the feasibility of the proposed maintenance organization model discussed in 2.3.2.

  import random
  
  ###################################################################
  # Maintenance policy object
  ###################################################################
  
  class MaintenancePolicyObject:
      def __init__(self, policy):
          self.policyDict = policy
  
      def getMaxStockLevel(self, componentType):
          return self.policyDict[componentType][
              "Max Stock Level"]
  
      def getReorderLevel(self, componentType):
          return self.policyDict[componentType][
              "Reorder Level"]
  
      def getPrevThreshold(self, componentType):
          return self.policyDict[componentType][
              "Preventive Hazard Threshold"]
  
      def keys(self):
          return self.policyDict.keys()
  
  
  ###################################################################
  # Equipment component related classes 
  ###################################################################
  
  class EquipmentComponentType:
      def __init__(self, label, scale, shape, MTTR, cost):
          self.scale = float(scale)
          self.shape = float(shape)
          self.MTTR  = float(MTTR)
          self.cost  = cost
          self.label = label
  
      def getHazardRate(self, age):
          return ((self.shape/self.scale) *
                  pow(age/self.scale,self.shape-1))
  
      def getMTTR(self):
          return self.MTTR
  
  class EquipmentComponentObject:
      def __init__(self, id, evaluationScore, componentType, age=0):
          self.id = id
          self.evaluationScore = evaluationScore
          self.componentType = componentType
          self.age = float(age)
          self.functioning = True
          self.used = False
  
      def step(self, time):
          if not self.used:
              self.used = True
              self.evaluationScore.addComponentCost(
                  self.componentType.cost)
          if (self.functioning):
              if (pow(0.5,
                      self.componentType.getHazardRate(self.age) *
                      float(time)) <
                  random.random()):
                  self.functioning = False
              self.age += time
  
      def isOk(self):
          return self.functioning
  
      def getType(self):
          return self.componentType
  
      def getTTR(self):
          return self.componentType.getMTTR()
  
      def getHazardRate(self):
          return self.componentType.getHazardRate(self.age)
  
      def toStr(self):
          return "%s(%d)"%(self.componentType.label,self.id)
  
  class EquipmentComponentContainer(object):
      def __init__(self):
          self.reset()
  
      def reset(self):
          self.components = dict()
          self.componentsByType = dict()
  
      def addComponent(self, component):
          if not self.hasComponent(component.id):
              self.components[component.id] = component
              if not component.getType() in self.componentsByType:
                  self.componentsByType[component.getType()] = []
              self.componentsByType[
                  component.getType()].append(component)
  
      def getComponent(self, componentId):
          return self.components[componentId]
  
      def getComponents(self):
          return self.components.values()
  
      def getComponentsByType(self, componentType):
          if self.hasComponentType(componentType):
              return self.componentsByType[componentType]
          return []
  
      def hasComponent(self, componentId):
          return componentId in self.components
  
      def hasComponentType(self, componentType):
          return componentType in self.componentsByType
  
      def removeComponent(self, component):
          cT = component.getType()
          self.componentsByType[cT].remove(component)
          if (len(self.componentsByType[cT]) == 0):
              del self.componentsByType[cT]
          del self.components[component.id]
  
  ###################################################################
  # Equipment unit related classes
  ###################################################################
  
  class EquipmentUnitType:
      def __init__(self, label, componentDict):
          self.componentDict = componentDict
          self.label = label
  
  class EquipmentUnitStatus:
      OK = 0
      PREVENTIVE = 1
      CORRECTIVE = 2
  
  class EquipmentUnitObject(EquipmentComponentContainer):
      def __init__(self, id, evaluationScore, unitType, components):
          super(EquipmentUnitObject,self).__init__()
          self.id = id
          self.evaluationScore = evaluationScore
          self.unitType = unitType
          for c in components:
              self.addComponent(c)
  
      def isOk(self):
          ok = True
          for c in self.getComponents():
              if not c.isOk():
                  ok = False
                  break
          return ok
  
      def getStatus(self, policy):
          if not self.isOk():
              return EquipmentUnitStatus.CORRECTIVE
          else:
              prev = False
              for c in self.getComponents():
                  if (c.getHazardRate() >
                      policy.getPrevThreshold(c.getType())):
                      prev = True
                      break
              if prev:
                  return EquipmentUnitStatus.PREVENTIVE
          return EquipmentUnitStatus.OK
  
      def getPreventiveComponents(self, policy):
          comps = []
          for c in self.getComponents():
              if (c.getHazardRate() >
                  policy.getPrevThreshold(c.getType())):
                  comps.append(c)
          return comps
  
      def getCorrectiveComponents(self):
          comps = []
          for c in self.getComponents():
              if not c.isOk():
                  comps.append(c)
          return comps
  
      def step(self,time):
          if self.isOk():
              self.evaluationScore.addAvailableHours(time)
              for c in self.getComponents():
                  c.step(time)
          else:
              self.evaluationScore.addUnavailableHours(time)
  
      def toStr(self):
          return "%s(%d)"%(self.unitType.label,self.id)
  
  ###################################################################
  # Maintainer Agent
  ###################################################################
  
  class MaintainerAgent(EquipmentComponentContainer):
      def __init__(self, label, evaluationScore, hourly_cost):
          super(MaintainerAgent,self).__init__()
          self.label = label
          self.evaluationScore = evaluationScore
          self.hourly_cost = hourly_cost
          self.location = None
          self.time_to_next_action = 0.0
          self.current_component = None
          self.unit = None
  
      def setLocation(self, location):
          self.location = location
  
      def isAvailable(self):
          return self.time_to_next_action <= 0.0
  
      def assign(self, unit, setup_time):
          self.unit = unit
          self.time_to_next_action = setup_time
          # Wait for setup
          if (self.time_to_next_action > 0.0):
              return
          # No setup time, start corrective maintenance
          for c in unit.getCorrectiveComponents():
              self.current_component = c
              break
          if self.current_component is None:
              # No setup or corrective maintennace, start preventive
              for c in unit.getPreventiveComponents(
                  self.location.getPolicy()):
                  self.current_component = c
                  break
          self.time_to_next_action = self.current_component.getTTR()
  
      def step(self, time):
          # Score keeping
          if self.unit is not None:
              self.evaluationScore.addMaintainerCost(time*
                                                     self.hourly_cost)
              self.evaluationScore.addUnavailableHours(time)
          self.time_to_next_action -= time
  
          # Time to do something?
          if (self.time_to_next_action <= 0.0 and
              self.unit is not None):
              if self.current_component is not None:
                  # Replace repaired component
                  self.unit.removeComponent(self.current_component)
                  self.location.addComponent(self.current_component)
                  spare = None
                  for c in self.getComponentsByType(
                      self.current_component.getType()):
                      spare = c
                      break
                  self.unit.addComponent(spare)
                  self.removeComponent(spare)
                  self.current_component = None
              # Find next corrective component to repair (if any)
              for c in self.unit.getCorrectiveComponents():
                  self.current_component = c
                  break
              if self.current_component is None:
              # No corrective, check for preventive
                  for c in self.unit.getPreventiveComponents(
                      self.location.getPolicy()):
                      if self.hasComponentType(c.getType()):
                          self.current_component = c
                          break
              if self.current_component is not None:
                  self.time_to_next_action = \
                      self.current_component.getTTR()
              else:
                  self.location.unAssignUnit(self.unit)
                  self.unit = None
  
      def toStr(self):
          return "MaintainerAgent %s"%(self.label,)
  
  ###################################################################
  # Location manager agent
  ###################################################################
  
  class LocationManagerAgent(EquipmentComponentContainer):
      def __init__(self, label, evaluationScore,
                   equipment_components,
                   equipment_units=[],
                   maintainers=[],
                   policy=None,
                   preventive_setup_time=0.0,
                   corrective_setup_time=0.0):
          super(LocationManagerAgent,self).__init__()
          self.label = label
          self.evaluationScore = evaluationScore
          for c in equipment_components:
              self.addComponent(c)
          self.maintainers = maintainers
          for m in self.maintainers:
              m.setLocation(self)
          self.policy = policy
          self.preventive_setup_time = preventive_setup_time
          self.corrective_setup_time = corrective_setup_time
          # Setup initial set of units
          self.assigned_units = []
          self.available_units = []
          self.corrective_queue = []
          self.preventive_queue = []
          for u in equipment_units:
              status = u.getStatus(policy)
              if status == EquipmentUnitStatus.CORRECTIVE:
                  self.corrective_queue.append(u)
              else:
                  if status == EquipmentUnitStatus.PREVENTIVE:
                      self.preventive_queue.append(u)
                  self.available_units.append(u)
          # Setup component delivery handling
          self.received_requests = []
          self.sent_requests = []
          self.upstream_channel = None
  
      def getPolicy(self):
          return self.policy
  
      def getAnySpare(self, componentType):
          spare = None
          for s in self.getComponentsByType(componentType):
              if (s.isOk() and
                  (spare is None or
                   s.getHazardRate() < spare.getHazardRate())):
                  spare = s
          if spare is not None:
              self.removeComponent(spare)
          return spare
  
      def getNewSpare(self, componentType):
          spare = None
          for s in self.getComponentsByType(componentType):
              if (s.isOk() and
                  (s.getHazardRate() <
                   self.policy.getPrevThreshold(componentType)) and
                  (spare is None or
                   s.getHazardRate() < spare.getHazardRate())):
                  spare = s
          if spare is not None:
              self.removeComponent(spare)
          return spare
  
      def step(self, time):
          # Step all available units
          for u in self.available_units:
              u.step(time)
          # Accumulate downtime for queued broken units
          for u in self.corrective_queue:
              self.evaluationScore.addUnavailableHours(time)
          # Step all maintainers
          for m in self.maintainers:
              m.step(time)
          # Make maintenance decisions and allocate work
          self.doMaintenance()
          # Handle delivery requests from downstream locations
          self.doDeliveries()
          # Place component requests from upstream locations
          self.doRequests()
  
      def doMaintenance(self):
          # Check for broken units needing corrective maintenance
          for u in self.available_units:
              status = u.getStatus(self.policy)
              if status == EquipmentUnitStatus.CORRECTIVE:
                  self.corrective_queue.append(u)
              elif (status == EquipmentUnitStatus.PREVENTIVE and
                    u not in self.preventive_queue):
                  self.preventive_queue.append(u)
          for u in self.corrective_queue:
              if u in self.available_units:
                  self.available_units.remove(u)
              if u in self.preventive_queue:
                  self.preventive_queue.remove(u)
          # Assign units for corrective maintenance
          self.assignUnits(self.corrective_queue,
                           self.corrective_setup_time, True)
          # Assign units for preventive maintenance
          self.assignUnits(self.preventive_queue,
                           self.preventive_setup_time, False)
  
      def assignUnits(self, queue, setup_time, is_corrective):
          # Assign units to available maintainers is possible
          for u in queue:
              # Check for an available maintainer
              available_maintainer = None
              for m in self.maintainers:
                  if m.isAvailable():
                      available_maintainer = m
                      break
  
              # Check for spares
              has_spares = True
              if available_maintainer is not None:
                  spares = []
                  if is_corrective:
                      for c in u.getCorrectiveComponents():
                          s = self.getAnySpare(c.getType())
                          if s is not None:
                              spares.append(s)
                          else:
                              has_spares = False
                              break
                  else:
                      for c in u.getPreventiveComponents(self.policy):
                          s = self.getNewSpare(c.getType())
                          if s is not None:
                              spares.append(s)
                          else:
                              has_spares = False
                              break
                  if has_spares:
                      for s in spares:
                          available_maintainer.addComponent(s)
                  else:
                      for s in spares:
                          self.addComponent(s)
                      continue
              # Assign maintenance
              if has_spares and available_maintainer is not None:
                  available_maintainer.assign(u, setup_time)
                  self.assigned_units.append(u)
          # Unqueue units that are currently being maintained
          for u in self.assigned_units:
              if u in self.corrective_queue:
                  self.corrective_queue.remove(u)
              if u in self.preventive_queue:
                  self.preventive_queue.remove(u)
              if u in self.available_units:
                  self.available_units.remove(u)
  
      def unAssignUnit(self, unit):
          self.assigned_units.remove(unit)
          self.available_units.append(unit)
  
      def doDeliveries(self):
          completedRequests = []
          for request in self.received_requests:
              # Find and deliver any spares matching the request
              delivery = []
              for c in request[1]:
                  s = self.getAnySpare(c)
                  if s is not None:
                      delivery.append(s)
              if len(delivery) > 0:
                  request[0].sendComponents(delivery)
              # Remove already delivered spares from the request
              for c in delivery:
                  request[1].remove(c.getType())
              # Remove request when everything has been delivered
              if len(request[1]) == 0:
                  completedRequests.append(request)
          for r in completedRequests:
              self.received_requests.remove(r)
  
      def receiveComponentsRequest(self, channel, componentsList):
          self.received_requests.append((channel, componentsList))
  
      def receiveDeliveredComponents(self, components):
          for c in components:
              self.addComponent(c)
              self.sent_requests.remove(c.getType())
  
      def setUpstreamChannel(self, upstream_channel):
          self.upstream_channel = upstream_channel
  
      def doRequests(self):
          # Request any spares
          if self.upstream_channel is not None:
              request = []
              for t in self.policy.keys():
                  # How many do we have?
                  n = 0;
                  for c in self.getComponentsByType(t):
                      if (c.isOk() and
                          (c.getHazardRate() <
                           self.policy.getPrevThreshold(t))):
                          n += 1
                  # How many are on order?
                  for ct in self.sent_requests:
                      if ct == t:
                          n += 1
                  # How many do we need?
                  if n <= self.policy.getReorderLevel(t):
                      for i in xrange(n,
                                      self.policy.getMaxStockLevel(t)):
                          request.append(t)
                          self.sent_requests.append(t)
              if len(request) > 0:
                  self.upstream_channel.requestComponents(request)
  
      def toStr(self):
          s = "%s: "%(self.label,)
          component_types = set()
          for c in self.getComponents():
              if not c.getType() in component_types:
                  component_types.add(c.getType())
          for t in component_types:
              c = 0
              p = 0
              d = 0
              for comp in self.getComponentsByType(t):
                  d += 1
                  if not comp.isOk():
                      c += 1
                  elif (self.policy is not None and
                        (comp.getHazardRate() >
                         self.policy.getPrevThreshold(t))):
                      p += 1
              s += "%s %d/%d/%d\t"%(t.label,c,p,d)
          return s
  
  ###################################################################
  # Distribution channel object
  ###################################################################
  
  class DistributionChannelObject:
      def __init__(self, label, evaluationScore,
                   upstream, downstream,
                   lead_time, cost):
          self.label = label
          self.evaluationScore = evaluationScore
          self.upstream = upstream
          self.downstream = downstream
          downstream.setUpstreamChannel(self)
          self.lead_time = float(lead_time)
          self.cost = cost
          self.transports = []
  
      def requestComponents(self, componentsList):
          self.upstream.receiveComponentsRequest(
              self, componentsList)
  
      def sendComponents(self, components):
          self.transports.append([self.lead_time, components])
  
      def step(self, time):
          delivered = []
          for t in self.transports:
              self.evaluationScore.addTransportCost(time*self.cost)
              t[0] -= time
              if t[0] <= 0.0:
                  self.downstream.receiveDeliveredComponents(t[1])
                  delivered.append(t)
          for t in delivered:
              self.transports.remove(t)
  
      def getComponents(self):
          comps = []
          for t in self.transports:
              for c in t[1]:
                  comps.append(c)
          return comps
  
      def toStr(self):
          return "%s: %d"%(self.label,len(self.getComponents()))
  
  ###################################################################
  # Resource tracking classes
  ###################################################################
  class EquipmentComponentObjects(EquipmentComponentContainer):
      def __init__(self, evaluationScore):
          super(EquipmentComponentObjects,self).__init__()
          self.reset()
          self.evaluationScore = evaluationScore
  
      def reset(self):
          super(EquipmentComponentObjects,self).reset()
          self.ctr = 0
  
      def new(self, componentType, age=0):
          eco = EquipmentComponentObject(self.ctr,
                                         self.evaluationScore,
                                         componentType,
                                         age)
          self.addComponent(eco)
          self.ctr = self.ctr + 1
          return eco
  
  class EquipmentUnitObjects:
      def __init__(self, evaluationScore, components_container):
          self.reset()
          self.evaluationScore = evaluationScore
          self.components_container = components_container
  
      def reset(self):
          self.ctr = 0
          self.objects = []
  
      def new(self, unitType):
          components = []
          for n,t in unitType.componentDict:
              for i in xrange(n):
                  components.append(
                      self.components_container.new(t))
          eqUnit = EquipmentUnitObject(self.ctr,
                                       self.evaluationScore,
                                       unitType,
                                       components)
          self.objects.append(eqUnit)
          self.ctr = self.ctr + 1
          return eqUnit
  
      def get(self, id):
          return self.objects[id]
  
  ###################################################################
  # Evaluation score
  ###################################################################
  
  class EvaluationScore:
      def __init__(self):
          self.reset()
  
      def reset(self):
          self.maintainer_cost = 0.0
          self.component_cost = 0.0
          self.transport_cost = 0.0
          self.available_hours = 0.0
          self.unavailable_hours = 0.0
  
      def addMaintainerCost(self, cost):
          self.maintainer_cost += cost
  
      def addComponentCost(self, cost):
          self.component_cost += cost
  
      def addTransportCost(self, cost):
          self.transport_cost += cost
  
      def addAvailableHours(self, hrs):
          self.available_hours += hrs
  
      def addUnavailableHours(self, hrs):
          self.unavailable_hours += hrs
  
      def getCost(self):
          return self.maintainer_cost + \
              self.component_cost + \
              self.transport_cost
  
      def getAvailability(self):
          return self.available_hours/(self.available_hours +
                                       self.unavailable_hours)
      def toStr(self):
          s =  "Evaluation Score\n"
          s += "----------------\n"
          s += "  Maintainers: %d\n"%(self.maintainer_cost,)
          s += "  Components: %d\n"%(self.component_cost,)
          s += "  Transport: %d\n"%(self.transport_cost,)
          s += "Total cost: %d\n"%(self.getCost(),)
          s += "  Available hours: %d\n"%(self.available_hours,)
          s += "  Unavailable hours: %d\n"%(self.unavailable_hours,)
          s += "Overall availability: %d%%\n"%(
              self.getAvailability()*100,)
          return s