def __init__(self, sim): """ Args: - `sim`: The :class:`model <simso.core.Model.Model>` object. """ self.sim = sim self._logs = Monitor(name="Logs", sim=sim)
def __init__(self, model, proc_info): Process.__init__(self, name=proc_info.name, sim=model) self._model = model self._internal_id = Processor._identifier Processor._identifier += 1 self.identifier = proc_info.identifier # EDIT currently running time partition self._time_part = False self._running_inst = None self._evts_inst = deque([]) self._sched_part = None self._running = None self.was_running = None self._evts = deque([]) self.sched = model.scheduler self.monitor = Monitor(name="Monitor" + proc_info.name, sim=model) self._caches = [] self._penalty = proc_info.penalty self._cs_overhead = proc_info.cs_overhead self._cl_overhead = proc_info.cl_overhead self._migration_overhead = proc_info.migration_overhead self.set_caches(proc_info.caches) self.timer_monitor = Monitor(name="Monitor Timer" + proc_info.name, sim=model) self._speed = proc_info.speed
class Logger(object): """ Simple logger. Every message is logged with its date. """ def __init__(self, sim): """ Args: - `sim`: The :class:`model <simso.core.Model.Model>` object. """ self.sim = sim self._logs = Monitor(name="Logs", sim=sim) def log(self, msg, kernel=False): """ Log the message `msg`. Args: - `msg`: The message to log. - `kernel`: Allows to make a distinction between a message from \ the core of the simulation or from the scheduler. """ self._logs.observe((msg, kernel)) @property def logs(self): """ The logs, a SimPy Monitor object. """ return self._logs
def run(self, aseed): self.initialize() seed(aseed) self.k = Resource(capacity=Nc, name="Clerk", sim=self) self.wM = Monitor(sim=self) s = Source('Source', sim=self) self.activate(s, s.generate(number=maxNumber, interval=ARRint), at=0.0) self.simulate(until=maxTime) return (self.wM.count(), self.wM.mean())
class BankModel(Simulation): def run(self,aseed): self.initialize() seed(aseed) self.k = Resource(capacity=Nc,name="Clerk",sim=self) self.wM = Monitor(sim=self) s = Source('Source',sim=self) self.activate(s,s.generate(number=maxNumber,interval=ARRint),at=0.0) self.simulate(until=maxTime) return (self.wM.count(),self.wM.mean())
def __init__(self, sim, scheduler_info, **kwargs): """ Args: - `sim`: :class:`Model <simso.core.Model>` instance. - `scheduler_info`: A :class:`SchedulerInfo` representing the \ scheduler. Attributes: - **sim**: :class:`Model <simso.core.Model.Model>` instance. \ Useful to get current time with ``sim.now_ms()`` (in ms) or \ ``sim.now()`` (in cycles). - **processors**: List of :class:`processors \ <simso.core.Processor.Processor>` handled by this scheduler. - **task_list**: List of :class:`tasks <simso.core.Task.GenericTask>` \ handled by this scheduler. Methods: """ self.sim = sim self.processors = [] self.task_list = [] self._lock = False self.overhead = scheduler_info.overhead self.overhead_activate = scheduler_info.overhead_activate self.overhead_terminate = scheduler_info.overhead_terminate self.data = scheduler_info.data self.monitor = Monitor(name="MonitorScheduler", sim=sim)
def __init__(self, sim, task_info): """ Args: - `sim`: :class:`Model <simso.core.Model>` instance. - `task_info`: A :class:`TaskInfo` representing the Task. :type sim: Model :type task_info: TaskInfo """ Process.__init__(self, name=task_info.name, sim=sim) self.name = task_info.name self._task_info = task_info self._monitor = Monitor(name="Monitor" + self.name + "_states", sim=sim) self._activations_fifo = deque([]) self._sim = sim self.cpu = None self._etm = sim.etm self._job_count = 0 self._last_cpu = None self._cpi_alone = {} self._jobs = [] self.job = None # other tasks executed after it is preempted self._other_tasks = []
def run(self, aseed): """ PEM """ seed(aseed) self.counter = Resource(1, name="Clerk", sim=self) self.Mon = Monitor('Time in the Bank', sim=self) source = Source(sim=self) self.activate(source, source.generate(number=20, rate=0.1), at=0.0) self.simulate(until=maxTime)
def run(self, aseed): """ PEM """ seed(aseed) self.k = Resource(capacity=Nc, name="Clerk", sim=self) self.wM = Monitor(sim=self) s = Source('Source', sim=self) self.activate(s, s.generate(number=maxNumber, interval=ARRint), at=0.0) self.simulate(until=maxTime)
def __init__(self, model, proc_info): Process.__init__(self, name=proc_info.name, sim=model) self._model = model self._internal_id = Processor._identifier Processor._identifier += 1 self.identifier = proc_info.identifier self._running = None self.was_running = None self._evts = deque([]) self.sched = model.scheduler self.monitor = Monitor(name="Monitor" + proc_info.name, sim=model) self._caches = [] self._penalty = proc_info.penalty self._cs_overhead = proc_info.cs_overhead self._cl_overhead = proc_info.cl_overhead self._migration_overhead = proc_info.migration_overhead self.set_caches(proc_info.caches) self.timer_monitor = Monitor(name="Monitor Timer" + proc_info.name, sim=model) self._speed = proc_info.speed
def __init__(self, registry_type=Registry, broker_means=dists.normal(0.05), **kw): super(MdsModel, self).__init__(**kw) regions = self.graph.regions self.regions = [i for i in range(regions[0] * regions[1])] self.brokers = dict() for r in self.regions: node = self.random_region_node(r) node.server.service_time = self.service_dist(broker_means()) broker = Broker(r, node, registry_type(), 60, self.brokers) # give them an intitial picture of the grid for node in self.graph.nodes_in_region(r): state = ResourceState(node.resource_agent, node.resource.free) broker.registry.update_state(state) self.brokers[r] = broker for node in self.graph.nodes_iter(): node.broker = self.brokers[node.region] # this nodes resource agent node.resource_agent = MdsResourceAgent(node, 30) # mapping of jobagents at this node # make a link to fast comms to the broker self.graph.make_link(node, node.broker.node) # ensure brokers have fast links to each other for i, broker in enumerate(self.brokers.itervalues()): for other in self.brokers.values()[i:]: self.graph.make_link(broker.node, other.node) self.mons["broker_util"] = Monitor("broker_util") self.mons["broker_queue"] = Monitor("broker_queue")
class GridResource(object): def __init__(self, node, capacity): self.node = node self.capacity = capacity self.jobs = set() self.util = Monitor("Resource %d utilisation" % node.id) self.util.observe(0,0) def can_allocate(self, job): return job.size <= self.free def start(self, job, confirm_process): assert self.can_allocate(job) self.jobs.add(job) self.util.observe(self.load) # the job will complete and remove itself at the right time job.start(job.execute(self, confirm_process)) def cancel(self, job): self.remove(job) # cancel previously scheduled finish event, w/SimPy work arround canceller = Process() canceller.cancel(job) def remove(self, job): assert job in self.jobs self.jobs.remove(job) self.util.observe(self.load) @property def used(self): return sum(job.size for job in self.jobs) @property def free(self): return self.capacity - self.used @property def load(self): return self.used / float(self.capacity) @property def utilisation(self): return self.util.timeAverage() def current_util(self, tlast): return stats.timeslice_average(self.util, tlast)
from SimPy.Simulation import Monitor from random import expovariate m = Monitor() # define the Monitor object, m for i in range(1000): # make the observations y = expovariate(0.1) m.observe(y) # set up and return the completed histogram h = m.histogram(low=0.0, high=20, nbins=30)
def tally(self, x): Monitor.tally(self, x) self.min = min(self.min, x) self.max = max(self.max, x)
def __init__(self): Monitor.__init__(self) self.min, self.max = (int(2**31-1),0)
class Processor(Process): """ A processor is responsible of deciding whether the simulated processor should execute a job or execute the scheduler. There is one instance of Processor per simulated processor. Those are responsible to call the scheduler methods. When a scheduler needs to take a scheduling decision, it must invoke the :meth:`resched` method. This is typically done in the :meth:`on_activate <simso.core.Scheduler.Scheduler.on_activate>`, :meth:`on_terminated <simso.core.Scheduler.Scheduler.on_terminated>` or in a :class:`timer <simso.core.Timer.Timer>` handler. """ _identifier = 0 @classmethod def init(cls): cls._identifier = 0 def __init__(self, model, proc_info): Process.__init__(self, name=proc_info.name, sim=model) self._model = model self._internal_id = Processor._identifier Processor._identifier += 1 self.identifier = proc_info.identifier self._running = None self.was_running = None self._evts = deque([]) self.sched = model.scheduler self.monitor = Monitor(name="Monitor" + proc_info.name, sim=model) self._caches = [] self._penalty = proc_info.penalty self._cs_overhead = proc_info.cs_overhead self._cl_overhead = proc_info.cl_overhead self._migration_overhead = proc_info.migration_overhead self.set_caches(proc_info.caches) self.timer_monitor = Monitor(name="Monitor Timer" + proc_info.name, sim=model) self._speed = proc_info.speed def resched(self): """ Add a resched event to the list of events to handle. """ self._evts.append((RESCHED,)) def activate(self, job): self._evts.append((ACTIVATE, job)) def terminate(self, job): self._evts.append((TERMINATE, job)) self._running = None def preempt(self, job=None): self._evts = deque([e for e in self._evts if e[0] != PREEMPT]) self._evts.append((PREEMPT,)) self._running = job def timer(self, timer): self._evts.append((TIMER, timer)) def set_speed(self, speed): assert speed >= 0, "Speed must be positive." self._evts.append((SPEED, speed)) @property def speed(self): return self._speed def is_running(self): """ Return True if a job is currently running on that processor. """ return self._running is not None def set_caches(self, caches): self._caches = caches for cache in caches: cache.shared_with.append(self) def get_caches(self): return self._caches caches = property(get_caches, set_caches) @property def penalty_memaccess(self): return self._penalty @property def cs_overhead(self): return self._cs_overhead @property def cl_overhead(self): return self._cl_overhead @property def internal_id(self): """A unique, internal, id.""" return self._internal_id @property def running(self): """ The job currently running on that processor. None if no job is currently running on the processor. """ return self._running def run(self): while True: if not self._evts: job = self._running if job: yield waituntil, self, lambda: job.context_ok self.monitor.observe(ProcCxtLoadEvent()) yield hold, self, self.cl_overhead # overhead load context self.monitor.observe(ProcCxtLoadEvent(terminated=True)) job.interruptReset() self.sim.reactivate(job) self.monitor.observe(ProcRunEvent(job)) job.context_ok = False else: self.monitor.observe(ProcIdleEvent()) # Wait event. yield waituntil, self, lambda: self._evts if job: self.interrupt(job) self.monitor.observe(ProcCxtSaveEvent()) yield hold, self, self.cs_overhead # overhead save context self.monitor.observe(ProcCxtSaveEvent(terminated=True)) job.context_ok = True evt = self._evts.popleft() if evt[0] == RESCHED: if any(x[0] != RESCHED for x in self._evts): self._evts.append(evt) continue if evt[0] == ACTIVATE: self.sched.on_activate(evt[1]) self.monitor.observe(ProcOverheadEvent("JobActivation")) self.sched.monitor_begin_activate(self) yield hold, self, self.sched.overhead_activate self.sched.monitor_end_activate(self) elif evt[0] == TERMINATE: self.sched.on_terminated(evt[1]) self.monitor.observe(ProcOverheadEvent("JobTermination")) self.sched.monitor_begin_terminate(self) yield hold, self, self.sched.overhead_terminate self.sched.monitor_end_terminate(self) elif evt[0] == TIMER: self.timer_monitor.observe(None) if evt[1].overhead > 0: print(self.sim.now(), "hold", evt[1].overhead) yield hold, self, evt[1].overhead evt[1].call_handler() elif evt[0] == SPEED: self._speed = evt[1] elif evt[0] == RESCHED: self.monitor.observe(ProcOverheadEvent("Scheduling")) self.sched.monitor_begin_schedule(self) yield waituntil, self, self.sched.get_lock decisions = self.sched.schedule(self) yield hold, self, self.sched.overhead # overhead scheduling if type(decisions) is not list: decisions = [decisions] decisions = [d for d in decisions if d is not None] for job, cpu in decisions: # If there is nothing to change, simply ignore: if cpu.running == job: continue # If trying to execute a terminated job, warn and ignore: if job is not None and not job.is_active(): print("Can't schedule a terminated job! ({})" .format(job.name)) continue # if the job was running somewhere else, stop it. if job and job.cpu.running == job: job.cpu.preempt() # Send that job to processor cpu. cpu.preempt(job) if job: job.task.cpu = cpu # Forbid to run a job simultaneously on 2 or more processors. running_tasks = [ cpu.running.name for cpu in self._model.processors if cpu.running] assert len(set(running_tasks)) == len(running_tasks), \ "Try to run a job on 2 processors simultaneously!" self.sched.release_lock() self.sched.monitor_end_schedule(self)
class Processor(Process): """ A processor is responsible of deciding whether the simulated processor should execute a job or execute the scheduler. There is one instance of Processor per simulated processor. Those are responsible to call the scheduler methods. When a scheduler needs to take a scheduling decision, it must invoke the :meth:`resched` method. This is typically done in the :meth:`on_activate <simso.core.Scheduler.Scheduler.on_activate>`, :meth:`on_terminated <simso.core.Scheduler.Scheduler.on_terminated>` or in a :class:`timer <simso.core.Timer.Timer>` handler. """ _identifier = 0 @classmethod def init(cls): cls._identifier = 0 def __init__(self, model, proc_info, power): Process.__init__(self, name=proc_info.name, sim=model) self._model = model self._internal_id = Processor._identifier Processor._identifier += 1 self.identifier = proc_info.identifier self._running = None self.was_running = None self._evts = deque([]) self.sched = model.scheduler self.monitor = Monitor(name="Monitor" + proc_info.name, sim=model) self._caches = [] self._penalty = proc_info.penalty self._cs_overhead = proc_info.cs_overhead self._cl_overhead = proc_info.cl_overhead self._migration_overhead = proc_info.migration_overhead self.set_caches(proc_info.caches) self.timer_monitor = Monitor(name="Monitor Timer" + proc_info.name, sim=model) self._speed = proc_info.speed self._power = power self.idle_time = None def resched(self): """ Add a resched event to the list of events to handle. """ self._evts.append((RESCHED, )) def activate(self, job): self._evts.append((ACTIVATE, job)) def terminate(self, job): self._evts.append((TERMINATE, job)) self._running = None def preempt(self, job=None): self._evts = deque([e for e in self._evts if e[0] != PREEMPT]) self._evts.append((PREEMPT, )) self._running = job def timer(self, timer): self._evts.append((TIMER, timer)) def set_speed(self, speed): assert speed >= 0, "Speed must be positive." self._evts.append((SPEED, speed)) def set_idle(self, period): self._power.count_energy(self, float(period)) def set_dummy(self, x): if self.idle_time == None: self.idle_time = self.sim.now() self._power.energyMode(self.sim.now(), self, x) def stop_dummy(self): if self.idle_time != None: self.idle_time = None self._power.stop_idle(self, self.sim.now()) def set_busy(self): if self.idle_time != None: period = self.sim.now() - self.idle_time self._power.record_idle_time(self, period) self.idle_time = None def set_idle_extend(self, time): if self.idle_time == None: self.idle_time = self.sim.now() self._power.set_state(time) def stop_idle_extend(self): if self.idle_time != None: time = self.sim.now() - self.idle_time self.idle_time = None self._power.stop_state(self, time) @property def speed(self): return self._speed def is_running(self): """ Return True if a job is currently running on that processor. """ return self._running is not None def set_caches(self, caches): self._caches = caches for cache in caches: cache.shared_with.append(self) def get_caches(self): return self._caches caches = property(get_caches, set_caches) @property def penalty_memaccess(self): return self._penalty @property def cs_overhead(self): return self._cs_overhead @property def cl_overhead(self): return self._cl_overhead @property def internal_id(self): """A unique, internal, id.""" return self._internal_id @property def running(self): """ The job currently running on that processor. None if no job is currently running on the processor. """ return self._running @property def less_effic_BET(self): bet = 10000 for s, BET, Pen, Co in self._power.states: if BET < bet: bet = BET return bet # Update the energy consumption at the end of simulation def update(self): total = 0 duration = self._model.duration / self._model.cycles_per_ms duration_t = duration * len(self._model.processors) utilization = (self._model.utilization) / len(self._model.processors) exec_time = (duration_t * utilization) ideal = 0 for cpu in self._model.processors: period = 0 if cpu.idle_time != None: period = float(self._model.duration - cpu.idle_time) / self._model.cycles_per_ms total += cpu._power.total() idle_time = duration_t - exec_time consumo_min = cpu._power.consumption(idle_time) + exec_time final = (total / consumo_min) - 1 max_c = ((duration * len(self._model.processors)) / consumo_min) - 1 self._model.logger.log( "Energy consumption with idle = {}. {} of {} max = {}".format( final, total, consumo_min, max_c)) def run(self): while True: if not self._evts: job = self._running if job: yield waituntil, self, lambda: job.context_ok self.monitor.observe(ProcCxtLoadEvent()) yield hold, self, self.cl_overhead # overhead load context self.monitor.observe(ProcCxtLoadEvent(terminated=True)) job.interruptReset() self.sim.reactivate(job) self.monitor.observe(ProcRunEvent(job)) job.context_ok = False else: self.monitor.observe(ProcIdleEvent()) # Wait event. yield waituntil, self, lambda: self._evts if job: self.interrupt(job) self.monitor.observe(ProcCxtSaveEvent()) yield hold, self, self.cs_overhead # overhead save context self.monitor.observe(ProcCxtSaveEvent(terminated=True)) job.context_ok = True evt = self._evts.popleft() if evt[0] == RESCHED: if any(x[0] != RESCHED for x in self._evts): self._evts.append(evt) continue if evt[0] == ACTIVATE: self.sched.on_activate(evt[1]) self.monitor.observe(ProcOverheadEvent("JobActivation")) self.sched.monitor_begin_activate(self) yield hold, self, self.sched.overhead_activate self.sched.monitor_end_activate(self) elif evt[0] == TERMINATE: self.sched.on_terminated(evt[1]) self.monitor.observe(ProcOverheadEvent("JobTermination")) self.sched.monitor_begin_terminate(self) yield hold, self, self.sched.overhead_terminate self.sched.monitor_end_terminate(self) elif evt[0] == TIMER: self.timer_monitor.observe(None) if evt[1].overhead > 0: print(self.sim.now(), "hold", evt[1].overhead) yield hold, self, evt[1].overhead evt[1].call_handler() elif evt[0] == SPEED: self._speed = evt[1] elif evt[0] == RESCHED: self.monitor.observe(ProcOverheadEvent("Scheduling")) self.sched.monitor_begin_schedule(self) yield waituntil, self, self.sched.get_lock decisions = self.sched.schedule(self) yield hold, self, self.sched.overhead # overhead scheduling if type(decisions) is not list: decisions = [decisions] decisions = [d for d in decisions if d is not None] for job, cpu in decisions: # If there is nothing to change, simply ignore: if cpu.running == job: continue # If trying to execute a terminated job, warn and ignore: if job is not None and not job.is_active(): print("Can't schedule a terminated job! ({})".format( job.name)) continue # if the job was running somewhere else, stop it. if job and job.cpu.running == job: # and job.cpu.running != cpu: job.cpu.preempt() # Send that job to processor cpu. cpu.preempt(job) if job: job.task.cpu = cpu # Forbid to run a job simultaneously on 2 or more processors. running_tasks = [ cpu.running.name for cpu in self._model.processors if cpu.running ] assert len(set(running_tasks)) == len(running_tasks), \ "Try to run a job on 2 processors simultaneously!" self.sched.release_lock() self.sched.monitor_end_schedule(self)
class Processor(Process): """ A processor is responsible of deciding whether the simulated processor should execute a job or execute the scheduler. There is one instance of Processor per simulated processor. Those are responsible to call the scheduler methods. When a scheduler needs to take a scheduling decision, it must invoke the :meth:`resched` method. This is typically done in the :meth:`on_activate <simso.core.Scheduler.Scheduler.on_activate>`, :meth:`on_terminated <simso.core.Scheduler.Scheduler.on_terminated>` or in a :class:`timer <simso.core.Timer.Timer>` handler. """ _identifier = 0 @classmethod def init(cls): cls._identifier = 0 def __init__(self, model, proc_info): Process.__init__(self, name=proc_info.name, sim=model) self._model = model self._internal_id = Processor._identifier Processor._identifier += 1 self.identifier = proc_info.identifier self._running = None self.was_running = None self._evts = deque([]) self.sched = model.scheduler self.monitor = Monitor(name="Monitor" + proc_info.name, sim=model) self._caches = [] self._penalty = proc_info.penalty self._cs_overhead = proc_info.cs_overhead self._cl_overhead = proc_info.cl_overhead self._migration_overhead = proc_info.migration_overhead self.set_caches(proc_info.caches) self.timer_monitor = Monitor(name="Monitor Timer" + proc_info.name, sim=model) self._speed = proc_info.speed def resched(self): """ Add a resched event to the list of events to handle. """ print(self._model.now(), "resched") self._evts.append((RESCHED,)) def activate(self, job): print(self._model.now(), "activate") self._evts.append((ACTIVATE, job)) def terminate(self, job): self._evts.append((TERMINATE, job)) self._running = None def preempt(self, job=None): print(self._model.now(), "preempt") self._evts = deque([e for e in self._evts if e[0] != PREEMPT]) self._evts.append((PREEMPT,)) self._running = job def timer(self, timer): self._evts.append((TIMER, timer)) def set_speed(self, speed): assert speed >= 0, "Speed must be positive." self._evts.append((SPEED, speed)) @property def speed(self): return self._speed def is_running(self): """ Return True if a job is currently running on that processor. """ return self._running is not None def set_caches(self, caches): self._caches = caches for cache in caches: cache.shared_with.append(self) def get_caches(self): return self._caches caches = property(get_caches, set_caches) @property def penalty_memaccess(self): return self._penalty @property def cs_overhead(self): return self._cs_overhead @property def cl_overhead(self): return self._cl_overhead @property def internal_id(self): """A unique, internal, id.""" return self._internal_id @property def running(self): """ The job currently running on that processor. None if no job is currently running on the processor. """ return self._running def run(self): while True: if not self._evts: job = self._running if job: yield waituntil, self, lambda: job.context_ok self.monitor.observe(ProcCxtLoadEvent()) yield hold, self, self.cl_overhead # overhead load context self.monitor.observe(ProcCxtLoadEvent(terminated=True)) job.interruptReset() self.sim.reactivate(job) self.monitor.observe(ProcRunEvent(job)) job.context_ok = False else: self.monitor.observe(ProcIdleEvent()) # Wait event. yield waituntil, self, lambda: self._evts if job: self.interrupt(job) self.monitor.observe(ProcCxtSaveEvent()) yield hold, self, self.cs_overhead # overhead save context self.monitor.observe(ProcCxtSaveEvent(terminated=True)) job.context_ok = True evt = self._evts.popleft() print(self._model.now(), "run", evt[0]) if evt[0] == RESCHED: if any(x[0] != RESCHED for x in self._evts): self._evts.append(evt) continue if evt[0] == ACTIVATE: self.sched.on_activate(evt[1]) self.monitor.observe(ProcOverheadEvent("JobActivation")) self.sched.monitor_begin_activate(self) yield hold, self, self.sched.overhead_activate self.sched.monitor_end_activate(self) elif evt[0] == TERMINATE: self.sched.on_terminated(evt[1]) self.monitor.observe(ProcOverheadEvent("JobTermination")) self.sched.monitor_begin_terminate(self) yield hold, self, self.sched.overhead_terminate self.sched.monitor_end_terminate(self) elif evt[0] == TIMER: self.timer_monitor.observe(None) if evt[1].overhead > 0: print(self.sim.now(), "hold", evt[1].overhead) yield hold, self, evt[1].overhead evt[1].call_handler() elif evt[0] == SPEED: self._speed = evt[1] elif evt[0] == RESCHED: self.monitor.observe(ProcOverheadEvent("Scheduling")) self.sched.monitor_begin_schedule(self) yield waituntil, self, self.sched.get_lock decisions = self.sched.schedule(self) yield hold, self, self.sched.overhead # overhead scheduling if type(decisions) is not list: decisions = [decisions] decisions = [d for d in decisions if d is not None] for job, cpu in decisions: if cpu.running is not None: print(self.sim.now(), "run", job.task.name, cpu.running.task.name) else: print(self.sim.now(), "run", job.task.name, cpu.running) # If there is nothing to change, simply ignore: if cpu.running == job: print(self.sim.now(), "no changes") continue # If trying to execute a terminated job, warn and ignore: if job is not None and not job.is_active(): print("Can't schedule a terminated job! ({})" .format(job.name)) continue # if the job was running somewhere else, stop it. if job and job.cpu.running == job: job.cpu.preempt() # Send that job to processor cpu. cpu.preempt(job) if job: job.task.cpu = cpu # Forbid to run a job simultaneously on 2 or more processors. running_tasks = [ cpu.running.name for cpu in self._model.processors if cpu.running] assert len(set(running_tasks)) == len(running_tasks), \ "Try to run a job on 2 processors simultaneously!" self.sched.release_lock() self.sched.monitor_end_schedule(self)
class Scheduler(object): """ The implementation of a scheduler is done by subclassing this abstract class. The scheduling events are modeled by method calls which take as arguments the :class:`jobs <simso.core.Job.Job>` and the :class:`processors <simso.core.Processor.Processor>`. The following methods should be redefined in order to interact with the simulation: - :meth:`init` Called when the simulation is ready. The scheduler \ logic should be initialized here. - :meth:`on_activate` Called upon a job activation. - :meth:`on_terminated` Called when a job is terminated. - :meth:`schedule` Take the scheduling decision. This method should \ not be called directly. A call to the :meth:`resched \ <simso.core.Processor.Processor.resched>` method is required. By default, the scheduler can only run on a single processor at the same simulation time. It is also possible to override this behavior by overriding the :meth:`get_lock` and :meth:`release_lock` methods. """ def __init__(self, sim, scheduler_info, **kwargs): """ Args: - `sim`: :class:`Model <simso.core.Model>` instance. - `scheduler_info`: A :class:`SchedulerInfo` representing the \ scheduler. Attributes: - **sim**: :class:`Model <simso.core.Model.Model>` instance. \ Useful to get current time with ``sim.now_ms()`` (in ms) or \ ``sim.now()`` (in cycles). - **processors**: List of :class:`processors \ <simso.core.Processor.Processor>` handled by this scheduler. - **task_list**: List of :class:`tasks <simso.core.Task.GenericTask>` \ handled by this scheduler. Methods: """ self.sim = sim self.processors = [] self.task_list = [] self._lock = False self.overhead = scheduler_info.overhead self.overhead_activate = scheduler_info.overhead_activate self.overhead_terminate = scheduler_info.overhead_terminate self.data = scheduler_info.data self.monitor = Monitor(name="MonitorScheduler", sim=sim) def init(self): """ This method is called when the system is ready to run. This method should be used in lieu of the __init__ method. This method is guaranteed to be called when the simulation starts, after the tasks are instantiated """ pass def on_activate(self, job): """ This method is called upon a job activation. Args: - `job`: The activated :class:`job <simso.core.Job.Job>`. """ pass def on_terminated(self, job): """ This method is called when a job finish (termination or abortion). Args: - `job`: The :class:`job <simso.core.Job.Job>` that terminates . """ pass def schedule(self, cpu): """ The schedule method must be redefined by the simulated scheduler. It takes as argument the cpu on which the scheduler runs. Args: - `cpu`: The :class:`processor <simso.core.Processor.Processor>` \ on which the scheduler runs. Returns a decision or a list of decisions. A decision is a couple (job, cpu). """ raise NotImplementedError("Function schedule to override!") def add_task(self, task): """ Add a task to the list of tasks handled by this scheduler. Args: - `task`: The :class:`task <simso.core.Task.GenericTask>` to add. """ self.task_list.append(task) def add_processor(self, cpu): """ Add a processor to the list of processors handled by this scheduler. Args: - `processor`: The :class:`processor \ <simso.core.Processor.Processor>` to add. """ self.processors.append(cpu) def get_lock(self): """ Implement a lock mechanism. Override it to remove the lock or change its behavior. """ if not self._lock: self._lock = True else: return False return True def release_lock(self): """ Release the lock. Goes in pair with :meth:`get_lock`. """ self._lock = False def monitor_begin_schedule(self, cpu): self.monitor.observe(SchedulerBeginScheduleEvent(cpu)) def monitor_end_schedule(self, cpu): self.monitor.observe(SchedulerEndScheduleEvent(cpu)) def monitor_begin_activate(self, cpu): self.monitor.observe(SchedulerBeginActivateEvent(cpu)) def monitor_end_activate(self, cpu): self.monitor.observe(SchedulerEndActivateEvent(cpu)) def monitor_begin_terminate(self, cpu): self.monitor.observe(SchedulerBeginTerminateEvent(cpu)) def monitor_end_terminate(self, cpu): self.monitor.observe(SchedulerEndTerminateEvent(cpu))
class Processor(Process): """ A processor is responsible of deciding whether the simulated processor should execute a job or execute the scheduler. There is one instance of Processor per simulated processor. Those are responsible to call the scheduler methods. When a scheduler needs to take a scheduling decision, it must invoke the :meth:`resched` method. This is typically done in the :meth:`on_activate <simso.core.Scheduler.Scheduler.on_activate>`, :meth:`on_terminated <simso.core.Scheduler.Scheduler.on_terminated>` or in a :class:`timer <simso.core.Timer.Timer>` handler. """ _identifier = 0 @classmethod def init(cls): cls._identifier = 0 def __init__(self, model, proc_info): Process.__init__(self, name=proc_info.name, sim=model) self._model = model self._internal_id = Processor._identifier Processor._identifier += 1 self.identifier = proc_info.identifier # EDIT currently running time partition self._time_part = False self._running_inst = None self._evts_inst = deque([]) self._sched_part = None self._running = None self.was_running = None self._evts = deque([]) self.sched = model.scheduler self.monitor = Monitor(name="Monitor" + proc_info.name, sim=model) self._caches = [] self._penalty = proc_info.penalty self._cs_overhead = proc_info.cs_overhead self._cl_overhead = proc_info.cl_overhead self._migration_overhead = proc_info.migration_overhead self.set_caches(proc_info.caches) self.timer_monitor = Monitor(name="Monitor Timer" + proc_info.name, sim=model) self._speed = proc_info.speed def activate_time_partitioning(self, part_scheduler): # Enable time partitioning on this CPU and reset structures self._time_part = True self._running_inst = None self.sched = None self._running = None self._evts = deque([]) self._sched_part = part_scheduler def resched(self, sched=None): """ Add a resched event to the list of events to handle. """ if(self._time_part and sched != None): sched._part._task_evts[self].append((RESCHED,)) else: self._evts.append((RESCHED,)) def activate(self, job): debug("---> ACTIVATE " + job.name + " on " + self.name + " at " + str(self._model.now())) if(self._time_part): job._task._part._task_evts[self].append((ACTIVATE, job,)) else: self._evts.append((ACTIVATE, job)) def terminate(self, job): debug("---> TERMINATE " + job.name + " on " + self.name + " at " + str(self._model.now())) if(self._time_part): job._task._part._task_evts[self].append((TERMINATE, job,)) else: self._evts.append((TERMINATE, job)) def preempt(self, job=None): to_remove = [] for e in self._evts: if e[0] != PREEMPT: to_remove.append(e) for e in to_remove: self._evts.remove(e) #self._evts = deque([e for e in self._evts if e[0] != PREEMPT]) self._evts.append((PREEMPT,)) self._running = job def timer(self, timer): # No easy way to derive current partition from a timer # instance. But args[1] in a timer should be a Task instance # or a scheduler if(self._time_part): timer.args[0]._part._task_evts[self].append((TIMER, timer)) else: self._evts.append((TIMER, timer)) def set_speed(self, speed): assert speed >= 0, "Speed must be positive." self._evts.append((SPEED, speed)) # EDIT added actions on time partitions def activate_inst(self, inst): self._evts_inst.append((ACTIVATE_TPART, inst)) def terminate_inst(self, inst): self._evts_inst.append((TERMINATE_TPART, inst)) def preempt_inst(self, inst=None): self._evts_inst.append((PREEMPT_TPART,)) # First stop the job in the old partition if(inst != self._running_inst and self._running): #self.interrupt(self._running) self._running_inst.part.running_jobs[self] = self._running self._running_inst = inst self._running = None self.sched = None # Resume suspended jobs if(inst): self._running = inst.part.running_jobs[self] inst.part.running_jobs[self] = None self._evts = inst.part.task_evts[self] self.sched = inst.part.scheduler self._evts.append((RESCHED,)) # if(self._running): # self._running.interruptReset() # self.sim.reactivate(self._running) else: self._evts = deque([]) if(inst): debug("Switched to part " + inst.name) else: debug("Switched to NONE") def resched_inst(self, sched=None): """ Add a resched event to the list of events to handle for time partitions. """ self._evts_inst.append((RESCHED_TPART,)) @property def speed(self): return self._speed def is_running(self): """ Return True if a job is currently running on that processor. """ return self._running is not None def is_time_partitioned(self): return self._time_part def set_caches(self, caches): self._caches = caches for cache in caches: cache.shared_with.append(self) def get_caches(self): return self._caches caches = property(get_caches, set_caches) @property def penalty_memaccess(self): return self._penalty @property def cs_overhead(self): return self._cs_overhead @property def cl_overhead(self): return self._cl_overhead @property def internal_id(self): """A unique, internal, id.""" return self._internal_id @property def running(self): """ The job currently running on that processor. None if no job is currently running on the processor. """ return self._running @property def running_inst(self): """ The time partition currently running on that processor. None if no time partition is currently running on the processor. """ return self._running_inst @property def sched_part(self): """A unique, internal, id.""" return self._sched_part def act_to_name(self, act): if(act == 1): return "RESCHED" elif(act == 2): return "ACTIVATE" elif(act == 3): return "TERMINATE" elif(act == 4): return "TIMER" elif(act == 5): return "PREEMPT" elif(act == 6): return "SPEED" elif(act == 7): return "ACTIVATE_TPART" elif(act == 8): return "TERMINATE_TPART" elif(act == 9): return "RESCHED_TPART" elif(act == 10): return "PREEMPT_TPART" # EDIT added procedures to run function to handle time partitioned # case def run(self): while True: debug("--- LOOP ---") if not self._evts and not self._evts_inst: job = self._running if job: yield waituntil, self, lambda: job.context_ok self.monitor.observe(ProcCxtLoadEvent()) yield hold, self, self.cl_overhead # overhead load context self.monitor.observe(ProcCxtLoadEvent(terminated=True)) job.interruptReset() self.sim.reactivate(job) self.monitor.observe(ProcRunEvent(job)) job.context_ok = False if (self._time_part): self._running_inst.notify_job_execution(self, job) debug(self.name + " RUNNING " + job.name + " at " + str(self._model.now())) else: debug(self.name + " IDLE AT " + str(self._model.now())) self.monitor.observe(ProcIdleEvent()) inst = self._running_inst if inst: inst.interruptReset() self.sim.reactivate(inst) self.monitor.observe(ProcStartPart(inst)) else: self.monitor.observe(ProcNoPart(inst)) # Wait for a job or a time partition event. yield waituntil, self, lambda: self._evts or self._evts_inst if job: self.interrupt(job) self.monitor.observe(ProcCxtSaveEvent()) yield hold, self, self.cs_overhead # overhead save context self.monitor.observe(ProcCxtSaveEvent(terminated=True)) job.context_ok = True if inst and self._evts_inst: self.interrupt(inst) if self._evts: evt = self._evts.popleft() # If there is a job event being processed, we must be # executing one of the partitions (or no time partition # system is active on this CPU) #assert self.sched != None or evt[0]==ACTIVATE, "Job event processed outside time partition." debug("TASK: " + self.act_to_name(evt[0]) + " at " + str(self._model.now()) + " on " + self.name) if evt[0] == RESCHED: if any(x[0] != RESCHED for x in self._evts): self._evts.append(evt) continue if evt[0] == ACTIVATE: if self._time_part: sched = evt[1].task.part.scheduler sched.on_activate(evt[1]) else: sched = self.sched sched.on_activate(evt[1]) self.monitor.observe(ProcOverheadEvent("JobActivation")) sched.monitor_begin_activate(self) yield hold, self, sched.overhead_activate sched.monitor_end_activate(self) elif evt[0] == TERMINATE: if self._time_part: sched = evt[1].task.part.scheduler sched.on_terminated(evt[1]) debug("NOTIF...") self._running = None self._running_inst.notify_job_termination(self, evt[1]) else: self.sched.on_terminated(evt[1]) self._running = None self.monitor.observe(ProcOverheadEvent("JobTermination")) self.sched.monitor_begin_terminate(self) yield hold, self, self.sched.overhead_terminate self.sched.monitor_end_terminate(self) elif evt[0] == TIMER: self.timer_monitor.observe(None) if evt[1].overhead > 0: debug(self.sim.now(), "hold", evt[1].overhead) yield hold, self, evt[1].overhead evt[1].call_handler() elif evt[0] == SPEED: self._speed = evt[1] elif evt[0] == RESCHED: self.monitor.observe(ProcOverheadEvent("Scheduling")) self.sched.monitor_begin_schedule(self) yield waituntil, self, self.sched.get_lock decisions = self.sched.schedule(self) yield hold, self, self.sched.overhead # overhead scheduling if type(decisions) is not list: decisions = [decisions] decisions = [d for d in decisions if d is not None] # if(decisions): # debug "TASK RESCED at " + str(self._model.now()) + " - " + str(decisions) + " on " + self.name for job, cpu in decisions: # If there is nothing to change, simply ignore: if cpu.running == job: continue # If trying to execute a terminated job, warn and ignore: if job is not None and not job.is_active(): debug("Can't schedule a terminated job! ({})" .format(job.name)) continue # if the job was running somewhere else, stop it. if job and job.cpu.running == job: job.cpu.preempt() # Send that job to processor cpu. cpu.preempt(job) if job: job.task.cpu = cpu # Forbid to run a job simultaneously on 2 or more processors. running_tasks = [ cpu.running.name for cpu in self._model.processors if cpu.running] assert len(set(running_tasks)) == len(running_tasks), \ "Try to run a job on 2 processors simultaneously!" self.sched.release_lock() self.sched.monitor_end_schedule(self) # If there are more task-related events, skip to next loop # to process all task events before partition events if (self._time_part and self._evts): continue # LOGIC TO RUN PARTITIONS JUST LIKE JOBS # In case time-partitioning is used, process also partition # events if (self._time_part and self._evts_inst): evt = self._evts_inst.popleft() debug("PART: " + self.act_to_name(evt[0]) + " at " + str(self._model.now()) + " on " + self.name) # TODO EDIT HERE if evt[0] == ACTIVATE_TPART: self.sched_part.on_activate_inst(evt[1]) self.monitor.observe(ProcOverheadEvent("PartitionActivation")) self.sched_part.monitor_begin_activate(self) yield hold, self, 0 # TODO add overhead for partition activation self.sched_part.monitor_end_activate(self) elif evt[0] == TERMINATE_TPART: self.sched_part.on_terminated_inst(evt[1]) self.preempt_inst(None) self.monitor.observe(ProcOverheadEvent("PartitionTermination")) self.sched_part.monitor_begin_terminate(self) yield hold, self, 0 # TODO add overhead for partition termination self.sched_part.monitor_end_terminate(self) elif evt[0] == RESCHED_TPART: self.monitor.observe(ProcOverheadEvent("PartitionScheduling")) self.sched_part.monitor_begin_schedule(self) decisions = self.sched_part.schedule(self) yield hold, self, 0 # TODO add overhead for partition scheduling if type(decisions) is not list: decisions = [decisions] decisions = [d for d in decisions if d is not None] for inst, cpus in decisions: for cpu in cpus: # If we are already running the same partition, do nothing if cpu._running_inst == inst: continue # If a partition was completed, raise a warning if inst is not None and not inst.is_active(): debug("Can't schedule a terminated partition instance! ({}) at {} - {}" .format(inst.name, self._model.now(), self.name)) continue cpu.preempt_inst(inst) self.sched_part.monitor_end_schedule(self)
class Epidemic: """A class for modelling and simulating epidemics.""" def __init__(self, epidemic_params): """The constructor of the class. Full params' list: - nr_individuals: number of individuals in the population - initial_infects: number of infects at the start of the simulation - initial_immunes: number of immunes at the start of the simulation - infect_prob: probability of getting infected after a contact with an infect - contact_rate: rate of the contact between individuals - recover_rate: rate of recover from infection - immune_after_recovery: if true, individuals becomes immunes after recovery - immunization_vanish_rate: if not zero, immunization is temporary with given rate - death_rate: rate of death caused by epidemic - newborn_can_be_infect: if true, newborn can be infect - newborn_can_be_immune: if true, newborn can be immune - newborn_prob: probability that a new individual born after a contact - natural_death_prob: probability of death not caused by epidemic - run_time: time duration of the simulation - debug: show more info about the running simulation - process_debug: show more info about SimPy processes during the simulation - progress: show a progress indicator during the simulation - stats: show some stats at the end of the simulation - plot: show a plot representing the evolution of the population at the end of the simulation """ # setting up the epidemic's parameters with metaprogramming for param in epidemic_params: self.__dict__[param] = epidemic_params.get(param) # setting the uninitialized parameters to their default values self.check_and_set_default_value(['initial_immunes', 'recover_rate', 'death_rate', 'immunization_vanish_rate', 'newborn_prob', 'natural_death_prob'], ['immune_after_recovery', 'newborn_can_be_immune', 'newborn_can_be_infect', 'debug', 'process_debug', 'progress', 'stats', 'plot']) # setting the random number generator using the python standard one self.rng = random.Random() # checking some features of the model from parameters passed to the constructor self.model_has_immunization = self.model_has_immunization() self.model_immunization_is_permanent = self.model_immunization_is_permanent() self.model_has_recovering = self.model_has_recovering() self.model_has_death = self.model_has_death() self.model_has_vital_dynamics = self.model_has_vital_dynamics() self.model_newborns_always_susceptibles = self.model_newborns_always_susceptibles() self.model_has_new_susceptibles = self.model_has_new_susceptibles() self.model_has_new_infects = self.model_has_new_infects() # initialize the population counters self.total_infects = self.initial_infects self.total_immunes = self.initial_immunes self.total_susceptibles = self.nr_individuals - self.initial_infects - self.initial_immunes self.total_newborns = 0 self.total_natural_deaths = 0 self.total_deaths = 0 # setting up the monitors for watching interesting variables self.m_suscettibili = Monitor(name="Suscettibili", ylab="suscettibili") self.m_suscettibili.append([0, self.total_susceptibles]) self.m_infetti = Monitor(name="Infetti", ylab='infetti') self.m_infetti.append([0, self.initial_infects]) if self.model_has_immunization: self.m_immuni = Monitor(name="Immuni", ylab='immuni') self.m_immuni.append([0, self.initial_immunes]) # setting up the array of all the individuals partecipating to the simulation self.all_individuals = [] # initialize the simulation environment (time, events, ...) initialize() for i in range(self.nr_individuals): # add individuals to the simulation with the specified health_status if i >= (self.nr_individuals - self.initial_infects): ind = self.Individual(self, ind_id=i, health_status='infect') elif i >= (self.nr_individuals - self.initial_infects - self.initial_immunes): ind = self.Individual(self, ind_id=i, health_status='immune') else: ind = self.Individual(self, ind_id=i) # activate it with function live() activate(ind, ind.live(), at=0.0) self.start_time = time.time() if self.process_debug: self.show_processes_status() # start the simulation simulate(until=self.run_time) self.stop_time = time.time() if self.process_debug: self.show_processes_status() # show final stats if required by params if self.stats: self.show_stats() # show plot if required by params if self.plot: self.show_plot() def model_has_recovering(self): """Checks from parameters if the epidemiological model has recovering.""" if self.recover_rate: return True else: return False def model_has_immunization(self): """Checks from parameters if the epidemiological model has immunization.""" if (self.model_has_recovering and self.immune_after_recovery) or (self.newborn_prob and self.newborn_can_be_immune): return True else: return False def model_immunization_is_permanent(self): """Checks from parameters if the epidemiological model has permanent immunization.""" if self.model_has_immunization and self.immunization_vanish_rate == 0: return True else: return False def model_has_death(self): """Checks from parameters if the epidemiological model has death.""" if self.death_rate: return True else: return False def model_has_vital_dynamics(self): """Checks from parameters if the epidemiological model has vital dynamics (births and natural deaths).""" if (self.newborn_prob or self.natural_death_prob): return True else: return False def model_has_new_susceptibles(self): """Checks if the number of the susceptibles can increase during the simulation.""" if self.model_has_recovering or \ (self.model_has_immunization and not self.model_immunization_is_permanent) or \ self.model_has_vital_dynamics: return True else: return False def model_has_new_infects(self): """Checks if the number of the infects can increase during the simulation, but not for susceptibles getting infected.""" if self.model_has_vital_dynamics and not self.model_newborns_always_susceptibles: return True else: return False def model_newborns_always_susceptibles(self): """Checks if newborns are always susceptibles in the epidemiological model.""" if self.model_has_vital_dynamics and not (self.newborn_can_be_immune and self.newborn_can_be_infect): return True else: return False def check_and_set_default_value(self, num_params, bool_params): """Checks if some optional parameters are set and, if not, set them to their default values. The default values of the optional parameters are: - 0 for every numerical parameters; - True for parameters "progress", "plot", "stats" - False for every other boolean parameters Input: two dictionaries of optional parameters of the constructor of the class Output: none (but the optional parameters are setted with their default values) """ for param in num_params: if hasattr(self, param) == False: self.__dict__[param] = 0 for param in bool_params: if hasattr(self, param) == False: if param in ['progress', 'plot', 'stats']: self.__dict__[param] = True else: self.__dict__[param] = False def show_processes_status(self): """Returns the current internal status of the simulation processes.""" active_counter = 0 passive_counter = 0 terminated_counter = 0 interrupted_counter = 0 for ind in self.all_individuals: if ind.active(): active_counter += 1 if ind.passive(): passive_counter += 1 if ind.terminated(): terminated_counter += 1 if ind.interrupted(): interrupted_counter += 1 print "Processes' status: %3i active, %3i passive, %3i terminated, %3i interrupted" % (active_counter, passive_counter, terminated_counter, interrupted_counter) def wait(self, rate): '''Returns the time interval before the next event.''' return self.rng.expovariate(rate) def next_event(self, events_rates): """Returns the next event, choosen from the given ones, and the time interval before it. Input: a dictionary of possible events and their respective rates Output: one of the possible events passed as input, and the time interval before it. """ rnd = self.rng.random() total_rates = 0 for event in events_rates: total_rates += events_rates.get(event) cumulated_rates = 0 for event in events_rates: cumulated_rates += events_rates.get(event) if rnd <= cumulated_rates/total_rates: returned_event = event break returned_event_interval = self.wait(total_rates) return returned_event, returned_event_interval def observe_vars(self): """Watches and records the values of the monitored variables.""" self.m_infetti.observe(self.total_infects) self.m_suscettibili.observe(self.total_susceptibles) if self.model_has_immunization: self.m_immuni.observe(self.total_immunes) def check_termination_conds(self): """Checks particular situations in which the simulation can be stopped before the time runs out.""" if self.total_infects == 0: if self.debug: print "[%f] STOP: infection ended, no more infects!"% (now()) print "\nSimulation ended prematurely: infection ended, no more infects." return True if not self.model_has_new_susceptibles: if self.total_susceptibles == 0 and self.total_immunes == 0: if self.debug: print "[%f] STOP: infection extended to the whole population!"% (now()) print "\nSimulation ended prematurely: infection extended to the whole population." return True if self.model_immunization_is_permanent and not self.model_has_vital_dynamics: if self.total_susceptibles == 0 and self.total_immunes == 0: if self.debug: print "[%f] STOP: infection ended, permanent immunizzation extended to the whole population!"% (now()) print "\nSimulation ended prematurely: infection ended, permanent immunizzation extended to the whole population." return True return False def check_current_nr_individuals(self): """Checks the correct number of individuals during the simulation.""" assert self.nr_individuals + self.total_newborns - self.total_natural_deaths - self.total_deaths == self.total_susceptibles + self.total_infects + self.total_immunes, "error: assert failed on the current number of individuals." def stop_simulation(self): """Stops the simulation.""" stopSimulation() def show_plot(self): """Plots the number of infected, susceptibles and, eventually, immunes against time using gnuplot-py.""" try: import Gnuplot except ImportError: print "warning: the gnuplot-py module cannot be found. The simulation will run anyway, but you could not plot the graph." pass g = Gnuplot.Gnuplot() if os.getenv('DISPLAY') == None: g('set term png') g('set output "test.png"') else: g('persist=1') g.xlabel('t') g.ylabel('number of individuals') x_infetti = self.m_infetti.tseries() y_infetti = self.m_infetti.yseries() x_suscettibili = self.m_suscettibili.tseries() y_suscettibili = self.m_suscettibili.yseries() g_infetti = Gnuplot.Data(x_infetti, y_infetti, inline=True, title='Infects', with='steps 1')
def __init__(self, epidemic_params): """The constructor of the class. Full params' list: - nr_individuals: number of individuals in the population - initial_infects: number of infects at the start of the simulation - initial_immunes: number of immunes at the start of the simulation - infect_prob: probability of getting infected after a contact with an infect - contact_rate: rate of the contact between individuals - recover_rate: rate of recover from infection - immune_after_recovery: if true, individuals becomes immunes after recovery - immunization_vanish_rate: if not zero, immunization is temporary with given rate - death_rate: rate of death caused by epidemic - newborn_can_be_infect: if true, newborn can be infect - newborn_can_be_immune: if true, newborn can be immune - newborn_prob: probability that a new individual born after a contact - natural_death_prob: probability of death not caused by epidemic - run_time: time duration of the simulation - debug: show more info about the running simulation - process_debug: show more info about SimPy processes during the simulation - progress: show a progress indicator during the simulation - stats: show some stats at the end of the simulation - plot: show a plot representing the evolution of the population at the end of the simulation """ # setting up the epidemic's parameters with metaprogramming for param in epidemic_params: self.__dict__[param] = epidemic_params.get(param) # setting the uninitialized parameters to their default values self.check_and_set_default_value(['initial_immunes', 'recover_rate', 'death_rate', 'immunization_vanish_rate', 'newborn_prob', 'natural_death_prob'], ['immune_after_recovery', 'newborn_can_be_immune', 'newborn_can_be_infect', 'debug', 'process_debug', 'progress', 'stats', 'plot']) # setting the random number generator using the python standard one self.rng = random.Random() # checking some features of the model from parameters passed to the constructor self.model_has_immunization = self.model_has_immunization() self.model_immunization_is_permanent = self.model_immunization_is_permanent() self.model_has_recovering = self.model_has_recovering() self.model_has_death = self.model_has_death() self.model_has_vital_dynamics = self.model_has_vital_dynamics() self.model_newborns_always_susceptibles = self.model_newborns_always_susceptibles() self.model_has_new_susceptibles = self.model_has_new_susceptibles() self.model_has_new_infects = self.model_has_new_infects() # initialize the population counters self.total_infects = self.initial_infects self.total_immunes = self.initial_immunes self.total_susceptibles = self.nr_individuals - self.initial_infects - self.initial_immunes self.total_newborns = 0 self.total_natural_deaths = 0 self.total_deaths = 0 # setting up the monitors for watching interesting variables self.m_suscettibili = Monitor(name="Suscettibili", ylab="suscettibili") self.m_suscettibili.append([0, self.total_susceptibles]) self.m_infetti = Monitor(name="Infetti", ylab='infetti') self.m_infetti.append([0, self.initial_infects]) if self.model_has_immunization: self.m_immuni = Monitor(name="Immuni", ylab='immuni') self.m_immuni.append([0, self.initial_immunes]) # setting up the array of all the individuals partecipating to the simulation self.all_individuals = [] # initialize the simulation environment (time, events, ...) initialize() for i in range(self.nr_individuals): # add individuals to the simulation with the specified health_status if i >= (self.nr_individuals - self.initial_infects): ind = self.Individual(self, ind_id=i, health_status='infect') elif i >= (self.nr_individuals - self.initial_infects - self.initial_immunes): ind = self.Individual(self, ind_id=i, health_status='immune') else: ind = self.Individual(self, ind_id=i) # activate it with function live() activate(ind, ind.live(), at=0.0) self.start_time = time.time() if self.process_debug: self.show_processes_status() # start the simulation simulate(until=self.run_time) self.stop_time = time.time() if self.process_debug: self.show_processes_status() # show final stats if required by params if self.stats: self.show_stats() # show plot if required by params if self.plot: self.show_plot()
def __init__(self, node, capacity): self.node = node self.capacity = capacity self.jobs = set() self.util = Monitor("Resource %d utilisation" % node.id) self.util.observe(0,0)