Skip to content

Commit

Permalink
setting up cli to run tech-mapping, updating docstrings, argument nam…
Browse files Browse the repository at this point in the history
…es, and defaults for consistnecy with supplycurve-aggregation
  • Loading branch information
mjgleason committed Jan 23, 2025
1 parent fb1f932 commit 524f9b1
Show file tree
Hide file tree
Showing 8 changed files with 243 additions and 53 deletions.
4 changes: 3 additions & 1 deletion examples/bespoke_wind_plants/single_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@
shutil.copy(RES.format(2013), res_fp.format(2013))
res_fp = res_fp.format('*')

TechMapping.run(excl_fp, RES.format(2012), dset=TM_DSET, max_workers=1)
TechMapping.run(
excl_fp, RES.format(2012), tm_dset=TM_DSET, max_workers=1
)
bsp = BespokeSinglePlant(gid, excl_fp, res_fp, TM_DSET,
SAM_SYS_INPUTS,
objective_function, cost_function,
Expand Down
6 changes: 4 additions & 2 deletions reV/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from reV.handlers.cli_multi_year import my_command
from reV.supply_curve.cli_sc_aggregation import sc_agg_command
from reV.supply_curve.cli_supply_curve import sc_command
from reV.supply_curve.cli_tech_mapping import tm_command
from reV.rep_profiles.cli_rep_profiles import rep_profiles_command
from reV.hybrids.cli_hybrids import hybrids_command
from reV.nrwal.cli_nrwal import nrwal_command
Expand All @@ -24,8 +25,9 @@


commands = [bespoke_command, gen_command, econ_command, collect_command,
my_command, sc_agg_command, sc_command, rep_profiles_command,
hybrids_command, nrwal_command, qa_qc_command]
my_command, tm_command, sc_agg_command, sc_command,
rep_profiles_command, hybrids_command, nrwal_command,
qa_qc_command]
main = make_cli(commands, info={"name": "reV", "version": __version__})
main.add_command(qa_qc_extra)
main.add_command(project_points)
Expand Down
2 changes: 1 addition & 1 deletion reV/supply_curve/aggregation.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ def _validate_tech_mapping(self):
)
try:
TechMapping.run(
self._excl_fpath, self._res_fpath, dset=self._tm_dset
self._excl_fpath, self._res_fpath, tm_dset=self._tm_dset
)
except Exception as e:
msg = (
Expand Down
66 changes: 66 additions & 0 deletions reV/supply_curve/cli_tech_mapping.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# -*- coding: utf-8 -*-
"""
reV Tech Mapping CLI utility functions.
"""
import logging

from gaps.cli import as_click_command, CLICommandFromClass

from reV.supply_curve.tech_mapping import TechMapping
from reV.utilities import ModuleName
from reV.utilities.exceptions import ConfigError
from reV.supply_curve.cli_sc_aggregation import _format_res_fpath

logger = logging.getLogger(__name__)


def _preprocessor(config):
"""Preprocess tech mapping config user input.
Parameters
----------
config : dict
User configuration file input as (nested) dict.
Returns
-------
dict
Updated config file.
"""
_validate_excl_fpath(config)
config = _format_res_fpath(config)
_validate_tm_dset(config)

return config


def _validate_excl_fpath(config):
paths = config["excl_fpath"]
if isinstance(paths, list):
raise ConfigError(
"Multiple exclusion file paths passed via excl_fpath. "
"Cannot run tech mapping with arbitrary multiple exclusion. "
"Specify a single exclusion file path to write to."
)


def _validate_tm_dset(config):
if config.get("tm_dset") is None:
raise ConfigError(
"tm_dset must be specified to run tech mapping."
)


tm_command = CLICommandFromClass(TechMapping, method="run",
name=str(ModuleName.TECH_MAPPING),
add_collect=False, split_keys=None,
config_preprocessor=_preprocessor)
main = as_click_command(tm_command)


if __name__ == '__main__':
try:
main(obj={})
except Exception:
logger.exception('Error running reV Tech Mapping CLI.')
raise
67 changes: 38 additions & 29 deletions reV/supply_curve/tech_mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class TechMapping:
"""Framework to create map between tech layer (exclusions), res, and gen"""

def __init__(
self, excl_fpath, res_fpath, sc_resolution=2560, dist_margin=1.05
self, excl_fpath, res_fpath, resolution=2560, dist_margin=1.05
):
"""
Parameters
Expand All @@ -41,7 +41,7 @@ def __init__(
arrays to allow for mapping to resource points
res_fpath : str
Filepath to .h5 resource file that we're mapping to.
sc_resolution : int | None, optional
resolution : int | None, optional
Supply curve resolution, does not affect the exclusion to resource
(tech) mapping, but defines how many exclusion pixels are mapped
at a time, by default 2560
Expand All @@ -58,9 +58,9 @@ def __init__(
)

with SupplyCurveExtent(
self._excl_fpath, resolution=sc_resolution
self._excl_fpath, resolution=resolution
) as sc:
self._sc_resolution = sc.resolution
self._resolution = sc.resolution
self._gids = np.array(list(range(len(sc))), dtype=np.uint32)
self._excl_shape = sc.exclusions.shape
self._n_excl = np.product(self._excl_shape)
Expand Down Expand Up @@ -296,43 +296,43 @@ def map_resource_gids(

return ind_out

def initialize_dataset(self, dset, chunks=(128, 128)):
def initialize_dataset(self, tm_dset, chunks=(128, 128)):
"""
Initialize output dataset in exclusions h5 file. If dataset already
exists, a warning will be issued.
Parameters
----------
dset : str
tm_dset : str
Name of the dataset in the exclusions H5 file to create.
chunks : tuple, optional
Chunk size for the dataset, by default (128, 128).
"""

with h5py.File(self._excl_fpath, "a") as f:
if dset in list(f):
if tm_dset in list(f):
wmsg = (
'TechMap results dataset "{}" already exists '
'in pre-existing Exclusions TechMapping file "{}"'.format(
dset, self._excl_fpath
tm_dset, self._excl_fpath
)
)
logger.warning(wmsg)
warn(wmsg, FileInputWarning)
else:
f.create_dataset(
dset,
tm_dset,
shape=self._excl_shape,
dtype=np.int32,
chunks=chunks,
)
f[dset][:] = -1
f[tm_dset][:] = -1

if self._dist_thresh:
f[dset].attrs["distance_threshold"] = self._dist_thresh
f[tm_dset].attrs["distance_threshold"] = self._dist_thresh

if self._res_fpath:
f[dset].attrs["src_res_fpath"] = self._res_fpath
f[tm_dset].attrs["src_res_fpath"] = self._res_fpath

def _check_fout(self):
"""Check the TechMapping output file for cached data."""
Expand All @@ -345,14 +345,14 @@ def _check_fout(self):
logger.exception(emsg)
raise FileInputError(emsg)

def map_resource(self, dset, max_workers=None, points_per_worker=10):
def map_resource(self, tm_dset, max_workers=None, points_per_worker=10):
"""
Map all resource gids to exclusion gids. Save results to dset in
exclusions h5 file.
Parameters
----------
dset : str, optional
tm_dset : str, optional
Name of the output dataset in the exclusions H5 file to which the
tech map will be saved.
max_workers : int, optional
Expand Down Expand Up @@ -390,7 +390,7 @@ def map_resource(self, dset, max_workers=None, points_per_worker=10):
] = i

with h5py.File(self._excl_fpath, "a") as f:
indices = f[dset]
indices = f[tm_dset]
n_finished = 0
for future in as_completed(futures):
n_finished += 1
Expand Down Expand Up @@ -422,8 +422,8 @@ def run(
cls,
excl_fpath,
res_fpath,
dset,
sc_resolution=2560,
tm_dset,
resolution=64,
dist_margin=1.05,
max_workers=None,
points_per_worker=10,
Expand All @@ -433,16 +433,25 @@ def run(
Parameters
----------
excl_fpath : str
Filepath to exclusions h5 (tech layer). dset will be
created in excl_fpath.
Filepath to exclusions data HDF5 file. This file must must contain
latitude and longitude datasets.
res_fpath : str
Filepath to .h5 resource file that we're mapping to.
dset : str, optional
Dataset name in excl_fpath to save mapping results to.
sc_resolution : int | None, optional
Supply curve resolution, does not affect the exclusion to resource
(tech) mapping, but defines how many exclusion pixels are mapped
at a time, by default 2560
Filepath to HDF5 resource file (e.g. WTK or NSRDB) to which
the exclusions will be mapped. Can refer to a single file (e.g.,
"/path/to/nsrdb_2024.h5" or a wild-card e.g.,
"/path/to/nsrdb_{}.h5")
tm_dset : str
Dataset name in the `excl_fpath` file to which the the
techmap (exclusions-to-resource mapping data) will be saved.
.. Important:: If this dataset already exists in the h5 file,
it will be overwritten.
resolution : int | None, optional
Supply Curve resolution. This value defines how many pixels
are in a single side of a supply curve cell. For example,
a value of ``64`` would generate a supply curve where the
side of each supply curve cell is ``64x64`` exclusion
pixels. By default, ``64``.
dist_margin : float, optional
Extra margin to multiply times the computed distance between
neighboring resource points, by default 1.05
Expand All @@ -453,11 +462,11 @@ def run(
Number of supply curve points to map to resource gids on each
worker, by default 10
"""
kwargs = {"dist_margin": dist_margin, "sc_resolution": sc_resolution}
kwargs = {"dist_margin": dist_margin, "resolution": resolution}
mapper = cls(excl_fpath, res_fpath, **kwargs)
mapper.initialize_dataset(dset)
mapper.initialize_dataset(tm_dset)
mapper.map_resource(
max_workers=max_workers,
points_per_worker=points_per_worker,
dset=dset
tm_dset=tm_dset
)
1 change: 1 addition & 0 deletions reV/utilities/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ class ModuleName(str, Enum):
REP_PROFILES = "rep-profiles"
SUPPLY_CURVE = "supply-curve"
SUPPLY_CURVE_AGGREGATION = "supply-curve-aggregation"
TECH_MAPPING = "tech-mapping"

def __str__(self):
return self.value
Expand Down
Loading

0 comments on commit 524f9b1

Please sign in to comment.