Skip to content

Commit

Permalink
Merge pull request #179 from NREL/cwfd_debug
Browse files Browse the repository at this point in the history
Competitive wind farms bug fix
  • Loading branch information
MRossol authored Jun 23, 2020
2 parents 872c74b + 413ddd2 commit 1426fe3
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 88 deletions.
33 changes: 19 additions & 14 deletions reV/supply_curve/cli_supply_curve.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,26 +194,32 @@ def direct(ctx, sc_points, trans_table, fixed_charge_rate, sc_features,
if isinstance(transmission_costs, str):
transmission_costs = dict_str_load(transmission_costs)

if simple:
sc_fun = SupplyCurve.simple
else:
sc_fun = SupplyCurve.full

try:
out = sc_fun(sc_points, trans_table, fixed_charge_rate,
sc_features=sc_features,
transmission_costs=transmission_costs,
line_limited=line_limited,
sort_on=sort_on, wind_dirs=wind_dirs, n_dirs=n_dirs,
downwind=downwind, max_workers=max_workers)
if simple:
out = SupplyCurve.simple(sc_points, trans_table,
fixed_charge_rate,
sc_features=sc_features,
transmission_costs=transmission_costs,
sort_on=sort_on, wind_dirs=wind_dirs,
n_dirs=n_dirs, downwind=downwind,
max_workers=max_workers)
else:
out = SupplyCurve.full(sc_points, trans_table,
fixed_charge_rate,
sc_features=sc_features,
transmission_costs=transmission_costs,
line_limited=line_limited,
sort_on=sort_on, wind_dirs=wind_dirs,
n_dirs=n_dirs, downwind=downwind,
max_workers=max_workers)
except Exception as e:
logger.exception('Supply curve compute failed. Received the '
'following error:\n{}'.format(e))
raise e

fn_out = '{}.csv'.format(name)
fpath_out = os.path.join(out_dir, fn_out)
out.to_csv(fpath_out)
out.to_csv(fpath_out, index=False)

runtime = (time.time() - t0) / 60
logger.info('Supply curve complete. Time elapsed: {:.2f} min. '
Expand Down Expand Up @@ -270,8 +276,7 @@ def get_node_cmd(name, sc_points, trans_table, fixed_charge_rate, sc_features,

if simple:
args += '-s '

if line_limited:
elif line_limited:
args += '-ll '

if verbose:
Expand Down
21 changes: 15 additions & 6 deletions reV/supply_curve/competitive_wind_farms.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ def __init__(self, wind_dirs, sc_points, n_dirs=2, offshore=False):
self._sc_gids, self._sc_point_gids, self._mask = \
self._parse_sc_points(sc_points, offshore=offshore)

self._offshore = offshore

valid = np.isin(self.sc_point_gids, self._wind_dirs.index)
if not np.all(valid):
msg = ("'sc_points contains sc_point_gid values that do not "
Expand Down Expand Up @@ -211,11 +213,13 @@ def _parse_sc_points(sc_points, offshore=False):
"""
sc_points = CompetitiveWindFarms._parse_table(sc_points)
if 'offshore' in sc_points and not offshore:
logger.debug('Not including offshore supply curve points in'
logger.debug('Not including offshore supply curve points in '
'CompetitiveWindFarm')
mask = sc_points['offshore'] == 0
sc_points = sc_points.loc[mask]

mask = np.ones(int(1 + sc_points['sc_point_gid'].max()), dtype=bool)

sc_points = sc_points[['sc_gid', 'sc_point_gid']]
sc_gids = sc_points.set_index('sc_gid')
sc_gids = {k: int(v[0]) for k, v in sc_gids.iterrows()}
Expand All @@ -225,8 +229,6 @@ def _parse_sc_points(sc_points, offshore=False):
sc_point_gids = {int(k): v['sc_gid']
for k, v in sc_point_gids.iterrows()}

mask = np.ones(int(1 + sc_points['sc_point_gid'].max()), dtype=bool)

return sc_gids, sc_point_gids, mask

@staticmethod
Expand Down Expand Up @@ -405,6 +407,10 @@ def remove_noncompetitive_farm(self, sc_points, sort_on='total_lcoe',
wind farms
"""
sc_points = self._parse_table(sc_points)
if 'offshore' in sc_points and not self._offshore:
mask = sc_points['offshore'] == 0
sc_points = sc_points.loc[mask]

sc_points = sc_points.sort_values(sort_on)

sc_point_gids = sc_points['sc_point_gid'].values.astype(int)
Expand All @@ -427,8 +433,8 @@ def remove_noncompetitive_farm(self, sc_points, sort_on='total_lcoe',
return sc_points.loc[mask].reset_index(drop=True)

@classmethod
def run(cls, wind_dirs, sc_points, n_dirs=2, sort_on='total_lcoe',
downwind=False, out_fpath=None):
def run(cls, wind_dirs, sc_points, n_dirs=2, offshore=False,
sort_on='total_lcoe', downwind=False, out_fpath=None):
"""
Exclude given number of neighboring Supply Point gids based on most
prominent wind directions
Expand All @@ -443,6 +449,9 @@ def run(cls, wind_dirs, sc_points, n_dirs=2, sort_on='total_lcoe',
Supply curve point summary table
n_dirs : int, optional
Number of prominent directions to use, by default 2
offshore : bool
Flag as to whether offshore farms should be included during
CompetitiveWindFarms
sort_on : str, optional
column to sort on before excluding neighbors,
by default 'total_lcoe'
Expand All @@ -459,7 +468,7 @@ def run(cls, wind_dirs, sc_points, n_dirs=2, sort_on='total_lcoe',
Updated supply curve points after removing non-competative
wind farms
"""
cwf = cls(wind_dirs, sc_points, n_dirs=n_dirs)
cwf = cls(wind_dirs, sc_points, n_dirs=n_dirs, offshore=offshore)
sc_points = cwf.remove_noncompetitive_farm(sc_points, sort_on=sort_on,
downwind=downwind)

Expand Down
102 changes: 40 additions & 62 deletions reV/supply_curve/supply_curve.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from reV.supply_curve.competitive_wind_farms import CompetitiveWindFarms
from reV.utilities.exceptions import SupplyCurveInputError, SupplyCurveError

from rex.utilities.execution import SpawnProcessPool
from rex.utilities import parse_table, SpawnProcessPool

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -100,36 +100,6 @@ def __getitem__(self, gid):

return self._sc_points.iloc[i]

@staticmethod
def _load_table(table):
"""
Extract features and their capacity from supply curve transmission
mapping table
Parameters
----------
table : str | pd.DataFrame
Path to .csv or .json or DataFrame to parse
Returns
-------
table : pandas.DataFrame
DataFrame extracted from file path
"""
if isinstance(table, str):
if table.endswith('.csv'):
table = pd.read_csv(table)
elif table.endswith('.json'):
table = pd.read_json(table)
else:
raise ValueError('Cannot parse {}'.format(table))

elif not isinstance(table, pd.DataFrame):
raise ValueError("Table must be a .csv, .json, or "
"a pandas DataFrame")

return table

@staticmethod
def _parse_sc_points(sc_points, sc_features=None):
"""
Expand All @@ -149,12 +119,12 @@ def _parse_sc_points(sc_points, sc_features=None):
DataFrame of supply curve point summary with additional features
added if supplied
"""
sc_points = SupplyCurve._load_table(sc_points)
sc_points = parse_table(sc_points)
logger.debug('Supply curve points table imported with columns: {}'
.format(sc_points.columns.values.tolist()))

if sc_features is not None:
sc_features = SupplyCurve._load_table(sc_features)
sc_features = parse_table(sc_features)
merge_cols = [c for c in sc_features
if c in sc_points]
sc_points = sc_points.merge(sc_features, on=merge_cols, how='left')
Expand Down Expand Up @@ -401,7 +371,7 @@ def _parse_trans_table(trans_table):
Loaded transmission feature table.
"""

trans_table = SupplyCurve._load_table(trans_table)
trans_table = parse_table(trans_table)

drop_cols = ['sc_point_gid', 'sc_gid', 'cap_left']
for col in drop_cols:
Expand Down Expand Up @@ -581,26 +551,19 @@ def _exclude_noncompetitive_wind_farms(self, comp_wind_dirs, sc_gid,
gid = comp_wind_dirs.check_sc_gid(sc_gid)
if gid is not None:
if comp_wind_dirs.mask[gid]:
upwind_gids = comp_wind_dirs['upwind', gid]
for n in upwind_gids:
exclude_gids = comp_wind_dirs['upwind', gid]
if downwind:
exclude_gids = np.append(exclude_gids,
comp_wind_dirs['downwind', gid])
for n in exclude_gids:
check = comp_wind_dirs.exclude_sc_point_gid(n)
if check:
sc_gids = comp_wind_dirs['sc_gid', n]
for sc_id in sc_gids:
logger.debug('Excluding upwind sc_gid {}'
.format(sc_id))
self._mask[sc_id] = False

if downwind:
downwind_gids = comp_wind_dirs['downwind', gid]
for n in downwind_gids:
check = comp_wind_dirs.exclude_sc_point_gid(n)
if check:
sc_gids = comp_wind_dirs['sc_gid', n]
for sc_id in sc_gids:
logger.debug('Excluding downind sc_gid {}'
.format(sc_id))
self._mask[sc_id] = False
if self._mask[sc_id]:
logger.debug('Excluding sc_gid {}'
.format(sc_id))
self._mask[sc_id] = False

return comp_wind_dirs

Expand Down Expand Up @@ -698,6 +661,7 @@ def _full_sort(self, trans_table, comp_wind_dirs=None,
capacities[i])
if connect:
connected += 1
logger.debug('Connecting sc gid {}'.format(sc_gid))
self._mask[sc_gid] = False

conn_lists['trans_gid'][sc_gid] = trans_gid
Expand Down Expand Up @@ -730,7 +694,13 @@ def _full_sort(self, trans_table, comp_wind_dirs=None,
connections = connections[columns]
connections = connections.reset_index()

unconnected = np.where(self._mask[self._sc_gids])[0].tolist()
sc_gids = self._sc_points['sc_gid'].values
connected = connections['sc_gid'].values
logger.debug('Connected gids {} out of total supply curve gids {}'
.format(len(connected), len(sc_gids)))
unconnected = ~np.isin(sc_gids, connected)
unconnected = sc_gids[unconnected].tolist()

if unconnected:
msg = ("{} supply curve points were not connected to tranmission! "
"Unconnected sc_gid's: {}"
Expand All @@ -739,12 +709,8 @@ def _full_sort(self, trans_table, comp_wind_dirs=None,
warn(msg)

supply_curve = self._sc_points.merge(connections, on='sc_gid')
if comp_wind_dirs is not None:
sc_gids = comp_wind_dirs.sc_gids
mask = supply_curve['sc_gid'].isin(sc_gids)
supply_curve = supply_curve.loc[mask]

return supply_curve
return supply_curve.reset_index(drop=True)

def full_sort(self, trans_table=None, sort_on='total_lcoe',
columns=('trans_gid', 'trans_capacity', 'trans_type',
Expand Down Expand Up @@ -819,7 +785,7 @@ def full_sort(self, trans_table=None, sort_on='total_lcoe',
total_lcoe_fric=total_lcoe_fric,
sort_on=sort_on, columns=columns,
downwind=downwind)
supply_curve = supply_curve.reset_index(drop=True)

sum_cols = {'combined_cap_cost': ['array_cable_CAPEX',
'export_cable_CAPEX',
'trans_cap_cost']}
Expand All @@ -830,7 +796,7 @@ def full_sort(self, trans_table=None, sort_on='total_lcoe',
def simple_sort(self, trans_table=None, sort_on='total_lcoe',
columns=('trans_gid', 'trans_type', 'lcot', 'total_lcoe',
'trans_cap_cost'),
wind_dirs=None, n_dirs=2, downwind=False):
wind_dirs=None, n_dirs=2, downwind=False, offshore=False):
"""
Run simple supply curve sorting that does not take into account
available capacity
Expand All @@ -854,6 +820,9 @@ def simple_sort(self, trans_table=None, sort_on='total_lcoe',
Number of prominent directions to use, by default 2
downwind : bool, optional
Flag to remove downwind neighbors as well as upwind neighbors
offshore : bool
Flag as to whether offshore farms should be included during
CompetitiveWindFarms
Returns
-------
Expand Down Expand Up @@ -882,6 +851,7 @@ def simple_sort(self, trans_table=None, sort_on='total_lcoe',
supply_curve = CompetitiveWindFarms.run(wind_dirs,
supply_curve,
n_dirs=n_dirs,
offshore=offshore,
sort_on=sort_on,
downwind=downwind)

Expand All @@ -898,7 +868,8 @@ def full(cls, sc_points, trans_table, fcr, sc_features=None,
transmission_costs=None, line_limited=False, sort_on='total_lcoe',
columns=('trans_gid', 'trans_capacity', 'trans_type',
'trans_cap_cost', 'dist_mi', 'lcot', 'total_lcoe'),
max_workers=None, wind_dirs=None, n_dirs=2, downwind=False):
max_workers=None, wind_dirs=None, n_dirs=2, downwind=False,
offshore=False):
"""
Run full supply curve taking into account available capacity of
tranmission features when making connections.
Expand Down Expand Up @@ -940,6 +911,9 @@ def full(cls, sc_points, trans_table, fcr, sc_features=None,
Number of prominent directions to use, by default 2
downwind : bool, optional
Flag to remove downwind neighbors as well as upwind neighbors
offshore : bool
Flag as to whether offshore farms should be included during
CompetitiveWindFarms
Returns
-------
Expand All @@ -952,7 +926,7 @@ def full(cls, sc_points, trans_table, fcr, sc_features=None,
line_limited=line_limited, max_workers=max_workers)
supply_curve = sc.full_sort(sort_on=sort_on, columns=columns,
wind_dirs=wind_dirs, n_dirs=n_dirs,
downwind=downwind)
downwind=downwind, offshore=offshore)

return supply_curve

Expand All @@ -961,7 +935,8 @@ def simple(cls, sc_points, trans_table, fcr, sc_features=None,
transmission_costs=None, sort_on='total_lcoe',
columns=('trans_gid', 'trans_type', 'lcot', 'total_lcoe',
'trans_cap_cost'),
max_workers=None, wind_dirs=None, n_dirs=2, downwind=False):
max_workers=None, wind_dirs=None, n_dirs=2, downwind=False,
offshore=False):
"""
Run simple supply curve by connecting to the cheapest tranmission
feature.
Expand Down Expand Up @@ -1000,6 +975,9 @@ def simple(cls, sc_points, trans_table, fcr, sc_features=None,
Number of prominent directions to use, by default 2
downwind : bool, optional
Flag to remove downwind neighbors as well as upwind neighbors
offshore : bool
Flag as to whether offshore farms should be included during
CompetitiveWindFarms
Returns
-------
Expand All @@ -1012,6 +990,6 @@ def simple(cls, sc_points, trans_table, fcr, sc_features=None,
max_workers=max_workers)
supply_curve = sc.simple_sort(sort_on=sort_on, columns=columns,
wind_dirs=wind_dirs, n_dirs=n_dirs,
downwind=downwind)
downwind=downwind, offshore=offshore)

return supply_curve
Loading

0 comments on commit 1426fe3

Please sign in to comment.