Source code for gridstatus.base

from enum import Enum

import pandas as pd
import requests
from tabulate import tabulate

# TODO: this is needed to make SPP request work. restrict only to SPP
[docs]requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS = "ALL:@SECLEVEL=1"
# not supported exception
[docs]class NotSupported(Exception): pass
[docs]class Markets(Enum): """Names of LMP Markets""" REAL_TIME_5_MIN = "REAL_TIME_5_MIN" REAL_TIME_15_MIN = "REAL_TIME_15_MIN" REAL_TIME_HOURLY = "REAL_TIME_HOURLY" DAY_AHEAD_HOURLY = "DAY_AHEAD_HOURLY"
[docs] def __contains__(cls, item): try: cls(item) except ValueError: return False return True
[docs]class ISOBase: markets = [] status_homepage = None def _get_json(self, *args, **kwargs): if "verbose" in kwargs: verbose = kwargs.pop("verbose") if verbose: print("Requesting", args[0], "with", kwargs) r = requests.get(*args, **kwargs) r = r.json() return r
[docs] def get_status(self, date, end=None, verbose=False): raise NotImplementedError()
[docs] def get_fuel_mix(self, date, end=None, verbose=False): """Get fuel mix in 5 minute intervals for a provided day Arguments: date (datetime or str): "latest", "today", or an object that can be parsed as a datetime for the day to return data. start (datetime or str): start of date range to return. alias for `date` parameter. Only specify one of `date` or `start`. end (datetime or str): "today" or an object that can be parsed as a datetime for the day to return data. Only used if requesting a range of dates. verbose (bool): print verbose output. Defaults to False. Returns: pd.Dataframe: dataframe with columns: Time and columns for each fuel type """ raise NotImplementedError()
[docs] def get_load(self, date, end=None, verbose=False): raise NotImplementedError()
[docs] def get_forecast(self, date, end=None, verbose=False): raise NotImplementedError()
[docs] def get_supply(self, date, end=None, verbose=False): raise NotImplemented()
[docs] def get_storage(self, date, end=None, verbose=False): raise NotImplementedError()
def _latest_lmp_from_today(self, market, locations, **kwargs): lmp_df = self.get_lmp( date="today", market=market, locations=locations, **kwargs ) # Assume sorted in ascending order latest_df = lmp_df.groupby("Location").last().reset_index() return latest_df def _latest_from_today(self, method, *args, **kwargs): data = method(date="today", *args, **kwargs) latest = data.iloc[-1] latest.index = latest.index.str.lower() return latest.to_dict() def _get_supply(self, date, end=None, verbose=False): if date == "latest": return self._latest_supply_from_fuel_mix() return self._supply_from_fuel_mix(date) def _supply_from_fuel_mix(self, date): df = self.get_fuel_mix(date) supply_df = df.pop("Time").to_frame() supply_df["Supply"] = df.sum(axis=1) # sum all the remaining columns return supply_df def _latest_supply_from_fuel_mix(self): mix = self.get_fuel_mix(date="latest") return {"time": mix.time, "supply": mix.total_production}
[docs]class GridStatus: def __init__(self, time, status, reserves, iso, notes=None, unit="MW") -> None: self.iso = iso self.time = time self.status = status self.reserves = reserves self.unit = unit self.notes = notes
[docs] def __repr__(self) -> str: s = self.iso.name + "\n" s += "Time: %s \n" % str(self.time) s += "Status: %s \n" % self.status if self.iso.status_homepage: s += "Status Homepage: %s \n" % self.iso.status_homepage if self.reserves is not None: s += "Reserves: %.0f %s" % (self.reserves, self.unit) if self.notes and len(self.notes): s += "Notes:\n" for n in self.notes: s += "- %s\n" % n return s
[docs]class FuelMix: def __init__(self, time, mix, iso=None, unit="MW") -> None: self.iso = iso self.time = time self.unit = unit mix_df = ( pd.Series(mix, name=self.unit) .sort_values( ascending=False, ) .to_frame() ) mix_df["Percent"] = mix_df[self.unit] / mix_df[self.unit].sum() * 100 mix_df.index.name = "Fuel" self._mix_df = mix_df
[docs] def __repr__(self) -> str: # TODO sort by magnitude s = "" if self.iso: s += "ISO: " + self.iso + "\n" s += "Total Production: %d %s \n" % (self.total_production, self.unit) s += "Time: %s \n" % self.time mix = self.mix mix["Percent"] = mix["Percent"].round(1) s += tabulate(mix, headers="keys", tablefmt="psql") return s
@property def total_production(self): return self.mix[self.unit].sum() @property def mix(self): return self._mix_df.copy()
""" Todos - fuel mix - how standardize should the mix be? - mark renewables - historical data - is the unit mh or mhw? - units for return values - documentation - include where the data is from - time step differences - what is the interval - api reference - - get_historical_fuel_mix vs get_fuel_mix_trend """