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

Enable roundtrip conversion PyPSA Network <--> PowerSimData Grid objects #675

Merged
merged 5 commits into from
Oct 5, 2022
Merged
Show file tree
Hide file tree
Changes from 3 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
54 changes: 51 additions & 3 deletions powersimdata/input/const/pypsa_const.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,18 +124,66 @@
"storage_gen": {
"rename": {
"bus_id": "bus",
"Pg": "p",
"Qg": "q",
},
"default_drop_cols": [
"GenFuelCost",
"GenIOB",
"GenIOC",
"GenIOD",
"Pc1",
"Pc2",
"Pg",
"Pmax",
"Pmin",
"Qc1max",
"Qc1min",
"Qc2max",
"Qc2min",
"Qg",
"Qmax",
"Qmin",
"Vg",
"apf",
"interconnect",
"lat",
"lon",
"mBase",
"mu_Pmax",
"mu_Pmin",
"mu_Qmax",
"mu_Qmin",
"ramp_10",
"ramp_30",
"ramp_agc",
"ramp_q",
"status",
"zone_id",
"zone_name",
],
},
"storage_gencost": {
"rename": {"c1": "marginal_cost"},
"rename": {"c1": "marginal_cost", "type": "cost_type"},
"default_drop_cols": ["c0", "c2", "interconnect", "n", "shutdown", "startup"],
},
"storage_storagedata": {
"rename": {
"OutEff": "efficiency_dispatch",
"InEff": "efficiency_store",
"LossFactor": "standing_loss",
"duration": "max_hours",
"genfuel": "carrier",
},
"default_drop_cols": [
"ExpectedTerminalStorageMax",
"ExpectedTerminalStorageMin",
"InitialStorageCost",
"InitialStorageLowerBound",
"InitialStorageUpperBound",
"MaxStorageLevel",
"MinStorageLevel",
"TerminalStoragePrice",
"UnitIdx",
"rho",
],
},
}
124 changes: 67 additions & 57 deletions powersimdata/input/converter/pypsa_to_grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from powersimdata.input.abstract_grid import AbstractGrid
from powersimdata.input.const import grid_const
from powersimdata.input.const.pypsa_const import pypsa_const
from powersimdata.input.grid import Grid
from powersimdata.network.constants.storage import storage as storage_const


Expand Down Expand Up @@ -101,8 +102,13 @@ def _get_storage_gencost(n, storage_type):
"Inapplicable storage_type passed to function _get_storage_gencost."
)

# There are "type" columns in gen and gencost with "type" column reserved
# for gen dataframe, hence drop it here before renaming
df_gencost = df_gencost.drop(columns="type", errors="ignore")
storage_gencost = _translate_df(df_gencost, "storage_gencost")
storage_gencost.assign(type=2, n=3, c0=0, c2=0)
if "type" in storage_gencost:
storage_gencost["type"] = pd.to_numeric(storage_gencost.type, errors="ignore")

return storage_gencost

Expand Down Expand Up @@ -130,6 +136,8 @@ def _get_storage_gen(n, storage_type):
storage_gen["Vg"] = 1
storage_gen["mBase"] = 100
storage_gen["status"] = 1
storage_gen["bus_id"] = pd.to_numeric(storage_gen.bus_id, errors="ignore")
storage_gen["type"] = pd.to_numeric(storage_gen.type, errors="ignore")

return storage_gen

Expand Down Expand Up @@ -164,27 +172,28 @@ def _read_network(self, n, add_pypsa_cols=True):
data_loc = "pypsa"

# Read in data from PyPSA
bus_in_pypsa = n.buses
sub_in_pypsa = pd.DataFrame()
bus2sub_in_pypsa = pd.DataFrame()
bus_pypsa = n.buses
sub_pypsa = pd.DataFrame()
bus2sub_pypsa = pd.DataFrame()
gencost_cols = ["start_up_cost", "shut_down_cost", "marginal_cost"]
gencost_in_pypsa = n.generators[gencost_cols]
plant_in_pypsa = n.generators.drop(gencost_cols, axis=1)
lines_in_pypsa = n.lines
transformers_in_pypsa = n.transformers
branch_in_pypsa = pd.concat(
[lines_in_pypsa, transformers_in_pypsa],
gencost_pypsa = n.generators[gencost_cols]
plant_pypsa = n.generators.drop(gencost_cols, axis=1)
lines_pypsa = n.lines
transformers_pypsa = n.transformers
branch_pypsa = pd.concat(
[lines_pypsa, transformers_pypsa],
join="outer",
)
dcline_in_pypsa = n.links[lambda df: df.index.str[:3] != "sub"]
storageunits_in_pypsa = n.storage_units
stores_in_pypsa = n.stores
dcline_pypsa = n.links[lambda df: df.index.str[:3] != "sub"]
storageunits_pypsa = n.storage_units
stores_pypsa = n.stores

# bus
df = bus_in_pypsa.drop(columns="type")
df = bus_pypsa.drop(columns="type")
bus = _translate_df(df, "bus")
bus.type.replace(["PQ", "PV", "Slack", ""], [1, 2, 3, 4], inplace=True)
bus["bus_id"] = bus.index
bus["type"] = bus.type.replace(
["(?i)PQ", "(?i)PV", "(?i)Slack", ""], [1, 2, 3, 4], regex=True
).astype(int)

# zones mapping
# only relevant if the PyPSA network was originally created from PSD
Expand All @@ -204,28 +213,28 @@ def _read_network(self, n, add_pypsa_cols=True):
sub_cols = ["name", "interconnect_sub_id", "lat", "lon", "interconnect"]
sub = bus[bus.is_substation][sub_cols]
sub.index = sub[sub.index.str.startswith("sub")].index.str[3:]
sub_in_pypsa_cols = [
sub_pypsa_cols = [
"name",
"interconnect_sub_id",
"y",
"x",
"interconnect",
]
sub_in_pypsa = bus_in_pypsa[bus_in_pypsa.is_substation][sub_in_pypsa_cols]
sub_in_pypsa.index = sub_in_pypsa[
sub_in_pypsa.index.str.startswith("sub")
sub_pypsa = bus_pypsa[bus_pypsa.is_substation][sub_pypsa_cols]
sub_pypsa.index = sub_pypsa[
sub_pypsa.index.str.startswith("sub")
].index.str[3:]

bus = bus[~bus.is_substation]
bus_in_pypsa = bus_in_pypsa[~bus_in_pypsa.is_substation]
bus_pypsa = bus_pypsa[~bus_pypsa.is_substation]

bus2sub = bus[["substation", "interconnect"]].copy()
bus2sub["sub_id"] = pd.to_numeric(
bus2sub.pop("substation").str[3:], errors="ignore"
)
bus2sub_in_pypsa = bus_in_pypsa[["substation", "interconnect"]].copy()
bus2sub_in_pypsa["sub_id"] = pd.to_numeric(
bus2sub_in_pypsa.pop("substation").str[3:], errors="ignore"
bus2sub_pypsa = bus_pypsa[["substation", "interconnect"]].copy()
bus2sub_pypsa["sub_id"] = pd.to_numeric(
bus2sub_pypsa.pop("substation").str[3:], errors="ignore"
)
else:
warnings.warn("Substations could not be parsed.")
Expand All @@ -239,28 +248,32 @@ def _read_network(self, n, add_pypsa_cols=True):
bus[["Bs", "Gs"]] = shunts[["Bs", "Gs"]]

# plant
df = plant_in_pypsa.drop(columns="type")
df = plant_pypsa.drop(columns="type")
plant = _translate_df(df, "generator")
plant["ramp_30"] = n.generators["ramp_limit_up"].fillna(0)
plant["Pmin"] *= plant["Pmax"] # from relative to absolute value
plant["bus_id"] = pd.to_numeric(plant.bus_id, errors="ignore")

# generation costs
# for type: type of cost model (1 piecewise linear, 2 polynomial), n: number of parameters for total cost function, c(0) to c(n-1): parameters
gencost = _translate_df(gencost_in_pypsa, "gencost")
gencost = gencost.assign(type=2, n=3, c0=0, c2=0)
gencost = _translate_df(gencost_pypsa, "gencost")
gencost = gencost.assign(
type=2, n=3, c0=0, c2=0, interconnect=plant.get("interconnect")
)

# branch
# lines
drop_cols = ["x", "r", "b", "g"]
df = lines_in_pypsa.drop(columns=drop_cols, errors="ignore")
df = lines_pypsa.drop(columns=drop_cols, errors="ignore")
lines = _translate_df(df, "branch")
lines["branch_device_type"] = "Line"
lines["branch_device_type"] = lines.get("branch_device_type", "Line")

# transformers
df = transformers_in_pypsa.drop(columns=drop_cols, errors="ignore")
df = transformers_pypsa.drop(columns=drop_cols, errors="ignore")
transformers = _translate_df(df, "branch")
transformers["branch_device_type"] = "Transformer"
transformers["branch_device_type"] = transformers.get(
"branch_device_type", "Transformer"
)

branch = pd.concat([lines, transformers], join="outer")
# BE model assumes a 100 MVA base, pypsa "assumes" a 1 MVA base
Expand All @@ -270,7 +283,7 @@ def _read_network(self, n, add_pypsa_cols=True):
branch["to_bus_id"] = pd.to_numeric(branch.to_bus_id, errors="ignore")

# DC lines
dcline = _translate_df(dcline_in_pypsa, "link")
dcline = _translate_df(dcline_pypsa, "link")
dcline["Pmin"] *= dcline["Pmax"] # convert relative to absolute
dcline["from_bus_id"] = pd.to_numeric(dcline.from_bus_id, errors="ignore")
dcline["to_bus_id"] = pd.to_numeric(dcline.to_bus_id, errors="ignore")
Expand All @@ -282,6 +295,7 @@ def _read_network(self, n, add_pypsa_cols=True):
storage_gen_stores = _get_storage_gen(n, "stores")
storage_gencost_stores = _get_storage_gencost(n, "stores")
storage_storagedata_stores = _get_storage_storagedata(n, "stores")
storage_genfuel = list(n.storage_units.carrier) + list(n.stores.carrier)

# Pull operational properties into grid object
if len(n.snapshots) == 1:
Expand Down Expand Up @@ -318,49 +332,45 @@ def _read_network(self, n, add_pypsa_cols=True):
"storage_storagedata_stores",
]
values = [
(bus, bus_in_pypsa, grid_const.col_name_bus),
(sub, sub_in_pypsa, grid_const.col_name_sub),
(bus2sub, bus2sub_in_pypsa, grid_const.col_name_bus2sub),
(plant, plant_in_pypsa, grid_const.col_name_plant),
(gencost, gencost_in_pypsa, grid_const.col_name_gencost),
(branch, branch_in_pypsa, grid_const.col_name_branch),
(dcline, dcline_in_pypsa, grid_const.col_name_dcline),
(bus, bus_pypsa, grid_const.col_name_bus),
(sub, sub_pypsa, grid_const.col_name_sub),
(bus2sub, bus2sub_pypsa, grid_const.col_name_bus2sub),
(plant, plant_pypsa, grid_const.col_name_plant),
(gencost, gencost_pypsa, grid_const.col_name_gencost),
(branch, branch_pypsa, grid_const.col_name_branch),
(dcline, dcline_pypsa, grid_const.col_name_dcline),
(
storage_gen_storageunits,
storageunits_in_pypsa,
storageunits_pypsa,
grid_const.col_name_plant,
),
(
storage_gencost_storageunits,
storageunits_in_pypsa,
storageunits_pypsa,
grid_const.col_name_gencost,
),
(
storage_storagedata_storageunits,
storageunits_in_pypsa,
storageunits_pypsa,
grid_const.col_name_storage_storagedata,
),
(storage_gen_stores, stores_in_pypsa, grid_const.col_name_plant),
(storage_gencost_stores, stores_in_pypsa, grid_const.col_name_gencost),
(storage_gen_stores, stores_pypsa, grid_const.col_name_plant),
(storage_gencost_stores, stores_pypsa, grid_const.col_name_gencost),
(
storage_storagedata_stores,
stores_in_pypsa,
stores_pypsa,
grid_const.col_name_storage_storagedata,
),
]

for k, v in zip(keys, values):
df_psd, df_pypsa, const_location = v

# Reindex
if k == "branch":
const_location += ["branch_device_type"]

df_psd = df_psd.reindex(const_location, axis="columns")

# Add renamed PyPSA columns
if add_pypsa_cols:
df_pypsa = df_pypsa.add_prefix("PyPSA_")
df_pypsa = df_pypsa.add_prefix("pypsa_")

df_psd = pd.concat([df_psd, df_pypsa], axis=1)

Expand All @@ -369,25 +379,19 @@ def _read_network(self, n, add_pypsa_cols=True):

data[k] = df_psd

# Append individual columns
if not n.shunt_impedances.empty:
data["bus"]["includes_pypsa_shunt"] = True
else:
data["bus"]["includes_pypsa_shunt"] = False

for df in (
data["storage_gen_storageunits"],
data["storage_gencost_storageunits"],
data["storage_storagedata_storageunits"],
):
df["which_storage_in_pypsa"] = "storage_units"
df["pypsa_component"] = "storage_units"

for df in (
data["storage_gen_stores"],
data["storage_gencost_stores"],
data["storage_storagedata_stores"],
):
df["which_storage_in_pypsa"] = "stores"
df["pypsa_component"] = "stores"

# Build PSD grid object
self.data_loc = data_loc
Expand Down Expand Up @@ -417,6 +421,7 @@ def _read_network(self, n, add_pypsa_cols=True):
],
join="outer",
)
self.storage["genfuel"] = storage_genfuel
self.storage.update(storage_const)

# Set index names to match PSD
Expand Down Expand Up @@ -446,3 +451,8 @@ def _translate_pnl(self, pnl, key):
{v: pnl[k].iloc[0] for k, v in translators.items() if k in pnl}, axis=1
)
return df

@property
def __class__(self):
"""If anyone asks, I'm a Grid object!"""
return Grid
Loading