class Collector(Process): """Periodically collects customer happiness to a time series.""" collect_interval = 60*10 @Chain def initialize(self): self.stat = TSeries(storing=True, time_fnc=self.sim.clock.get, time_scale=10.0*3600) while True: self.stat.collect(100.0 * (compute_abandoned())) yield self.collect_interval
class Queue(Process): content = None def reset(self): self.content = [] self.size = Checkable(0) self.size_tseries = TSeries(time_fnc=self.sim.clock.get, storing=True) self.size_tseries.collect(0) self.category_ftable = FTable() Consumer.waiting_time.clear() def status(self): return self.content def put(self, obj): self.content.append(obj) self.size.set(len(self.content)) self.size_tseries.collect(len(self.content)) self.category_ftable.collect(obj) def get(self): obj = self.content.pop(0) self.size.set(len(self.content)) self.size_tseries.collect(len(self.content)) return obj def finalize(self): p = self.plotter = Plotter(rows=1, cols=2) Consumer.waiting_time.histogram(axes=p, title="Consumer waiting time") self.category_ftable.pie_chart(axes=p, title="Production distribution") self.size_tseries.run_chart(axes=p, title="Queue size time series", label="Queue size", color="green", legend=True)
class Worker(Process): IDLE = "idle" BUSY = "busy" def constructor(self): self.state = TSeries() self.current = None self.tasks = Deque() self.operation = None def status(self): return "(%s, %d tasks remaining) current=%s" % (self.state.last_value, len(self.tasks), self.current) def reset(self): self.state = TSeries(numeric=False, time_fnc=self.sim.clock.get) self.current = None self.tasks.clear() @Chain def initialize(self): self.state.collect(Worker.IDLE) while True: self.current = (yield self.operation.tasks.get()).result self.state.collect(Worker.BUSY) yield self.current # block until task is completed while len(self.tasks) > 0: self.current = self.tasks.popleft() yield self.current self.state.collect(Worker.IDLE) self.current = None def finalize(self): # repeat the last collected state to "close" the interval when ending a simulation self.state.repeat()
def reset(self): self.queue = [] self.size = Checkable(0) self.pending = TSeries(storing=True, time_fnc=self.sim.clock.get) self.qtime = Tally() # queueing time self.stime = Tally() # service time self.rtime = Tally() # response time
def reset(self): self.content = [] self.size = Checkable(0) self.size_tseries = TSeries(time_fnc=self.sim.clock.get, storing=True) self.size_tseries.collect(0) self.category_ftable = FTable() Consumer.waiting_time.clear()
def reset(self): self.clock.precision = 0.5 self.members.clear() Virus.autoname_reset() for _ in xrange(Virus.initial_population): self.members.add(Virus()) self.count = TSeries(storing=True, time_fnc=self.clock.get) self.count.collect(len(self.members))
class RequestQueue(Process): def reset(self): self.queue = [] self.size = Checkable(0) self.pending = TSeries(storing=True, time_fnc=self.sim.clock.get) self.qtime = Tally() # queueing time self.stime = Tally() # service time self.rtime = Tally() # response time def put(self, io_request): self.queue.append(io_request) self.size.set(len(self.queue)) self.pending.collect(len(self.queue)) def get(self): io_request = self.queue.pop(0) self.size.set(len(self.queue)) self.pending.collect(len(self.queue)) return io_request def collect(self, io_request): # Collect indicators about a departing request. self.qtime.collect(io_request.queueing_time()) self.stime.collect(io_request.service_time()) self.rtime.collect(io_request.response_time()) def finalize(self): print "Mean queue size (weighted):", self.pending.wmean() print "Mean queueing time:", self.qtime.mean() print "Mean service time:", self.stime.mean() print "Mean responese time:", self.rtime.mean() self.plotter = Plotter() self.pending.run_chart(axes=self.plotter, label="Pending requests", legend=True)
class Machine(Process): IDLE = "idle" BUSY = "busy" current = None state = TSeries() def status(self): return "(%s) %s" % (self.state.last_value, self.current) def reset(self): self.state = TSeries(numeric=False, storing=True, time_fnc=self.sim.clock.get) self.current = None @Chain def initialize(self): while True: self.state.collect(Machine.IDLE) self.current = None self.current = (yield self.parent.queue.get()).result #yield self.prepare() yield self.process() self.current.action.succeed() @Chain def process(self): with self.current.history.add("process", machine=self.path): self.state.collect(Machine.BUSY) yield self.current.quantity * self.current.product.complexity[self.parent.name] def finalize(self): self.state.repeat() print self.path, self.state.report()
def reset(self): if "queue" not in self: self["queue"] = Store() self["queue"].content.clear() self.state = TSeries(storing=False, numeric=False, time_fnc=self.sim.clock.get) self.workers = Checkable(self.initial_workers) self.throughput = self.throughput_fnc(self.initial_workers) self.current = None self.previous = None
class Server(Process): speed = 1.0 customer = None def status(self): return self.customer def reset(self): self.state = TSeries(numeric=False, time_fnc=self.sim.clock.get) self.customer = None self.pending = [] #@Chain def initialize(self): while True: self.customer = None self.state.collect("idle") self.customer, service_time = (yield self.sim.queue.idle(self)).result self.state.collect("busy") def finalize(self): self.state.collect(self.state.last_value) def serve(self, customer, service_time): inactive = bool(len(self.pending) == 0) service = self._serve(customer, service_time) self.pending.append(service) if inactive: self.action.succeed() return service.observe() #@Chain def _serve(self, customer, service_time): self.customer = customer yield service_time / self.speed self.customer = None
class Queue(Process): customers = [] def status(self): return "\n".join(customer.name for customer in self.customers) def reset(self): self.customer_requests = Deque() self.idle_servers = Deque() self.size = TSeries(time_fnc=self.sim.clock.get) def wait_for_server(self): """Called by a customer to enter the queue for a server.""" return CustomerRequest(self) def _add_request(self, request): if len(self.idle_servers) > 0: request.succeed(self.idle_servers.popleft()) else: self.customer_requests.append(request) self.size.collect(len(self.customer_requests)) def _drop_request(self, request): self.customer_requests.remove(request) self.size.collect(len(self.customer_requests)) def idle(self, server): """Called by a server after being released by a customer.""" if len(self.customer_requests) > 0: self.customer_requests[0].succeed(server) else: self.idle_servers.append(server) def finalize(self): self.size.collect(self.size.last_value)
class PhysicalCPU(Process): clients = [] speed = 1.0 def status(self): if len(self.clients) == 0: return "idle" client_speed = 100.0 / len(self.clients) * self.speed lines = ["%d clients (%.02f%% to each)" % (len(self.clients), client_speed)] lines.extend(client.path for client in self.clients) return "\n".join(lines) def reset(self): self.state = TSeries(numeric=False, time_fnc=self.sim.clock.get) self.state.collect("Idle") self.clients = set() def finalize(self): self.state.collect("Idle" if len(self.clients) == 0 else "Busy") @Chain def acquire(self, client): yield Chain.result(self) if len(self.clients) == 0: self.state.collect("Busy") self.clients.add(client) self.update() def release(self, client): self.clients.remove(client) if len(self.clients) == 0: self.state.collect("Idle") else: self.update() def update(self): if len(self.clients) > 0: client_speed = self.speed / len(self.clients) for client in self.clients: client.set_speed(client_speed) def utilization(self): return self.state.wrel_frequency("Busy") * 100.0
class DiskFIFO(Process): """A first-in-first-out disk.""" current = None def status(self): if self.current is None: return "idle" lines = ["%s (%d queued)" % (self.current, len(self.queue))] lines.extend(client.path for client in self.queue) return "\n".join(lines) def reset(self): self.state = TSeries(numeric=False, time_fnc=self.sim.clock.get) self.state.collect("Idle") self.current = None self.queue = Deque() def finalize(self): self.state.collect("Idle" if self.current is None else "Busy") @Chain def acquire(self, client): yield Chain.result(self) if self.current is None: self.current = client self.state.collect("Busy") raise Chain.success() self.queue.append(client) yield Action() # suspend until activated by release() def release(self, client): assert client is self.current if len(self.queue) > 0: self.current = self.queue.popleft() self.current.action.succeed() # activate next in line else: self.current = None self.state.collect("Idle") def utilization(self): return self.state.wrel_frequency("Busy") * 100.0
class VirusSim(Simulator): def reset(self): self.clock.precision = 0.5 self.members.clear() Virus.autoname_reset() for _ in xrange(Virus.initial_population): self.members.add(Virus()) self.count = TSeries(storing=True, time_fnc=self.clock.get) self.count.collect(len(self.members)) @Chain def initialize(self): # Print time if trace is disabled. if not self.stack.trace: while True: print "%10.6f: %d viruses" % (self.time, len(self.members)) yield 10 def finalize(self): axes = self.count.run_chart(label="Registered growth", title="Virus population", legend=True) self.plotter = axes.figure.plotter print "\n" + self.count.report()
class Agent(Process): """Necessary configuration parameters: skills :: [skill] service_time :: {need: float} """ client = None def status(self): if self.client is None: return "idle" return "%s (%s)" % (self.client.name, self.client.need) def reset(self): self.client = None self.state = TSeries(numeric=False, time_fnc=self.sim.clock.get) @Chain def initialize(self): sim = self.sim queue = sim.members["queue"] self.state.collect("idle") while True: self.state.collect("idle") self.client = (yield queue.content.get(filter=self.can_answer)).result yield 0.0 self.client.action.fail() # cause the timeout to fail self.state.collect("busy") yield self.service_time[self.client.need] self.client = None def finalize(self): results = self.sim.simulation.results if "utilization" not in results: results.utilization = Namespace() self.state.collect("idle" if self.client is None else "busy") results.utilization[self.path] = self.state.wrel_frequency("busy") def can_answer(self, client): # filter function for Store.content.get() return client.need in self.skills
class Machine(Process): IDLE = "idle" BUSY = "busy" def constructor(self): self.state = TSeries() self.current = None self.operation = None self.channel = None def status(self): return "(%s) %s" % (self.state.last_value, self.current) def reset(self): self.state = TSeries(numeric=False, time_fnc=self.sim.clock.get) self.current = None self.channel = Channel() @Chain def initialize(self): self.state.collect(Machine.IDLE) while True: lot = (yield self.operation.queue.get()).result # block until a lot is available load = LoadTask(lot, self) self.operation.tasks.append(load) yield load.observe() # block until operator finishes loading yield self.process() unload = UnloadTask(self) self.operation.tasks.append(unload) yield unload.observe() # block until operator finishes loading @Chain def process(self): yield self.channel.emit("Process started") self.state.collect(Machine.BUSY) duration = self.current.quantity * self.current.product.complexity[self.operation.path] self.process_delay = Delay(duration) yield self.process_delay self.state.collect(Machine.IDLE) yield self.channel.emit("Process finished") def finalize(self): # repeat the last collected state to "close" the interval when ending a simulation self.state.repeat()
class VirtualCPU(PhysicalCPU): pCPU = None # target for acquire() requests (may be a group of cpus) current_pCPU = None # pCPU where the vCPU is currently running def status(self): if len(self.clients) == 0: return "idle" client_speed = 100.0 / len(self.clients) * self.speed lines = ["%d clients (%.02f%% to each, total %.02f%% from %s)" % (len(self.clients), client_speed, self.speed * 100.0, self.current_pCPU)] lines.extend(client.path for client in self.clients) return "\n".join(lines) def reset(self): PhysicalCPU.reset(self) self.pCPU_utz = TSeries(time_fnc=self.sim.clock.get) self.pCPU_utz.collect(0.0) def finalize(self): PhysicalCPU.reset(self) self.pCPU_utz.collect(0.0) @Chain def acquire(self, client): yield Chain.result(self) if len(self.clients) == 0: self.current_pCPU = (yield self.pCPU.acquire(self)).result yield PhysicalCPU.acquire(self, client) def release(self, client): PhysicalCPU.release(self, client) if len(self.clients) == 0: self.current_pCPU.release(self) self.current_pCPU = None self.pCPU_utz.collect(0.0) def set_speed(self, speed): self.pCPU_utz.collect(speed) self.speed = speed self.update()
class Disk(Process): mean_service_time = 0.02 # Mean service time for one IO request. def reset(self): self.state = TSeries(numeric=False, time_fnc=self.sim.clock.get) self.state.collect("Idle") @Chain def initialize(self): sim = self.sim queue = sim.members["Queue"] while True: if queue.size.get() == 0: self.state.collect("Idle") yield Poll.greater_than(0, queue.size) self.state.collect("Busy") io_request = queue.get() io_request.service = sim.time yield sample.nonnegative(sim.rng.expovariate, 1.0 / Disk.mean_service_time) io_request.departure = self.sim.time queue.collect(io_request) def finalize(self): queue = self.sim.members["Queue"] if queue.running: queue.stop() # Make sure to finalize the queue before the disk. self.state.collect(self.state.last_value()) self.state.pareto_chart(axes=queue.plotter, title=self.name) print self, "utilization:", self.state.wrel_frequency("Busy")
def reset(self): self.state = TSeries(numeric=False, time_fnc=self.sim.clock.get) self.current = None self.tasks.clear()
def constructor(self): self.state = TSeries() self.current = None self.tasks = Deque() self.operation = None
def reset(self): self.state = TSeries(numeric=False, storing=True, time_fnc=self.sim.clock.get) self.current = None
def reset(self): self.state = TSeries(numeric=False, time_fnc=self.sim.clock.get) self.customer = None self.pending = []
def reset(self): self.customer_requests = Deque() self.idle_servers = Deque() self.size = TSeries(time_fnc=self.sim.clock.get)
class WorkStation(Process): """Configuration parameters: initial_workers :: int throughput_fnc :: callable(int) -> float changeover :: {(from, to): float} setup :: {product: float} """ state = TSeries() workers = Checkable(0) throughput = 0.0 current = None initial_workers = 0 changeover = {} setup = {} def set_workers(self, workers): """This method is used by the line balancer to adjust the number of workers in each assembly line and/or the workbenches, and update their throughput.""" self.throughput = self.throughput_fnc(workers) self.workers.set(workers) def throughput_fnc(self, workers): raise NotImplementedError() def status(self): return "%d workers (throughput=%f) - %s - %s" % \ (self.workers.get(), self.throughput, self.state.last_value(), self.current) def reset(self): if "queue" not in self: self["queue"] = Store() self["queue"].content.clear() self.state = TSeries(storing=False, numeric=False, time_fnc=self.sim.clock.get) self.workers = Checkable(self.initial_workers) self.throughput = self.throughput_fnc(self.initial_workers) self.current = None self.previous = None @Chain def initialize(self): queue = self["queue"] while True: yield self.fetch(queue) yield self.prepare(self.current.product) yield self.process(self.current) self.previous = self.current.product self.current.action.succeed() self.current = None @Chain def fetch(self, queue): """Get the order with minimum preparation time and maximum process time from the queue.""" self.state.collect(IDLE) if len(queue.content) == 0: self.current = (yield queue.content.get()).result else: best_score = None best_order = None for order in queue.content: changeover = self.changeover.get((self.previous, order.product), 0.0) setup = self.setup.get(order.product, 0.0) ptime = order.quantity * order.product.complexity[self.name] score = (-(changeover + setup), +ptime) if score > best_score: best_score = score best_order = order queue.content.remove(best_order) self.current = best_order self.current.location = self.name @Chain def prepare(self, product): """Prepare the workstation for processing the specified product. This may include a changeover if the product is different from the one previously processed, and a setup time for preparing the machinery to start working.""" self.state.collect(CHANGEOVER) yield self.changeover.get((self.previous, product), 0.0) self.state.collect(SETUP) yield self.setup.get(product, 0.0) @Chain def process(self, order): """Simulate the processing of 'order' with a possibly variable number of workers.""" self.state.collect(BUSY) remaining = order.quantity while True: if self.workers.get() == 0: self.state.collect(NO_WORKERS) yield Poll.greater_than(0, self.workers) self.state.collect(BUSY) rate = self.throughput / order.product.complexity[self.name] process = Delay(remaining / rate) yield Renege(process, Poll.not_equal_to(self.workers.get(), self.workers)) remaining -= process.elapsed_time * rate if process.succeeded(): raise Chain.success() def finalize(self): self.state.collect(self.state.last_value()) self.state.pareto_chart(axes=self.sim.plotter, title=self.name)
class PSQueue(Process): """Single-server processor sharing queue. This is used to represent how processes or threads run in time-sharing mode in modern operating systems.""" remaining = {} # {request: remaining_servtime} def status(self): lines = ["%d requests" % (len(self.remaining),)] for request, remaining in self.remaining.iteritems(): lines.append("%s (remaining=%f)" % (request.owner.name, remaining)) return "\n".join(lines) def reset(self): self.remaining = {} self.ongoing = Checkable(0) self.state = TSeries(numeric=False, time_fnc=self.sim.clock.get) self.finishing = False self.start_time = None @Chain def initialize(self): while True: if len(self.remaining) == 0: self.state.collect("Idle") yield Poll.greater_than(0, self.ongoing) self.state.collect("Busy") self.start_time = self.sim.time nprocs = len(self.remaining) delay = Delay(min(self.remaining.itervalues()) * nprocs) yield delay self.update() if delay.failed(): # Interrupted by put_request(), wait until it exits yield Action() def finalize(self): self.state.collect("Idle" if len(self.remaining) == 0 else "Busy") def put_request(self, process, servtime): request = Request(servtime) request.bind(process) interrupted = False if len(self.remaining) > 0 and not self.finishing: self.action.fail() interrupted = True self.remaining[request] = servtime self.ongoing.set(len(self.remaining)) if interrupted: self.action.succeed() return request def update(self): # Update remaining service times and check for finished requests. nprocs = len(self.remaining) elapsed = (self.sim.time - self.start_time) / nprocs finished = [] for request, remaining in self.remaining.iteritems(): # This is required because of damn floating point arithmetic errors... if remaining - elapsed < 1e-6: finished.append(request) else: self.remaining[request] = remaining - elapsed # Remove finished requests. if len(finished) > 0: self.finishing = True for request in finished: del self.remaining[request] request.fulfill() self.ongoing.set(len(self.remaining)) self.finishing = False def utilization(self): return self.state.wrel_frequency("Busy") * 100.0
def reset(self): self.remaining = {} self.ongoing = Checkable(0) self.state = TSeries(numeric=False, time_fnc=self.sim.clock.get) self.finishing = False self.start_time = None
def initialize(self): self.stat = TSeries(storing=True, time_fnc=self.sim.clock.get, time_scale=10.0*3600) while True: self.stat.collect(100.0 * (compute_abandoned())) yield self.collect_interval
def constructor(self): self.state = TSeries() self.current = None self.operation = None self.channel = None
def reset(self): self.state = TSeries(numeric=False, time_fnc=self.sim.clock.get) self.current = None self.channel = Channel()
def reset(self): self.state = TSeries(numeric=False, time_fnc=self.sim.clock.get) self.state.collect("Idle")