# -*- coding: utf-8 -*-
"""
Aggregate powerrose and sort directions by dominance
"""
import h5py
import logging
import numpy as np
import pandas as pd
from reV.supply_curve.aggregation import Aggregation
from reV.supply_curve.extent import SupplyCurveExtent
from reVX.utilities.utilities import log_versions
from rex.utilities.utilities import row_col_indices
logger = logging.getLogger(__name__)
[docs]class ProminentWindDirections(Aggregation):
"""
Aggregate PowerRose to Supply Curve points and sort directions in order
of prominence. Then convert to equivalent sc_point_gid
"""
DIR_ORDER = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW']
def __init__(self, power_rose_h5_fpath, excl_fpath,
agg_dset='powerrose_100m', tm_dset='techmap_wtk',
resolution=128, excl_area=None):
"""
Parameters
----------
power_rose_h5_fpath : str
Filepath to .h5 file containing powerrose data
excl_fpath : str
Filepath to exclusions h5 with techmap dataset.
agg_dset : str, optional
Powerrose dataset to aggreate, by default 'powerrose_100m'
tm_dset : str, optional
Dataset name in the techmap file containing the
exclusions-to-resource mapping data,
by default 'techmap_wtk'
resolution : int, optional
SC resolution, must be input in combination with gid. Prefered
option is to use the row/col slices to define the SC point instead,
by default 128
excl_area : float | None
Area of an exclusion pixel in km2. None will try to infer the area
from the profile transform attribute in excl_fpath.
"""
log_versions(logger)
self.power_rose_h5_fpath = power_rose_h5_fpath
super().__init__(excl_fpath, tm_dset, agg_dset, resolution=resolution,
excl_area=excl_area)
@classmethod
def _map_direction_pos(cls, power_rose_h5_fpath):
"""
Map powerrose directions to sc row and column shifts
Parameters
----------
power_rose_h5_fpath : str
Filepath to .h5 file containing powerrose
Returns
-------
list
Pos of major cardinal directions in power rose data
"""
directions = cls.DIR_ORDER
with h5py.File(power_rose_h5_fpath, 'r') as f:
cardinal_dirs = list(f['cardinal_directions'][...].astype(str))
dir_pos = [cardinal_dirs.index(d) for d in directions]
return dir_pos
@staticmethod
def _compute_neighbors(sc_point_gids, shape):
"""
Compute neighboring supply curve point gids in following order:
['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW']
Parameters
----------
sc_point_gids : list | ndarray
Supply curve point gids to get neighbors for
shape : tuple
Exclusions gid shape
Returns
-------
neighbor_gids : ndarray
Neighboring supply curve point gids
"""
rows, cols = row_col_indices(sc_point_gids, shape[1])
row_shifts = [-1, -1, 0, 1, 1, 1, 0, -1]
rows = np.expand_dims(rows, axis=1) + row_shifts
mask = rows < 0
rows[mask] = 0
mask = rows > shape[0]
rows[mask] = shape[0]
col_shifts = [0, 1, 1, 1, 0, -1, -1, -1]
cols = np.expand_dims(cols, axis=1) + col_shifts
mask = cols < 0
cols[mask] = 0
mask = cols > shape[1]
cols[mask] = shape[1]
neighbor_gids = rows * shape[1] + cols
return neighbor_gids
@classmethod
def _get_neighbors(cls, excl_fpath, sc_point_gids, resolution=128):
"""
Get neighboring sc_point_gids for all given supply curve points
Parameters
----------
excl_fpath : str
Filepath to exclusions h5 with techmap dataset.
sc_point_gids : list | ndarray
List of supply curve point gids of interest
resolution : int, optional
SC resolution, must be input in combination with gid. Prefered
option is to use the row/col slices to define the SC point instead,
by default 64
Returns
-------
neighbor_gids : pandas.DataFrame
Neighboring sc_point_gids by cardinal direction
"""
with SupplyCurveExtent(excl_fpath, resolution=resolution) as sc:
shape = sc.shape
neighbor_gids = cls._compute_neighbors(sc_point_gids, shape)
directions = cls.DIR_ORDER
columns = ['{}_gid'.format(d) for d in directions]
neighbor_gids = pd.DataFrame(neighbor_gids,
columns=columns,
index=sc_point_gids)
return neighbor_gids
[docs] def prominent_directions(self, max_workers=None,
sites_per_worker=1000):
"""
Aggregate power rose data to supply curve points, find all neighboring
supply curve points, sort neighbors in order of prominent powerrose
directions
Parameters
----------
max_workers : int | None, optional
Number of cores to run summary on. None is all
available cpus, by default None
sites_per_worker : int, optional
Number of SC points to process on a single parallel worker,
by default 1000
Returns
-------
sc_pr : pandas.DataFrame
Update meta data table with neighboring supply curve point gids
and power-rose value at each cardinal direction
"""
agg_out = self.aggregate(self.power_rose_h5_fpath,
max_workers=max_workers,
sites_per_worker=sites_per_worker)
meta = agg_out.pop('meta')
neighbor_gids = self._get_neighbors(self._excl_fpath,
meta['sc_point_gid'].values,
resolution=self._resolution)
dir_pos = self._map_direction_pos(self.power_rose_h5_fpath)
sc_pr = agg_out.pop('powerrose_100m')[dir_pos].T
columns = ['{}_pr'.format(d)
for d in self.DIR_ORDER]
sc_pr = pd.DataFrame(sc_pr, index=meta['sc_point_gid'].values,
columns=columns)
sc_pr = neighbor_gids.join(sc_pr)
del neighbor_gids
sc_pr.index.name = 'sc_point_gid'
sc_pr = sc_pr.reset_index()
sc_pr = pd.merge(meta, sc_pr, on='sc_point_gid')
return sc_pr
[docs] @classmethod
def run(cls, power_rose_h5_fpath, excl_fpath,
agg_dset='powerrose_100m', tm_dset='techmap_wtk',
resolution=128, excl_area=None, max_workers=None,
sites_per_worker=1000, out_fpath=None):
"""
Aggregate powerrose to supply curve points, find neighboring supply
curve point gids and rank them based on prominent powerrose direction
Parameters
----------
power_rose_h5_fpath : str
Filepath to .h5 file containing power rose data, of same format as
WTK data with directions on axis 0 (rows) and sites on axis 1
(columns)
excl_fpath : str
Filepath to exclusions h5 with techmap dataset.
agg_dset : str, optional
Powerrose dataset to aggreate, by default 'powerrose_100m'
tm_dset : str, optional
Dataset name in the techmap file containing the
exclusions-to-resource mapping data,
by default 'techmap_wtk'
resolution : int, optional
SC resolution, must be input in combination with gid. Prefered
option is to use the row/col slices to define the SC point instead,
by default 128
excl_area : float | None
Area of an exclusion pixel in km2. None will try to infer the area
from the profile transform attribute in excl_fpath.
max_workers : int | None, optional
Number of cores to run summary on. None is all
available cpus, by default None
sites_per_worker : int, optional
Number of SC points to process on a single parallel worker,
by default 1000
out_fpath : str
Path to .csv file to save output table to
Returns
-------
sc_pr : pandas.DataFrame
Update meta data table with neighboring supply curve point gids
and power-rose value at each cardinal direction
"""
pr = cls(power_rose_h5_fpath, excl_fpath,
agg_dset=agg_dset, tm_dset=tm_dset,
resolution=resolution, excl_area=excl_area)
sc_pr = pr.prominent_directions(max_workers=max_workers,
sites_per_worker=sites_per_worker)
if out_fpath:
sc_pr.to_csv(out_fpath, index=False)
return sc_pr