Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add functionality to load Grid objects with HIFLD grid model #566

Merged
merged 3 commits into from
Dec 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 69 additions & 2 deletions powersimdata/input/abstract_grid.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
import os

import pandas as pd

from powersimdata.input import const
from powersimdata.input.helpers import (
add_coord_to_grid_data_frames,
add_zone_to_grid_data_frames,
csv_to_data_frame,
)
from powersimdata.network.csv_reader import CSVReader


class AbstractGrid:
"""Grid Builder."""
"""Grid Builder. Child classes must assign self.top_dirname and
self.umbrella_interconnect before self.__init__ is called, or re-define the __init__
and/or methods called within the __init__ to avoid an AttributeError.
"""

def __init__(self):
"""Constructor"""
self.data_loc = None
self.interconnect = None
self.zone2id = {}
self.id2zone = {}
self.sub = pd.DataFrame()
Expand All @@ -20,6 +30,63 @@ def __init__(self):
self.bus = pd.DataFrame()
self.branch = pd.DataFrame()
self.storage = storage_template()
self._set_data_loc()
self._build_network()

def _set_data_loc(self):
"""Sets data location.

:raises IOError: if directory does not exist.
"""
data_loc = os.path.join(self.top_dirname, "data")
if os.path.isdir(data_loc) is False:
raise IOError("%s directory not found" % data_loc)
else:
self.data_loc = data_loc

def _build_network(self):
"""Build network."""
reader = CSVReader(self.data_loc)
self.bus = reader.bus
self.plant = reader.plant
self.branch = reader.branch
self.dcline = reader.dcline
self.gencost["after"] = self.gencost["before"] = reader.gencost

self._add_information_to_model()

if self.umbrella_interconnect not in self.interconnect:
self._drop_interconnect()

def _add_information_to_model(self):
self.sub = csv_to_data_frame(self.data_loc, "sub.csv")
self.bus2sub = csv_to_data_frame(self.data_loc, "bus2sub.csv")
self.id2zone = csv_to_data_frame(self.data_loc, "zone.csv").zone_name.to_dict()
self.zone2id = {v: k for k, v in self.id2zone.items()}

add_zone_to_grid_data_frames(self)
add_coord_to_grid_data_frames(self)

def _drop_interconnect(self):
"""Trim data frames to only keep information pertaining to the user
defined interconnect(s).

"""
for key, value in self.__dict__.items():
if key in ["sub", "bus2sub", "bus", "plant", "branch"]:
value.query("interconnect == @self.interconnect", inplace=True)
elif key == "gencost":
value["before"].query(
"interconnect == @self.interconnect", inplace=True
)
elif key == "dcline":
value.query(
"from_interconnect == @self.interconnect &"
"to_interconnect == @self.interconnect",
inplace=True,
)
self.id2zone = {k: self.id2zone[k] for k in self.bus.zone_id.unique()}
self.zone2id = {value: key for key, value in self.id2zone.items()}


def storage_template():
Expand Down
7 changes: 6 additions & 1 deletion powersimdata/input/grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from powersimdata.data_access.context import Context
from powersimdata.data_access.scenario_list import ScenarioListManager
from powersimdata.input.scenario_grid import FromREISE, FromREISEjl
from powersimdata.network.hifld.model import HIFLD
from powersimdata.network.model import ModelImmutables
from powersimdata.network.usa_tamu.constants import storage as tamu_storage
from powersimdata.network.usa_tamu.model import TAMU
Expand All @@ -13,7 +14,7 @@

class Grid:

SUPPORTED_MODELS = {"usa_tamu"}
SUPPORTED_MODELS = {"hifld", "usa_tamu"}
SUPPORTED_ENGINES = {"REISE", "REISE.jl"}

"""Grid
Expand Down Expand Up @@ -49,11 +50,15 @@ def __init__(self, interconnect, source="usa_tamu", engine="REISE"):
data = cached
elif source == "usa_tamu":
data = TAMU(interconnect)
elif source == "hifld":
data = HIFLD(interconnect)
elif os.path.splitext(source)[1] == ".mat":
if engine == "REISE":
data = FromREISE(source)
elif engine == "REISE.jl":
data = FromREISEjl(source)
else:
raise ValueError(f"Unknown source: {source}")

self.data_loc = data.data_loc
self.interconnect = data.interconnect
Expand Down
12 changes: 5 additions & 7 deletions powersimdata/input/scenario_grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,19 @@ def __init__(self, filename):

:param str filename: path to file.
"""
self.filename = filename
super().__init__()
self._set_data_loc(filename)

self._build_network()

def _set_data_loc(self, filename):
def _set_data_loc(self):
"""Sets data location.

:param str filename: path to file
:raises FileNotFoundError: if file does not exist.
"""
if os.path.isfile(filename) is False:
raise FileNotFoundError("%s file not found" % filename)
if os.path.isfile(self.filename) is False:
raise FileNotFoundError("%s file not found" % self.filename)
else:
self.data_loc = filename
self.data_loc = self.filename

def _read_network(self):
data = loadmat(self.data_loc, squeeze_me=True, struct_as_record=False)
Expand Down
Empty file.
Empty file.
105 changes: 105 additions & 0 deletions powersimdata/network/hifld/constants/plants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
_exports = [
"all_resources",
"carbon_per_mmbtu",
"carbon_per_mwh",
"carbon_resources",
"clean_resources",
"label2type",
"nox_per_mwh",
"renewable_resources",
"so2_per_mwh",
"type2color",
"type2hatchcolor",
"type2label",
]

type2color = {
"wind": "xkcd:green",
"solar": "xkcd:amber",
"hydro": "xkcd:light blue",
"ng": "xkcd:orchid",
"nuclear": "xkcd:silver",
"coal": "xkcd:light brown",
"geothermal": "xkcd:hot pink",
"dfo": "xkcd:royal blue",
"biomass": "xkcd:dark green",
"other": "xkcd:melon",
"storage": "xkcd:orange",
"wind_offshore": "xkcd:teal",
"solar_curtailment": "xkcd:amber",
"wind_curtailment": "xkcd:green",
"wind_offshore_curtailment": "xkcd:teal",
}

type2label = {
"nuclear": "Nuclear",
"geothermal": "Geo-thermal",
"coal": "Coal",
"dfo": "DFO",
"hydro": "Hydro",
"ng": "Natural Gas",
"solar": "Solar",
"wind": "Wind",
"wind_offshore": "Wind Offshore",
"biomass": "Biomass",
"other": "Other",
"storage": "Storage",
"solar_curtailment": "Solar Curtailment",
"wind_curtailment": "Wind Curtailment",
"wind_offshore_curtailment": "Offshore Wind Curtailment",
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know that PostREISE plotting functions rely on this dictionary and it is convenient to group generator type and curtailment but it is ugly. Curtailment color label/color should be moved in a dedicated dictionary. This completely out of the scope of this PR.


type2hatchcolor = {
"solar_curtailment": "xkcd:grey",
"wind_curtailment": "xkcd:grey",
"wind_offshore_curtailment": "xkcd:grey",
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here curtailment is not a generator type


label2type = {value: key for key, value in type2label.items()}

renewable_resources = {"solar", "wind", "wind_offshore"}
carbon_resources = {"coal", "ng", "dfo"}
clean_resources = renewable_resources | {"geothermal", "hydro", "nuclear"}
all_resources = carbon_resources | {"other"} | clean_resources


# MWh to kilograms of CO2
# Source: IPCC Special Report on Renewable Energy Sources and Climate Change
# Mitigation (2011), Annex II: Methodology, Table A.II.4, 50th percentile
# http://www.ipcc-wg3.de/report/IPCC_SRREN_Annex_II.pdf
carbon_per_mwh = {
"coal": 1001,
"dfo": 840,
"ng": 469,
}

# MMBTu of fuel per hour to kilograms of CO2 per hour
# Source: https://www.epa.gov/energy/greenhouse-gases-equivalencies-calculator-calculations-and-references
# = (Heat rate MMBTu/h) * (kg C/mmbtu) * (mass ratio CO2/C)
carbon_per_mmbtu = {
"coal": 26.05,
"dfo": 20.31,
"ng": 14.46,
}

# MWh to kilograms of NOx
# Source: EPA eGrid 2018, tab 'US18' (U.S. summary), columns AN to AP
# https://www.epa.gov/egrid/egrid-questions-and-answers
nox_per_mwh = {
"coal": 0.658,
"dfo": 1.537,
"ng": 0.179,
}

# MWh to kilograms of SO2
# Source: EPA eGrid 2018, tab 'US18' (U.S. summary), columns AV to AX
# https://www.epa.gov/egrid/egrid-questions-and-answers
so2_per_mwh = {
"coal": 0.965,
"dfo": 2.189,
"ng": 0.010,
}


def __dir__():
return sorted(_exports)
17 changes: 17 additions & 0 deletions powersimdata/network/hifld/constants/storage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
_exports = ["defaults"]

defaults = {
"duration": 4,
"min_stor": 0.05,
"max_stor": 0.95,
"InEff": 0.9,
"OutEff": 0.9,
"energy_value": 20,
"LossFactor": 0,
"terminal_min": 0,
"terminal_max": 1,
}


def __dir__():
return sorted(_exports)
Loading