Example #1
0
 def __init__(self, sim):
     """
     Args:
         - `sim`: The :class:`model <simso.core.Model.Model>` object.
     """
     self.sim = sim
     self._logs = Monitor(name="Logs", sim=sim)
Example #2
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
Example #3
0
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
Example #4
0
 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())  
Example #6
0
    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)
Example #7
0
    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 = []
Example #8
0
 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)
Example #9
0
 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)
Example #10
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 __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")
Example #12
0
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)
Example #13
0
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)
Example #14
0
 def tally(self, x):
     Monitor.tally(self, x)
     self.min = min(self.min, x)
     self.max = max(self.max, x)
Example #15
0
 def __init__(self):
     Monitor.__init__(self)
     self.min, self.max = (int(2**31-1),0)
Example #16
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)
Example #17
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, 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)
Example #18
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.
        """
        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)
Example #19
0
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))
Example #20
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
        # 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)
Example #21
0
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')
Example #22
0
 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()
Example #23
0
 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)