Creating your first simulator

This tutorial will help you to create your own simulator using demod. It assumes that you have installed demod, and that you are familiar with both: python and numpy .

A simulator is a python class that inherits from the demod.simulators.base_simulators.Simulator class.

We can start by creating this object

import numpy as np
from demod.simulators.base_simulators import Simulator

class ExampleSimulator(Simulator):
    """This is a simple example simulator showing how to implement a sim.

    It implements a simple simulator that memorize the number of occupants
    inside the households.
    """

Now that we have created a Simulator and that we have decided what it should do, there are 3 methods that every Simulator must implement.

The first one is the constructor, __init__(). If have never heard of python’s __init__(), you can get more information .

class ExampleSimulator(Simulator):
    """This is a simple example simulator showing how to implement a sim.

    It implements a simple simulator that memorize the number of occupants
    inside the households, based on arrivals and departures.

    """
    def __init__(self, n_hh, *args, **kwargs):
        """Initialization of the function parameters and set-up.

        Put here everything you want to be performed before the simulation.
        This can inclue loading files or setting variable.
        *args, **kwargs are additional arguments that are passed to the
        parent simulator object.

        Args:
            n_households: The number of households.
                A parameter that is passed to any Simulator.
            max_residents: The maximum number of residents
                that are allowed in the households.
        """
        # Initialize the parent instance of the simulator
        super().__init__(n_households, *args, **kwargs)

        # Attributes a variable that will store the position of the households
        # Empty array of the size of the number of households
        self.occupants = np.empty(n_households, dtype=int)

Note

We use numpy arrays to store the variables and to do computation because it makes coding and scalability very easy.

Now, the constructor object can be created using

example_sim = ExampleSimulator(42)

However we did not initialize the value of the number of occupants (it is still an empty array) To initialize it, we can use the second simulator method : Simulator.initialize_starting_state()

def initialize_starting_state(self, max_residents):
    """This is the function initializing the starting state.

    The starting state defines which values the simulator
    should have at the beggining of the simulation.

    Args:
        max_residents: the maximum number of residents in the households
    """
    # Randomly initialize the number, using the n_household class attribute
    self.occupants = np.random.randint(0, max_residents, size=self.n_households)

    # Call to the parent initialization method
    super().initialize_starting_state()

Now that we have implemented the initialization method, we can add it do the constructor

def __init__(self, n_households, max_residents, *args, **kwargs):

    super().__init__(n_households, *args, **kwargs)
    self.occupants = np.empty(n_households, dtype=int)

    # Initialize the simulator
    self.initialize_starting_state(max_residents)

Now, when we create the simulator, it will also run the initialization of the starting state. Note that we added an input variable that determines the maximum number of residents in a house at the start.

It is time to create the third method of the simulator : Simulator.step()

It performs an iteration of the simulation. It receives some variables as input, and uses them to update its internal variables.

def step(self, arriving, leaving):
    """Step function of the simulator.

    Updates the number of residents based
    on the number of arrivals and departures.
    """
    self.occupants += arriving
    self.occupants -= leaving
    # Calls the parent step method.
    super().step()

We have implement the three mandatory methods, by now we also want to be able to read the number of occupant. For that we can create a getter.

def get_occupancy(self):
    """Return the number of occupants.
    """
    return self.occupants

You can create any getter you want, but make their name start with get_ and try to reuse the naming of getters that already exist if possible.

Now we have finished, the simulator can be used in a simulation. You could improve it by settings limits on the persons, or raising errors depending on the values of the inputs (ex: no one can leave if there is no one at home). You can also continue the tutorial where we use demod to add time functionality in the simulator.

Note

When you implement these 3 methods, you should always call the super() corresponding method. This is because the base simulator contains some instructions that are helpful for all kinds of simulators, such as the logging of the values.

Base Simulator

In demod, all simulations are performed by Simulator objects. Therefore all simulators are children of the Simulator class.

The base simulator provides some functionality common to all simulators:

class demod.simulators.base_simulators.Simulator(n_households, logger=None)

Abstract mother class for all the simulators.

The 3 methods of this simulator are meant to be called by its children. See Creating your first simulator for more informations.

n_households

The number of households simulated.

Type

int

current_time_step

The current time step of the simulation.

Type

int

logger

A logger object, that can log the value of variables during the simulation.

Type

demod.simulators.base_simulators.SimLogger

You need to implement in your simulator the 3 following methods.

The constructor Simulator.__init__(). If have never heard of python’s __init__(), you can get more information .

Simulator.__init__(n_households, logger=None)

Initialize the simulator.

This method is supposed to be called in the __init__() of all children of Simulator .

Parameters
Raises
  • TypeError – If the type of n_housholds is not an integer

  • ValueError – If n_households is non positive

Return type

None

Simulator.initialize_starting_state() initialize the variable simulations and computes their initial value at the start of the simulation.

Simulator.initialize_starting_state(start_time_step=0, *args, **kwargs)

Initialize the values of the variables.

If a start_time_step is given, the initilization will call the step function to simulate the requested number of time steps. Any arguments to be used by the step function can be passed as *args, **kwargs.

Parameters

start_time_step (int) – The time step at which the simulation should start. Defaults to 0.

Raises
  • TypeError – If the type of start_time_step is not int.

  • ValueError – If the start_time_step is negative.

Return type

None

Simulator.step() performs an iteration of the simulation. It receives as inputs requested variables.

Simulator.step()

Perform a simulation step.

Increment the current_time_step, calls logger, and clear cached variables.

Return type

None