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

refactor: add compare module, move Grid/Scenario check functions from PostREISE to PowerSimData #507

Merged
merged 6 commits into from
Jun 25, 2021
Empty file.
25 changes: 25 additions & 0 deletions powersimdata/design/compare/generation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from powersimdata.design.compare.helpers import _reindex_as_necessary
from powersimdata.input.check import _check_data_frame


def calculate_plant_difference(plant1, plant2):
"""Calculate the capacity differences between two plant data frames. If capacity in
``plant2`` is larger than capacity in ``plant1``, the return will be positive.

:param pandas.DataFrame plant1: first plant data frame.
:param pandas.DataFrame plant2: second plant data frame.
:return: (*pandas.DataFrame*) -- merged data frames with a new 'diff' column.
"""
_check_data_frame(plant1, "plant1")
_check_data_frame(plant2, "plant2")
# Reindex so that we don't get NaN when calculating upgrades for new generators
plant1, plant2 = _reindex_as_necessary(plant1, plant2, ["bus_id", "type"])
plant_merge = plant1.merge(
plant2, how="outer", right_index=True, left_index=True, suffixes=(None, "_2")
)
plant_merge["diff"] = plant_merge.Pmax_2.fillna(0) - plant_merge.Pmax.fillna(0)
# Ensure that lats & lons get filled in as necessary from plant2 entries
for l in ["lat", "lon"]:
plant_merge[l].fillna(plant_merge[f"{l}_2"], inplace=True)

return plant_merge
19 changes: 19 additions & 0 deletions powersimdata/design/compare/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
def _reindex_as_necessary(df1, df2, check_columns):
"""Check for indices with mismatched entries in specified columns. If any entries
don't match, reindex based on these columns such that there are no shared indices
with mismatched entries in these columns.

:param pandas.DataFrame df1: data frame containing ``check_columns``.
:param pandas.DataFrame df2: data frame containing ``check_columns``.
:param iterable check_columns: column
:return: (*tuple*) -- data frames, reindexed as necessary.
"""
# Coerce to list for safety, since pandas interprets lists and tuples differently
check_columns_list = list(check_columns)
shared_indices = set(df1.index) & set(df2.index)
check1 = df1.loc[shared_indices, check_columns_list]
check2 = df2.loc[shared_indices, check_columns_list]
if not check1.equals(check2):
df1 = df1.set_index(keys=check_columns_list, drop=False, append=True)
df2 = df2.set_index(keys=check_columns_list, drop=False, append=True)
return df1, df2
61 changes: 61 additions & 0 deletions powersimdata/design/compare/transmission.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from powersimdata.design.compare.helpers import _reindex_as_necessary
from powersimdata.input.check import _check_data_frame, _check_grid_type


def calculate_branch_difference(branch1, branch2):
"""Calculate the capacity differences between two branch data frames. If capacity in
``branch2`` is larger than capacity in ``branch1``, the return will be positive.

:param pandas.DataFrame branch1: first branch data frame.
:param pandas.DataFrame branch2: second branch data frame.
:param float/int difference_threshold: drop any changes less than this value from
the returned Series.
:return: (*pandas.Series*) -- capacity difference between the two branch data
frames.
"""
_check_data_frame(branch1, "branch1")
_check_data_frame(branch2, "branch2")
if not ("rateA" in branch1.columns) and ("rateA" in branch2.columns):
raise ValueError("branch1 and branch2 both must have 'rateA' columns")
branch1, branch2 = _reindex_as_necessary(
branch1, branch2, ["from_bus_id", "to_bus_id"]
)
branch_merge = branch1.merge(
branch2, how="outer", right_index=True, left_index=True, suffixes=(None, "_2")
)
branch_merge["diff"] = branch_merge.rateA_2.fillna(0) - branch_merge.rateA.fillna(0)
# Ensure that lats & lons get filled in as necessary from branch2 entries
for l in ["from_lat", "from_lon", "to_lat", "to_lon"]:
branch_merge[l].fillna(branch_merge[f"{l}_2"], inplace=True)

return branch_merge


def calculate_dcline_differences(grid1, grid2):
"""Calculate capacity differences between dcline tables, and add to/from lat/lon.

:param powersimdata.input.grid.Grid grid1: first grid instance.
:param powersimdata.input.grid.Grid grid2: second grid instance.
:return: (*pandas.DataFrame*) -- data frame with all indices, plus new columns:
diff, from_lat, from_lon, to_lat, to_lon.
"""
_check_grid_type(grid1)
_check_grid_type(grid2)
dcline1, dcline2 = _reindex_as_necessary(
grid1.dcline, grid2.dcline, ["from_bus_id", "to_bus_id"]
)
# Get latitudes and longitudes for to & from buses
for dcline, grid in [(dcline1, grid1), (dcline2, grid2)]:
dcline["from_lat"] = grid.bus.loc[dcline.from_bus_id, "lat"].to_numpy()
dcline["from_lon"] = grid.bus.loc[dcline.from_bus_id, "lon"].to_numpy()
dcline["to_lat"] = grid.bus.loc[dcline.to_bus_id, "lat"].to_numpy()
dcline["to_lon"] = grid.bus.loc[dcline.to_bus_id, "lon"].to_numpy()
dc_merge = dcline1.merge(
dcline2, how="outer", right_index=True, left_index=True, suffixes=(None, "_2")
)
dc_merge["diff"] = dc_merge.Pmax_2.fillna(0) - dc_merge.Pmax.fillna(0)
# Ensure that lats & lons get filled in as necessary from grid2.dcline entries
for l in ["from_lat", "from_lon", "to_lat", "to_lon"]:
dc_merge[l].fillna(dc_merge[f"{l}_2"], inplace=True)

return dc_merge
32 changes: 16 additions & 16 deletions powersimdata/design/investment/investment_costs.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
import numpy as np
import pandas as pd

from powersimdata.design.compare.generation import calculate_plant_difference
from powersimdata.design.compare.transmission import (
calculate_branch_difference,
calculate_dcline_differences,
)
from powersimdata.design.investment import const
from powersimdata.design.investment.create_mapping_files import (
bus_to_neem_reg,
Expand Down Expand Up @@ -68,12 +73,11 @@ def calculate_ac_inv_costs(
else:
_check_grid_models_match(base_grid, grid)

# find upgraded AC lines
grid_new = cp.deepcopy(grid)
# Reindex so that we don't get NaN when calculating upgrades for new branches
base_grid.branch = base_grid.branch.reindex(grid_new.branch.index).fillna(0)
grid_new.branch.rateA = grid.branch.rateA - base_grid.branch.rateA
grid_new.branch = grid_new.branch[grid_new.branch.rateA != 0.0]
# find upgraded AC lines
capacity_difference = calculate_branch_difference(base_grid.branch, grid.branch)
grid_new.branch = grid.branch.assign(rateA=capacity_difference.diff)
grid_new.branch = grid_new.branch.query("rateA != 0.0")
if exclude_branches is not None:
present_exclude_branches = set(exclude_branches) & set(grid_new.branch.index)
grid_new.branch.drop(index=present_exclude_branches, inplace=True)
Expand Down Expand Up @@ -290,11 +294,10 @@ def calculate_dc_inv_costs(scenario, sum_results=True, base_grid=None):
_check_grid_models_match(base_grid, grid)

grid_new = cp.deepcopy(grid)
# Reindex so that we don't get NaN when calculating upgrades for new DC lines
base_grid.dcline = base_grid.dcline.reindex(grid_new.dcline.index).fillna(0)
# find upgraded DC lines
grid_new.dcline.Pmax = grid.dcline.Pmax - base_grid.dcline.Pmax
grid_new.dcline = grid_new.dcline[grid_new.dcline.Pmax != 0.0]
capacity_difference = calculate_dcline_differences(base_grid.dcline, grid.dcline)
grid_new.dcline = grid.dcline.assign(Pmax=capacity_difference.diff)
grid_new.dcline = grid_new.dcline.query("Pmax != 0.0")

costs = _calculate_dc_inv_costs(grid_new, sum_results)
return costs
Expand Down Expand Up @@ -383,11 +386,11 @@ def calculate_gen_inv_costs(
else:
_check_grid_models_match(base_grid, grid)

# Find change in generation capacity
grid_new = cp.deepcopy(grid)
# Reindex so that we don't get NaN when calculating upgrades for new generators
base_grid.plant = base_grid.plant.reindex(grid_new.plant.index).fillna(0)
grid_new.plant.Pmax = grid.plant.Pmax - base_grid.plant.Pmax
# Find change in generation capacity
capacity_difference = calculate_plant_difference(base_grid.plant, grid.plant)
grid_new.plant = grid.plant.assign(Pmax=capacity_difference.diff)
grid_new.plant = grid_new.plant.query("Pmax >= 0.01")
# Find change in storage capacity
# Reindex so that we don't get NaN when calculating upgrades for new storage
base_grid.storage["gen"] = base_grid.storage["gen"].reindex(
Expand All @@ -398,9 +401,6 @@ def calculate_gen_inv_costs(
)
grid_new.storage["gen"]["type"] = "storage"

# Drop small changes
grid_new.plant = grid_new.plant[grid_new.plant.Pmax > 0.01]

costs = _calculate_gen_inv_costs(grid_new, year, cost_case, sum_results)
return costs

Expand Down
Loading