Source code for citylearn.power_outage

from typing import List
import numpy as np
from citylearn.base import Environment

[docs]class PowerOutage: """Base stochastic power outage model class. Randomly assigns power outage signal to time steps. Parameters ---------- random_seed: int, optional Pseudorandom number generator seed for repeatable results. """ def __init__(self, random_seed: int = None): self.random_seed = random_seed @property def random_seed(self) -> int: return np.random.randint(*Environment.DEFAULT_RANDOM_SEED_RANGE) if self.__random_seed is None else self.__random_seed @random_seed.setter def random_seed(self, value: int): self.__random_seed = value
[docs] def get_signals(self, time_steps: int, **kwargs) -> np.ndarray: """Returns power outage signal time series. Returns time series with randomly selected time steps set as candidates for power outage. Parameters ---------- time_steps: int Number of time steps in returned signal time series. Other Parameters ---------------- kwargs: Any Any other parameters specific to :py:meth:`get_signals` method for other subclasses of :py:class:`citylearn.grid_resilience.PowerOutage`. Returns ------- signals: np.ndarray Power outage time series signal where value of 0 indicates no power outage at time step index whereas value of 1 indicates a power outage at said time step. """ nprs = np.random.RandomState(self.random_seed) signals = nprs.choice([0, 1], size=time_steps) return signals
[docs]class ReliabilityMetricsPowerOutage(PowerOutage): """Power outage signal stochastic model based on Distribution System Reliability Metrics. Generates time series of power outage signals based on System Average Interruption Frequency Index (SAIFI) and Customer Average Interruption Duration Index (CAIDI). The signal is generated by sampling `n` instances from a binomial distribution to select days that experience power outage where the probability, `p`, is the ratio of `saifi` to number of days in a year (365). The start time step index for the outage on each day is then randomly selected from a uniform distribution of `start_time_steps` or all valid daily time step indexes. Finally the duration of each power outage event is set by sampling from an exponential distribution with a scale set to `caidi`. Parameters ---------- saifi: float, default: 1.436 Number of non-momentary electric interruptions, per year, the average customer experienced and is used as the average number of days per year that experience power outage. caidi: float, default: 331.2 Average number of minutes it takes to restore non-momentary electric interruptions and is used as the average length of a power outage. start_time_steps: List[int], optional List of candidate daily time step indexes to randomly select from when deciding start time step of an outage. For example, for an hourly simulation that wants to consider power outages that start during the evening peak between 4 PM to 7 PM will set `start_time_steps` as [15, 16, 17, 18]. By default all daily time step indexes are considered. Other Parameters ---------------- **kwargs : dict Other keyword arguments used to initialize :py:class:`citylearn.grid_resilience.PowerOutage` super class. Notes ----- The reliability metrics are sourced from https://www.eia.gov/electricity/annual/html/epa_11_01.html. """ def __init__(self, saifi: float = None, caidi: float = None, start_time_steps: List[int] = None, **kwargs): super().__init__(**kwargs) self.saifi = saifi self.caidi = caidi self.start_time_steps = start_time_steps @property def saifi(self) -> float: return self.__saifi @property def caidi(self) -> float: return self.__caidi @property def start_time_steps(self) -> List[int]: return self.__start_time_steps @saifi.setter def saifi(self, value: float): self.__saifi = 1.436 if value is None else value @caidi.setter def caidi(self, value: float): self.__caidi = 331.2 if value is None else value @start_time_steps.setter def start_time_steps(self, value: List[float]): self.__start_time_steps = value
[docs] def get_signals(self, time_steps: int, seconds_per_time_step: float, **kwargs) -> np.ndarray: """Returns power outage signal time series. Returns time series with randomly selected time steps set as candidates for power outage. Parameters ---------- time_steps: int Number of time steps in returned signal time series. seconds_per_time_step: float Number of seconds in one `time_step`. Other Parameters ---------------- kwargs: Any Any other parameters specific to :py:meth:`get_signals` method for other subclasses of :py:class:`citylearn.grid_resilience.PowerOutage`. Returns ------- signals: np.ndarray Power outage time series signal where value of 0 indicates no power outage at time step index whereas value of 1 indicates a power outage at said time step. """ nprs = np.random.RandomState(self.random_seed) days_per_year = 365.0 seconds_per_day = 86400.0 seconds_per_minute = 60.0 time_steps_per_day = seconds_per_day/seconds_per_time_step time_steps_per_minute = seconds_per_minute/seconds_per_time_step day_count = time_steps/time_steps_per_day daily_outage_probability = self.saifi/days_per_year outage_days = nprs.binomial(n=1, p=daily_outage_probability, size=int(day_count)) outage_day_ixs = outage_days*np.arange(day_count) outage_day_ixs = outage_day_ixs[outage_day_ixs != 0] outage_day_count = outage_days[outage_days == 1].shape[0] start_time_steps = list(range(int(time_steps_per_day))) if self.start_time_steps is None else self.start_time_steps outage_start_time_steps = nprs.choice(start_time_steps, size=outage_day_count) outage_durations = nprs.exponential(scale=self.caidi, size=outage_day_count) # [mins] outage_duration_time_steps = outage_durations*time_steps_per_minute signals = np.zeros(time_steps, dtype=int) for i, j , k in zip(outage_day_ixs, outage_start_time_steps, outage_duration_time_steps): start_ix = i*time_steps_per_day + j end_ix = start_ix + k start_ix = int(start_ix) end_ix = int(end_ix) signals[start_ix:end_ix] = 1 return signals