Create a Time Aware Simulator¶
This tutorial will help you to create a simulators that keeps track of the current time using demod. It assumes that you have followed the previous tutorial about creating a demod simulator.
Demod helps handling time in the simulation when required.
It can do that for simulator inheriting from
TimeAwareSimulator
.
class TimeAwareExampleSimulator(TimeAwareSimulator):
"""Adds Time functionality to our previous example Simulator."""
The first thing we have to handle now is the constructor of the
simulator.
Let’s first check the one of the
TimeAwareSimulator
.
-
TimeAwareSimulator.
__init__
(n_households, start_datetime=datetime.datetime(2014, 1, 1, 4, 0), step_size=datetime.timedelta(seconds=60), logger=None, **kwargs) Create a Time Aware Simulator.
- Parameters
n_households (int) – The number of households
start_datetime (datetime.datetime) – The start of the simulation. Defaults starts at 4 am on the 1rs January 2014.
step_size (datetime.timedelta) – The duration of a step. Defaults to 1 minute.
logger (Optional[demod.simulators.base_simulators.SimLogger]) – An optional logger object. Defaults to None.
- Raises
TypeError – If the types of the inputs does not match.
- Return type
None
There are two new parameters that are mandatory.
The step_size
which
will tell how long a step of the simulator last,
and start_datetime
which
specifies when the simulation starts.
Note
Demod uses the standard python library datetime for handling the time, where datetime.datetime represent a time stamp and datetime.timedelta represent time intervals.
Let’s adapt the __init__ and methods:
def __init__(
self, n_households, max_residents, *args,
initialization_algo='random', **kwargs
):
# The new parameters will simply be passed through *args, **kwargs to
# TimeAwareSimulator, we don't need any change about that.
super().__init__(n_households, *args, **kwargs)
self.occupants = np.empty(n_households, dtype=int)
# Initialize the simulator
self.initialize_starting_state(max_residents)
Note
Our example simulator can accept any step_size, but some simulator might allow only one step size, which can be specified in the __init__ method. You can also add a condition on the start_datetime, if for example the simulator is used for retrieving old climate data, the start_datetime must be included in the dataset.
We did not need to change many things, excepted that we added
a new initialization_algo
argument with a default value.
Now let’s see how we can use initialize_starting_state.
-
TimeAwareSimulator.
initialize_starting_state
(*args, initialization_time=None, **kwargs) Initialize the starting state of a time aware simulator.
This method will compute how many initialization steps are required to get to
current_time
. The steps are calculated based onstep_size
andinitialization_time
. The initial datetime will becurrent_time
or if it is not possible due to step_size constraints, the first datetime reachable by the simulator beforecurrent_time
.- Parameters
*args – Any arg that must be passed to the step method during the initialization.
initialization_time (Optional[Union[datetime.datetime, datetime.timedelta]]) – The time or datetime for which the simulator is initalized. Defaults to None (no initialization steps, initialized at the current datetime).
*kwargs – Any keyword arg that must be passed to the step method during the initialization.
- Raises
ValueError – If the initialization_time is not reachable by the simulator.
NotImplementedError – If the type of the initialization_time is not recognized.
- Return type
None
We can implement our initialization like this:
def initialize_starting_state(self, max_residents, initialization_algo):
"""This is the function initializing the starting state.
Different methods, depending on the algorithm.
"""
if initialization_algo == 'random':
# Same as previous initialization
self.occupants = np.random.randint(
0, max_residents, size=self.n_households
)
super().initialize_starting_state(
# Will not run any step
# self.current_time tracks the time during simulation
initialization_time=self.current_time
)
elif initialization_algo == 'all_inside_at_4am':
# Now assume that all the residents are at home at 4 AM
# Sets the residents to be all there
self.occupants = max_residents * np.ones(self.n_households)
# Call to the parent initialization
super().initialize_starting_state(
# Specifies that the method has been initialized for 4 AM
initialization_time=datetime.time(4, 0, 0),
# Sets dummy variables for the step function during
# the initial steps
arriving=np.zeros(self.n_households),
leaving=np.zeros(self.n_households)
)
else:
# Import a specific error message
from demod.utils.error_messages import UNIMPLEMENTED_ALGO_IN_METHOD
# Raise the error message
raise NotImplementedError(UNIMPLEMENTED_ALGO_IN_METHOD.format(
algo=initialization_algo,
method=self.initialize_starting_state,
))
Note
Demod provides support for error messages, you can learn more here.
Finally we can look at the step function:
-
TimeAwareSimulator.
step
() Perform a time aware step.
Update the
current_time
according to thestep_size
.- Return type
None
The interesting thing is that you can add a callback to the step function that will trigger another method when being called.
from demod.simulators.base_simulators import Callbacks
@ Callbacks.before_next_day_4am
def step(self, arriving, leaving):
"""Step function of the simulator.
"""
self.occupants += arriving
self.occupants -= leaving
super().step()
def on_before_next_day_4am(self):
"""This function is called by the Callbacks.before_next_day_4am
It will be called every day at 4 am, right before the step
function is called.
"""
# We want to print the percentage households with no one at home.
print("There is {} percent of the households that are empty".format(
np.mean(self.occupants==0) * 100
))
You can see all the available callbacks function, and their corresponding
methods in demod.simulators.base_simulators.Callbacks
.
Finally as a bonus,
you can try to pass
logger=SimLogger('current_datetime', 'your_getter)
to your new simulator, to have a plot that used the time
in the x coordinates !
You can continue the tutorial by