예제 #1
0
def test_sample_episode_state_space_full(prices):
    space = StateSpace('state').from_primitives(
        PrimCfg('price [$/MWh]', min(prices), max(prices), 'continuous',
                prices), )

    start, end = space.sample_episode('full')

    assert start == 0
    assert end == len(prices)
예제 #2
0
    def __init__(
            self,
            name,
            episode_length=24,  # one day 24 hr
            dataset='data',
            **kwargs):
        self.metadata = {'render.modes': []}
        self.reward_range = (-float('inf'), float('inf'))

        self.name = name
        self.episodes = 0
        self.episode_length = episode_length

        super().__init__(**kwargs)

        self.state_space = StateSpace().from_dataset(dataset)
        self.observation_space = self.state_space
        assert self.state_space.num_samples == self.observation_space.num_samples

        self.action_space = ActionSpace().from_primitives(
            Prim('Turn down', -1, 1, 'continuous', None))

        # load tolerable power data
        self.tolerable_power_df = pd.read_csv(join(dataset, 'tolerable.csv'),
                                              parse_dates=True)[name]
예제 #3
0
def test_call_state_space(prices):
    space = StateSpace('state').from_primitives(
        PrimCfg('price [$/MWh]', min(prices), max(prices), 'continuous',
                prices), )

    for idx in np.random.randint(0, len(prices), size=10):
        state = space(steps=idx, offset=0)
        assert state == prices[idx]
예제 #4
0
    def __init__(
            self,
            power=2.0,
            capacity=4.0,
            efficiency=0.9,
            initial_charge=0.0,

            episode_length=2016,
            sample_strat='fixed',

            prices=None,
            dataset='example',
            **kwargs
    ):
        self.power = float(power)
        self.capacity = float(capacity)
        self.efficiency = float(efficiency)
        self.initial_charge = initial_charge
        self.sample_strat = sample_strat
        super().__init__(**kwargs)

        #  this is f*****g messy
        if prices is not None:
            self.state_space = StateSpace().from_primitives(
                Prim('Price [$/MWh]', min(prices), max(prices), 'continuous', prices),
                Prim('Charge [MWh]', 0, self.capacity, 'continuous', 'append')
            )

        else:
            self.state_space = StateSpace().from_dataset(dataset).append(
                Prim('Charge [MWh]', 0, self.capacity, 'continuous', 'append')
            )

        self.observation_space = self.state_space
        assert self.state_space.num_samples == self.observation_space.num_samples

        if sample_strat == 'full':
            self.episode_length = self.state_space.num_samples
        else:
            self.episode_length = min(episode_length, self.state_space.num_samples)

        self.action_space = ActionSpace().from_primitives(
            Prim('Power [MW]', -self.power, power, 'continuous', None)
        )
예제 #5
0
def test_sample_episode_state_space_random(prices):
    space = StateSpace('state').from_primitives(
        PrimCfg('price [$/MWh]', min(prices), max(prices), 'continuous',
                prices), )

    starts, ends = [], []
    for _ in range(50):
        start, end = space.sample_episode('random', episode_length=10)
        starts.append(start)
        ends.append(end)

    assert min(starts) >= 0
    assert max(starts) <= len(prices)

    #  catch case when we always start at 0
    if np.mean(starts) != 0:
        #  check we start at different points
        assert np.std(starts) != 0
        #  check we end at different points
        assert np.std(ends) != 0
예제 #6
0
class Battery(BaseEnv):
    """
    Electric battery operating in price arbitrage

    action = charging is positive, discharging is negative
    shape = (batch_size, 1)

    args
        power [MW]
        capacity [MWh]
        efficiency [%]
        initial_charge [% or 'random']
    """
    def __init__(
            self,
            power=2.0,
            capacity=4.0,
            efficiency=0.9,
            initial_charge=0.0,

            episode_length=2016,
            sample_strat='fixed',

            prices=None,
            dataset='example',
            **kwargs
    ):
        self.power = float(power)
        self.capacity = float(capacity)
        self.efficiency = float(efficiency)
        self.initial_charge = initial_charge
        self.sample_strat = sample_strat
        super().__init__(**kwargs)

        #  this is f*****g messy
        if prices is not None:
            self.state_space = StateSpace().from_primitives(
                Prim('Price [$/MWh]', min(prices), max(prices), 'continuous', prices),
                Prim('Charge [MWh]', 0, self.capacity, 'continuous', 'append')
            )

        else:
            self.state_space = StateSpace().from_dataset(dataset).append(
                Prim('Charge [MWh]', 0, self.capacity, 'continuous', 'append')
            )

        self.observation_space = self.state_space
        assert self.state_space.num_samples == self.observation_space.num_samples

        if sample_strat == 'full':
            self.episode_length = self.state_space.num_samples
        else:
            self.episode_length = min(episode_length, self.state_space.num_samples)

        self.action_space = ActionSpace().from_primitives(
            Prim('Power [MW]', -self.power, power, 'continuous', None)
        )

    def __repr__(self):
        return '<energypy BATTERY env - {:2.1f} MW {:2.1f} MWh>'.format(
            self.power, self.capacity)

    def _reset(self):
        """ samples new episode, returns initial observation """
        #  initial charge in percent
        if self.initial_charge == 'random':
            initial_charge = random()
        else:
            initial_charge = float(self.initial_charge)

        #  charge in MWh
        self.charge = float(self.capacity * initial_charge)

        #  new episode
        self.start, self.end = self.state_space.sample_episode(
            self.sample_strat, episode_length=self.episode_length
        )

        #  set initial state and observation
        self.state = self.state_space(
            self.steps, self.start, append={'Charge [MWh]': self.charge}
        )
        self.observation = self.observation_space(
            self.steps, self.start, append={'Charge [MWh]': self.charge}
        )

        assert self.charge <= self.capacity
        assert self.charge >= 0

        return self.observation

    def _step(self, action):
        """
        one step through the environment
        action = charging is positive, discharging is negative [MW]
        shape = (batch_size, 1)

        returns
            transition (dict)
        """
        old_charge = self.charge

        #  convert from MW to MWh/5 min by /12
        net_charge = action / 12

        #  we first check to make sure this charge is within our capacity limit
        new_charge = np.clip(old_charge + net_charge, 0, self.capacity)

        #  we can now calculate the gross power of charge or discharge
        #  charging is positive, discharging negative
        gross_power = (new_charge - old_charge) * 12

        #  account for losses / the round trip efficiency
        #  we lose electricity when we discharge
        if gross_power < 0:
            losses = abs(gross_power * (1 - self.efficiency))

        #  we don't lose anything when we charge
        else:
            losses = 0

        net_power = gross_power + losses

        #  the new charge of the battery after losses
        self.charge = old_charge + (gross_power / 12)

        #  energy balances
        assert np.isclose(gross_power - net_power, -losses)

        #  reward is the cost to charge / benefit to discharge
        #  use net power as it accounts for losses
        electricity_price = self.get_state_variable('Price [$/MWh]')
        reward = -net_power * electricity_price / 12

        #  zero indexing steps
        if self.steps == self.episode_length - 1:
            done = True
            next_state = np.zeros((1, *self.state_space.shape))
            next_observation = np.zeros((1, *self.observation_space.shape))

        else:
            done = False
            next_state = self.state_space(
                self.steps + 1, self.start,
                append={'Charge [MWh]': float(self.charge)}
            )
            next_observation = self.observation_space(
                self.steps + 1, self.start,
                append={'Charge [MWh]': float(self.charge)}
            )

        return {
            'step': int(self.steps),
            'state': self.state,
            'observation': self.observation,
            'action': action,
            'reward': float(reward),
            'next_state': next_state,
            'next_observation': next_observation,
            'done': bool(done),

            'Price [$/MWh]': float(electricity_price),
            'Initial charge [MWh]': float(old_charge),
            'Final charge [MWh]': float(self.charge),
            'Gross [MW]': float(gross_power),
            'Net [MW]': float(net_power),
            'Loss [MW]': float(losses)
        }