Skip to content

Commit

Permalink
Merge pull request #284 from Breakthrough-Energy/daniel/capacity_scal…
Browse files Browse the repository at this point in the history
…ing_zones

Within capacity scaling, detect zones collisions and add ability to specify target area type
  • Loading branch information
danielolsen authored Sep 5, 2020
2 parents 02581e3 + 0f86c50 commit 9d6ca5c
Showing 1 changed file with 25 additions and 18 deletions.
43 changes: 25 additions & 18 deletions powersimdata/design/clean_capacity_scaling.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ def load_targets_from_csv(filename, drop_ignored=True):
"allowed_resources": "solar, wind",
"external_ce_addl_historical_amount": 0,
"solar_percentage": np.nan,
"area_type": np.nan,
}

# Validate input
Expand Down Expand Up @@ -97,22 +98,35 @@ def load_targets_from_csv(filename, drop_ignored=True):
return raw_targets


def _make_zonename2target(grid, targets, enforced_area_type=None):
def _make_zonename2target(grid, targets):
"""Creates a dictionary of {zone_name: target_name} pairs.
:param powersimdata.input.grid.Grid grid: Grid instance defining the set of zones.
:param iterable targets: a collection of strings, used to look up constituent zones.
:param str/None enforced_area_type: if provided, specify to area_to_loadzone()
that the area type must be this. If None, area_to_loadzone() proceeds in order.
:param pandas.DataFrame targets: a dataframe used to look up constituent zones.
:return: (*dict*) -- a dictionary of {zone_name: target_name} pairs.
:raises ValueError: if a zone is not present in any target areas, or
if a zone is present in more than one target area.
"""
target_zones = {
target_name: area_to_loadzone(grid, target_name, area_type=enforced_area_type)
for target_name in targets
target_name: area_to_loadzone(grid, target_name)
if pd.isnull(targets.loc[target_name, "area_type"])
else area_to_loadzone(grid, target_name, targets.loc[target_name, "area_type"])
for target_name in targets.index.tolist()
}
# Check for any collisions
zone_sets = target_zones.values()
if len(set.union(*zone_sets)) != sum([len(t) for t in zone_sets]):
zone_sets_list = [zone for _set in zone_sets for zone in _set]
duplicates = {zone for zone in zone_sets_list if zone_sets_list.count(zone) > 1}
error_areas = {
zone: {area for area, zone_set in target_zones.items() if zone in zone_set}
for zone in duplicates
}
error_msgs = [f"{k} within: {', '.join(v)}" for k, v in error_areas.items()]
raise ValueError(f"Zone(s) within multiple area! {'; '.join(error_msgs)}")
zonename2target = {}
for target_name, zone_set in target_zones.items():
# Filter out parts of states not in this interconnect
# Filter out parts of states not in the interconnect(s) in this Grid
filtered_zone_set = zone_set & set(grid.zone2id.keys())
zonename2target.update({zone: target_name for zone in filtered_zone_set})
untargetted_zones = set(grid.zone2id.keys()) - set(zonename2target.keys())
Expand All @@ -139,17 +153,13 @@ def _get_scenario_length(scenario):
return num_hours


def add_resource_data_to_targets(
input_targets, scenario, enforced_area_type=None, calculate_curtailment=False
):
def add_resource_data_to_targets(input_targets, scenario, calculate_curtailment=False):
"""Add resource data to targets. This data includes: previous capacity,
previous generation, previous capacity factor (with and without curtailment),
and previous curtailment.
:param pandas.DataFrame input_targets: table includeing target names, used to
summarize resource data.
:param powersimdata.scenario.scenario.Scenario scenario: A Scenario instance.
:param str/None enforced_area_type: if provided, specify to area_to_loadzone()
that the area type must be this. If None, area_to_loadzone() proceeds in order.
:return: (*pandas.DataFrame*) -- DataFrame of targets including resource data.
"""
targets = input_targets.copy()
Expand All @@ -159,8 +169,7 @@ def add_resource_data_to_targets(
scenario_length = _get_scenario_length(scenario)

# Map each zone in the grid to a target
targets_list = input_targets.index.tolist()
zonename2target = _make_zonename2target(grid, targets_list, enforced_area_type)
zonename2target = _make_zonename2target(grid, targets)
plant["target_area"] = [zonename2target[z] for z in plant["zone_name"]]

# Summarize important values by target area & type
Expand Down Expand Up @@ -216,19 +225,17 @@ def add_resource_data_to_targets(
return targets


def add_demand_to_targets(input_targets, scenario, enforced_area_type=None):
def add_demand_to_targets(input_targets, scenario):
"""Add demand data to targets.
:param pandas.DataFrame input_targets: table including target names, used to
summarize demand.
:param powersimdata.scenario.scenario.Scenario scenario: A Scenario instance.
:param str/None enforced_area_type: if provided, specify to area_to_loadzone()
that the area type must be this. If None, area_to_loadzone() proceeds in order.
:return: (*pandas.DataFrame*) -- DataFrame of targets including demand data.
"""
grid = scenario.state.get_grid()
targets = input_targets.copy()

zonename2target = _make_zonename2target(grid, targets.index, enforced_area_type)
zonename2target = _make_zonename2target(grid, targets)
zoneid2target = {grid.zone2id[z]: target for z, target in zonename2target.items()}
summed_demand = scenario.state.get_demand().sum().to_frame()
summed_demand["target"] = [zoneid2target[id] for id in summed_demand.index]
Expand Down

0 comments on commit 9d6ca5c

Please sign in to comment.