Skip to content

Commit

Permalink
feat: scale factor table output
Browse files Browse the repository at this point in the history
  • Loading branch information
dmuldrew authored and Daniel Muldrew committed Apr 23, 2020
1 parent 07a99ad commit 1060345
Show file tree
Hide file tree
Showing 4 changed files with 276 additions and 13 deletions.
88 changes: 80 additions & 8 deletions powersimdata/design/clean_capacity_scaling.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@
import json
import os
import pickle
import warnings
from collections import defaultdict

from powersimdata.design.scenario_info import GridInfo
from abc import ABC, abstractmethod

class AbstractStrategyManager:

class AbstractStrategyManager(ABC):
"""
Base class for strategy objects, contains common functions
"""
Expand Down Expand Up @@ -82,6 +87,68 @@ def add_target(self, target_manager_obj):
"Input must be of TargetManager type"
self.targets[target_manager_obj.region_name] = target_manager_obj

@abstractmethod
def data_frame_of_next_capacities(self):
pass

def output_capacities_table(self, base_grid):
next_capacities = self.data_frame_of_next_capacities()[[
'next_solar_capacity', 'next_wind_capacity']]
grid_info = GridInfo(base_grid)
grid_resources = grid_info.get_available_resource('all')
gen_capacity = pd.DataFrame(
columns=grid_resources,
index=self.targets.keys())

for tar in self.targets:
row_dict = {}
assert set(self.targets[tar].resources.resources.keys())\
.issubset(set(grid_resources)), f"{tar} region contains " \
f"generator types not " \
f"contained within the grid!"
for res in grid_resources:
next_capacity = 0
if res == 'solar':
next_capacity = \
next_capacities.loc[tar, 'next_solar_capacity']
elif res == 'wind':
next_capacity = \
next_capacities.loc[tar, 'next_wind_capacity']
else:
try:
next_capacity = self.targets[tar].resources[res]\
.prev_capacity
except AttributeError:
print(f"Resource {res} not found in target region"
f" {tar}")
row_dict.update({res: next_capacity})
gen_capacity.loc[tar] = pd.Series(row_dict)
return gen_capacity

def create_scale_factor_table(self, base_grid, gen_capacity=None):
"""
Outputs a scaling factor table for targets and resource with
respect to the base USA grid for creating a change table.
:param base_grid: reference grid to calculate change table scaling
:param gen_capacity: dataframe of next capacities
factors
"""
grid_info = GridInfo(base_grid)
if gen_capacity is None:
gen_capacity = self.output_capacities_table(base_grid)
scale_factor_table = defaultdict(dict)
for tar, row in gen_capacity.iterrows():
for res in gen_capacity.columns:
base_target_resource_cap = grid_info.get_capacity(res, tar)
if base_target_resource_cap <= 0:
warnings.warn(
f"Base grid capacity is zero for target area {tar} "
f"and resource {res} and cannot be scaled!")
else:
scale_factor_table[res][tar] = \
row[res]/base_target_resource_cap
return scale_factor_table

@staticmethod
def load_target_from_json(target_name):
"""Loads JSON file of given target.
Expand Down Expand Up @@ -121,11 +188,12 @@ def __init__(self):
AbstractStrategyManager.__init__(self)

def set_addl_curtailment(self, additional_curtailment_table):
"""Sets additional curtailment for a region and particular resource type
"""Sets additional curtailment for a region and particular resource
type
:param dict additional_curtailment_table: nested dictionary structure of
the form: {‘Alabama’:{‘solar’: .2}, ‘Maryland’: {‘wind’: .1}}. The
numbers are curtailment factors between 0 and 1.
:param dict additional_curtailment_table: nested dictionary structure
of the form: {‘Alabama’:{‘solar’: .2}, ‘Maryland’: {‘wind’: .1}}. The
numbers are curtailment factors between 0 and 1.
"""
for region_name, target_obj in additional_curtailment_table.items():
for resource_name, curtailment_factor in target_obj.items():
Expand Down Expand Up @@ -205,9 +273,10 @@ def set_collab_addl_curtailment(self, addl_curtailment):
"""Sets additional curtailment for Collaborative Strategy
:param dict addl_curtailment: dictionary with '*solar*' and '*wind*'
keys defined: {"solar": .2, "wind": .3} with values between 0 and 1.
keys defined: {"solar": .2, "wind": .3} with values between 0 and
1.
"""
assert set(addl_curtailment.keys()) == set(["solar", "wind"])
assert set(addl_curtailment.keys()) == {"solar", "wind"}
assert 0 <= addl_curtailment["solar"] <= 1, "solar additional " \
"curtailment must be " \
"between 0 and 1"
Expand Down Expand Up @@ -634,6 +703,8 @@ def __getitem__(self, key):
:raises KeyError For attempts to use key not in the dictionary
:return: instance of Resource class
"""
if key == 'all':
return self.resources
try:
return self.resources[key]
except KeyError as e:
Expand Down Expand Up @@ -763,7 +834,8 @@ def set_generation(self, prev_generation, tolerance=1e-3):
def set_curtailment(self, prev_curtailment):
"""Sets curtailment from scenario run.
:param float prev_curtailment: calculated curtailment from scenario run.
:param float prev_curtailment: calculated curtailment from scenario
run.
"""
assert (prev_curtailment >= 0), \
"prev_curtailment must be greater than zero"
Expand Down
10 changes: 7 additions & 3 deletions powersimdata/design/scenario_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@ def _check_state(scenario):


class GridInfo:
def __init__(self, interconnect_name=None):
if interconnect_name is not None:
self.grid = Grid([interconnect_name])
"""
Lightweight class to obtain base grid information; use ScenarioInfo for
methods requiring more than base grid information
"""
def __init__(self, grid=None):
if grid:
self.grid = grid

def area_to_loadzone(self, area, area_type=None):
"""Map the query area to a list of loadzones
Expand Down
187 changes: 187 additions & 0 deletions powersimdata/design/tests/test_change_table_output.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import pandas as pd
from pytest import approx, raises
from powersimdata.input.grid import Grid
from powersimdata.design.clean_capacity_scaling\
import IndependentStrategyManager, Resource

from powersimdata.design.clean_capacity_scaling\
import TargetManager, ResourceManager, CollaborativeStrategyManager

from powersimdata.design.tests.test_strategies\
import _build_collaborative_test_atlantic_resources, \
_build_collaborative_test_pacific_resources


def test_change_table_output():
collab = _setup_collaborative_strategy()

mock_plant = {
'plant_id': [101, 102, 103, 104, 105, 106],
'bus_id': [1001, 1002, 1003, 1004, 1005, 1006],
'type': ['solar', 'wind', 'geo', 'solar', 'nuclear', 'hydro'],
'zone_name': ['Pacific', 'Atlantic', 'Atlantic', 'Pacific',
'Pacific', 'Pacific'],
'GenFuelCost': [0, 0, 3.3, 4.4, 5.5, 0],
'Pmin': [0, 0, 0, 0, 0, 0],
'Pmax': [50, 200, 80, 100, 120, 220],
}

scale_factor_table = collab.create_scale_factor_table(GridMock(mock_plant))

answer = {'solar': {'Pacific': 62.00925925925926},
'nuclear': {'Pacific': 35.833333333333336},
'hydro': {'Pacific': 17.727272727272727},
'wind': {'Atlantic': 45.24999999999999},
'geo': {'Atlantic': 50.0}}
assert len(scale_factor_table) == len(answer)
for gen_type, next_level in scale_factor_table.items():
assert len(next_level) == len(answer[gen_type])
for region_name, scale_factor in next_level.items():
assert scale_factor == approx(answer[gen_type][region_name])

next_capacities = collab.data_frame_of_next_capacities()[[
'next_solar_capacity', 'next_wind_capacity']]
assert scale_factor_table['solar']['Pacific'] == \
next_capacities.loc['Pacific', 'next_solar_capacity']/150
assert scale_factor_table['wind']['Atlantic'] == \
next_capacities.loc['Atlantic', 'next_wind_capacity']/200

assert scale_factor_table['geo']['Atlantic'] == collab.targets[
'Atlantic'].resources['geo'].prev_capacity / 80
assert scale_factor_table['nuclear']['Pacific'] == collab.targets[
'Pacific'].resources['nuclear'].prev_capacity / 120
assert scale_factor_table['hydro']['Pacific'] == collab.targets[
'Pacific'].resources['hydro'].prev_capacity / 220


def test_gen_type_not_in_grid():
collab = _setup_collaborative_strategy()

mock_plant = {
'plant_id': [101, 102, 103, 104, 105, 106],
'bus_id': [1001, 1002, 1003, 1004, 1005, 1006],
'type': ['solar', 'wind', 'geo', 'solar', 'nuclear', 'hydro'],
'zone_name': ['Pacific', 'Atlantic', 'Atlantic', 'Pacific',
'Pacific', 'Pacific'],
'GenFuelCost': [0, 0, 3.3, 4.4, 5.5, 0],
'Pmin': [0, 0, 0, 0, 0, 0],
'Pmax': [50, 200, 80, 100, 120, 220],
}

collab.targets['Pacific'].resources.resources['unobtanium'] = \
Resource('unobtanium', 42)
with raises(AssertionError):
collab.create_scale_factor_table(GridMock(mock_plant))


class GridMock:
def __init__(self, mock_plant):
self.zone2id = {'Pacific': 1, 'Atlantic': 2}
self.plant = pd.DataFrame(mock_plant)
self.plant.set_index('plant_id', inplace=True)


def _setup_collaborative_strategy():
# create Pacific
pacific_target = TargetManager('Pacific', 0, 'renewables', 200000*1000)
pacific_target.set_allowed_resources(['solar', 'wind', 'geo'])
pacific_resources = _build_collaborative_test_pacific_resources()
resources_dict = {}
for r in pacific_resources:
resources_dict[r.name] = r
pacific_resources = ResourceManager()
pacific_resources.resources = resources_dict
pacific_target.add_resource_manager(pacific_resources)

# create Atlantic
atlantic_target = TargetManager('Atlantic', 0.4, 'clean', 300000*1000)
atlantic_target.set_allowed_resources(['solar', 'wind', 'geo', 'hydro',
'nuclear'])
atlantic_resources = _build_collaborative_test_atlantic_resources()
resources_dict = {}
for r in atlantic_resources:
resources_dict[r.name] = r
atlantic_resources = ResourceManager()
atlantic_resources.resources = resources_dict
atlantic_target.add_resource_manager(pacific_resources)

collab = CollaborativeStrategyManager()
collab.set_next_sim_hours(8784)
collab.add_target(pacific_target)
collab.add_target(atlantic_target)
return collab


def test_change_table_output_from_capacities_dataframe():
gen_capacity = _create_capacities_dataframe()
strategy = IndependentStrategyManager()
scale_factor_table = strategy.create_scale_factor_table(
Grid(['Eastern']), gen_capacity)

answer = {'dfo':
{'Maine': 1.0,
'North Carolina': 1.0,
'Florida': 1.0},
'hydro':
{'Maine': 1.0,
'North Carolina': 1.0,
'Florida': 1.0},
'ng':
{'Maine': 1.0,
'North Carolina': 1.0,
'Florida': 1.0},
'other':
{'Maine': 1.0,
'North Carolina': 1.0,
'Florida': 1.0},
'solar':
{'Maine': 4.0,
'North Carolina': 1.0,
'Florida': 1.0},
'wind':
{'Maine': 1.0,
'North Carolina': 3.0,
'Florida': 20.0},
'coal':
{'North Carolina': 1.0,
'Florida': 1.0},
'nuclear':
{'North Carolina': 1.0,
'Florida': 1.0}}

assert len(scale_factor_table) == len(answer)
for gen_type, next_level in scale_factor_table.items():
assert len(next_level) == len(answer[gen_type])
for region_name, scale_factor in next_level.items():
assert scale_factor == approx(answer[gen_type][region_name])


def _create_capacities_dataframe():
data = {'zone_id': {'Maine': 1.0,
'North Carolina': 33.0,
'Florida': 66.0},
'coal': {'Maine': 0.0,
'North Carolina': 11494.205,
'Florida': 11090.296},
'dfo': {'Maine': 917.597,
'North Carolina': 490.80100000000004,
'Florida': 5663.306},
'hydro': {'Maine': 714.8,
'North Carolina': 1985.3899999999999,
'Florida': 55.701},
'ng': {'Maine': 1758.198,
'North Carolina': 13154.294,
'Florida': 47986.782999999996},
'other': {'Maine': 361.0,
'North Carolina': 365.754,
'Florida': 886.998},
'solar': {'Maine': 1.0 * 4,
'North Carolina': 3550.216,
'Florida': 1862.899},
'wind': {'Maine': 898.8,
'North Carolina': 209.0 * 3,
'Florida': 3.0 * 20},
'nuclear': {'Maine': 0.0,
'North Carolina': 4875.788,
'Florida': 3341.23}}
return pd.DataFrame(data)
4 changes: 2 additions & 2 deletions powersimdata/design/tests/test_target_manager_input.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from powersimdata.design.clean_capacity_scaling \
import AbstractStrategyManager, TargetManager
import IndependentStrategyManager, TargetManager
import pandas as pd


Expand Down Expand Up @@ -46,7 +46,7 @@ def test_populate_strategy_from_dataframe():
'allowed_resources': ['solar', 'wind']}
planning_dataframe = pd.DataFrame.from_dict(planning_data)

strategy = AbstractStrategyManager()
strategy = IndependentStrategyManager()
strategy.targets_from_data_frame(planning_dataframe)

assert strategy.targets['Pacific'].ce_category == \
Expand Down

0 comments on commit 1060345

Please sign in to comment.