Energy generators

The energy generator is responsible for determining the amount of energy that is available in the smart grid at each time step (i.e., a local power plant).

Extending this class allows controlling this amount to make the simulation easier, or more difficult, or more realistic, etc., to learning agents.

In order to allow for a large variety of energy generators, the generate_available_energy() method, which is responsible for determining the amount available at a given time step, receives: 1) the current sum of needs of all agents, 2) the current step, 3) the minimum sum of needs from all agents, and 4) the maximum sum of needs from all agents.

These data allow, e.g., scaling the available energy to the current number of agents through their current/min/max total need. As the available energy is part of the observations, it is important to know the possible bounds of the generator, in order to specify the observation space (and ultimately to be able to scale observations to [0, 1] for easier handling by learning algorithms). The available_energy_bounds() serves this purpose. The method could return different bounds at each time step, but this is not recommended, as this would make it harder to define the observation space.

In order to create a new energy generator, these two methods must be implemented. Let us assume that we would like an energy generator that returns a realistic amount, based on real-world data, independently of the agents’ needs:

from smartgrid.util import EnergyGenerator

class RealworldEnergyGenerator(EnergyGenerator):

    def __init__(self, data):
        super().__init__(self)
        self.data = data

    def generate_available_energy(self,
                                  current_need,
                                  current_step,
                                  min_need,
                                  max_need):
        # Retrieve the current step from the data (cycling over the array).
        step = current_step %= len(self.data)
        energy = self.data[step]
        return energy

    def available_energy_bounds(self,
                                current_need,
                                current_step,
                                min_need,
                                max_need):
        # As we return values from the data directly, the bounds are simply
        # the min and max values of the data.
        lower_bound = min(self.data)
        upper_bound = max(self.data)
        return lower_bound, upper_bound

Note that it is very important that the result of generate_available_energy() is contained within the bounds specified by available_energy_bounds(); otherwise, the observation space might be incorrect, and learning agents will receive incoherent observations. The bounds might not need to be extremely precise (although it would be better): very large values such as 0 and 1_000_000 can be used if no accurate approximation can be determined.

Once the energy generator is defined, it can be used when instantiating the World:

from smartgrid import World

# Generates 100Wh at t=0, then 200Wh at t=1, 300Wh at t=2, 10Wh at t=3,
# 100Wh at t=4, and so on...
data = [100, 200, 300, 10]
generator = RealworldEnergyGenerator(data)

world = World(
    agents=...,  # Not relevant here
    energy_generator=generator
)