From aeea1f46145dc812298474a14c010c2bd152cbc3 Mon Sep 17 00:00:00 2001 From: Paul Pinchuk Date: Tue, 13 Aug 2024 17:36:58 -0600 Subject: [PATCH 01/16] Update docstrings --- reV/generation/cli_gen.py | 49 +++++++++++++++++++++++++++++++++--- reV/generation/generation.py | 8 +++--- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/reV/generation/cli_gen.py b/reV/generation/cli_gen.py index 69c62bc49..58c031fc8 100644 --- a/reV/generation/cli_gen.py +++ b/reV/generation/cli_gen.py @@ -16,7 +16,7 @@ logger = logging.getLogger(__name__) -def _preprocessor(config, job_name, log_directory, verbose, +def _preprocessor(config, job_name, log_directory, resource_file, verbose, analysis_years=None): """Preprocess generation config user input. @@ -30,6 +30,50 @@ def _preprocessor(config, job_name, log_directory, verbose, Path to log output directory. verbose : bool Flag to signal ``DEBUG`` verbosity (``verbose=True``). + resource_file : str | list + Filepath to resource data. This input can be path to a + single resource HDF5 file or a path including a wildcard input + like ``/h5_dir/prefix*suffix``. This path can contain brackets + ``{}``, which will be filled in by unique values from the + `analysis_years` input (which must not be null in this case). + Alternatively, this input can be a list of explicit files to + process. In this case, the length of the list must match the + length of the `analysis_years` input exactly, and the path are + assumed to align with the `analysis_years` (i.e. the first path + corresponds to the first analysis year, the second path + corresponds to the second analysis year, and so on). In all + cases, the resource data must be readable by + :py:class:`rex.resource.Resource` + or :py:class:`rex.multi_file_resource.MultiFileResource`. + (i.e. the resource data conform to the + `rex data format `_). This + means the data file(s) must contain a 1D ``time_index`` + dataset indicating the UTC time of observation, a 1D + ``meta`` dataset represented by a DataFrame with + site-specific columns, and 2D resource datasets that match + the dimensions of (``time_index``, ``meta``). The time index + must start at 00:00 of January 1st of the year under + consideration, and its shape must be a multiple of 8760. + + .. Important:: If you are using custom resource data (i.e. + not NSRDB/WTK/Sup3rCC, etc.), ensure the following: + + - The data conforms to the + `rex data format `_. + - The ``meta`` DataFrame is organized such that every + row is a pixel and at least the columns + ``latitude``, ``longitude``, ``timezone``, and + ``elevation`` are given for each location. + - The time index and associated temporal data is in + UTC. + - The latitude is between -90 and 90 and longitude is + between -180 and 180. + - For solar data, ensure the DNI/DHI are not zero. You + can calculate one of these these inputs from the + other using the relationship + + .. math:: GHI = DNI * cos(SZA) + DHI + analysis_years : int | list, optional A single year or list of years to perform analysis for. These years will be used to fill in any brackets ``{}`` in the @@ -46,8 +90,7 @@ def _preprocessor(config, job_name, log_directory, verbose, config.get("execution_control", {}).setdefault("max_workers") analysis_years = format_analysis_years(analysis_years) - config["resource_file"] = _parse_res_files(config["resource_file"], - analysis_years) + config["resource_file"] = _parse_res_files(resource_file, analysis_years) lr_res_file = config.get("low_res_resource_file") if lr_res_file is None: config["low_res_resource_file"] = [None] * len(analysis_years) diff --git a/reV/generation/generation.py b/reV/generation/generation.py index 2a57e3318..626b65d1f 100644 --- a/reV/generation/generation.py +++ b/reV/generation/generation.py @@ -236,10 +236,10 @@ def __init__( info on the allowed and/or required SAM config file inputs. resource_file : str Filepath to resource data. This input can be path to a - single resource HDF5 file, a path to a directory containing - data spread across multiple HDF5 files, or a path including - a wildcard input like ``/h5_dir/prefix*suffix``. In all - cases, the resource data must be readable by + single resource HDF5 file or a path including a wildcard + input like ``/h5_dir/prefix*suffix`` (i.e. if your datasets + for a single year are spread out over multiple files). In + all cases, the resource data must be readable by :py:class:`rex.resource.Resource` or :py:class:`rex.multi_file_resource.MultiFileResource`. (i.e. the resource data conform to the From afcc8a454cb9627b83ad7ede74f7079cdeae22f5 Mon Sep 17 00:00:00 2001 From: ppinchuk Date: Tue, 13 Aug 2024 18:56:28 -0600 Subject: [PATCH 02/16] Update docstrings --- reV/SAM/generation.py | 2 +- reV/generation/generation.py | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/reV/SAM/generation.py b/reV/SAM/generation.py index a0e8ea373..60d2c45e6 100644 --- a/reV/SAM/generation.py +++ b/reV/SAM/generation.py @@ -1548,7 +1548,7 @@ class Geothermal(AbstractSamGenerationFromWeatherFile): or 1 allowed. - ``design_temp`` : EGS plant design temperature (in C). Only affects EGS runs. This value may be adjusted internally by - ``reV under the following conditions: + ``reV`` under the following conditions: - The design temperature is larger than the resource temperature diff --git a/reV/generation/generation.py b/reV/generation/generation.py index 626b65d1f..916164250 100644 --- a/reV/generation/generation.py +++ b/reV/generation/generation.py @@ -253,8 +253,14 @@ def __init__( consideration, and its shape must be a multiple of 8760. .. Note:: If executing ``reV`` from the command line, this - path can contain brackets ``{}`` that will be filled in by - the `analysis_years` input. + input string can contain brackets ``{}`` that will be + filled in by the `analysis_years` input. Alternatively, + this input can be a list of explicit files to process. In + this case, the length of the list must match the length of + the `analysis_years` input exactly, and the path are + assumed to align with the `analysis_years` (i.e. the first + path corresponds to the first analysis year, the second + path corresponds to the second analysis year, and so on) .. Important:: If you are using custom resource data (i.e. not NSRDB/WTK/Sup3rCC, etc.), ensure the following: From 171793bc7b6efde460e2784bd3f4d9d88f6b63f3 Mon Sep 17 00:00:00 2001 From: ppinchuk Date: Tue, 13 Aug 2024 18:58:51 -0600 Subject: [PATCH 03/16] Undo cli changes --- reV/generation/cli_gen.py | 49 +++------------------------------------ 1 file changed, 3 insertions(+), 46 deletions(-) diff --git a/reV/generation/cli_gen.py b/reV/generation/cli_gen.py index 58c031fc8..69c62bc49 100644 --- a/reV/generation/cli_gen.py +++ b/reV/generation/cli_gen.py @@ -16,7 +16,7 @@ logger = logging.getLogger(__name__) -def _preprocessor(config, job_name, log_directory, resource_file, verbose, +def _preprocessor(config, job_name, log_directory, verbose, analysis_years=None): """Preprocess generation config user input. @@ -30,50 +30,6 @@ def _preprocessor(config, job_name, log_directory, resource_file, verbose, Path to log output directory. verbose : bool Flag to signal ``DEBUG`` verbosity (``verbose=True``). - resource_file : str | list - Filepath to resource data. This input can be path to a - single resource HDF5 file or a path including a wildcard input - like ``/h5_dir/prefix*suffix``. This path can contain brackets - ``{}``, which will be filled in by unique values from the - `analysis_years` input (which must not be null in this case). - Alternatively, this input can be a list of explicit files to - process. In this case, the length of the list must match the - length of the `analysis_years` input exactly, and the path are - assumed to align with the `analysis_years` (i.e. the first path - corresponds to the first analysis year, the second path - corresponds to the second analysis year, and so on). In all - cases, the resource data must be readable by - :py:class:`rex.resource.Resource` - or :py:class:`rex.multi_file_resource.MultiFileResource`. - (i.e. the resource data conform to the - `rex data format `_). This - means the data file(s) must contain a 1D ``time_index`` - dataset indicating the UTC time of observation, a 1D - ``meta`` dataset represented by a DataFrame with - site-specific columns, and 2D resource datasets that match - the dimensions of (``time_index``, ``meta``). The time index - must start at 00:00 of January 1st of the year under - consideration, and its shape must be a multiple of 8760. - - .. Important:: If you are using custom resource data (i.e. - not NSRDB/WTK/Sup3rCC, etc.), ensure the following: - - - The data conforms to the - `rex data format `_. - - The ``meta`` DataFrame is organized such that every - row is a pixel and at least the columns - ``latitude``, ``longitude``, ``timezone``, and - ``elevation`` are given for each location. - - The time index and associated temporal data is in - UTC. - - The latitude is between -90 and 90 and longitude is - between -180 and 180. - - For solar data, ensure the DNI/DHI are not zero. You - can calculate one of these these inputs from the - other using the relationship - - .. math:: GHI = DNI * cos(SZA) + DHI - analysis_years : int | list, optional A single year or list of years to perform analysis for. These years will be used to fill in any brackets ``{}`` in the @@ -90,7 +46,8 @@ def _preprocessor(config, job_name, log_directory, resource_file, verbose, config.get("execution_control", {}).setdefault("max_workers") analysis_years = format_analysis_years(analysis_years) - config["resource_file"] = _parse_res_files(resource_file, analysis_years) + config["resource_file"] = _parse_res_files(config["resource_file"], + analysis_years) lr_res_file = config.get("low_res_resource_file") if lr_res_file is None: config["low_res_resource_file"] = [None] * len(analysis_years) From f4a0ac8f61e88874a6a745e2a1d29e7a02dc4020 Mon Sep 17 00:00:00 2001 From: ppinchuk Date: Tue, 13 Aug 2024 19:06:10 -0600 Subject: [PATCH 04/16] Fix typo --- reV/generation/generation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reV/generation/generation.py b/reV/generation/generation.py index 916164250..d103e33a9 100644 --- a/reV/generation/generation.py +++ b/reV/generation/generation.py @@ -260,7 +260,7 @@ def __init__( the `analysis_years` input exactly, and the path are assumed to align with the `analysis_years` (i.e. the first path corresponds to the first analysis year, the second - path corresponds to the second analysis year, and so on) + path corresponds to the second analysis year, and so on). .. Important:: If you are using custom resource data (i.e. not NSRDB/WTK/Sup3rCC, etc.), ensure the following: From 8533171f08be72dfeddbc15b1178ba4ec6886ee5 Mon Sep 17 00:00:00 2001 From: ppinchuk Date: Tue, 13 Aug 2024 19:06:18 -0600 Subject: [PATCH 05/16] Fix PySAM links --- examples/project_points/README.rst | 2 +- .../project_points/reV_project_points.ipynb | 25 +++---------------- examples/running_locally/README.rst | 2 +- .../running_locally/running_reV_locally.ipynb | 2 +- .../running_reV_with_HSDS.ipynb | 2 +- 5 files changed, 7 insertions(+), 26 deletions(-) diff --git a/examples/project_points/README.rst b/examples/project_points/README.rst index da0739966..fc898863e 100644 --- a/examples/project_points/README.rst +++ b/examples/project_points/README.rst @@ -4,7 +4,7 @@ reV Project Points `reV Gen `_ and `reV Econ `_ use `Project Points `_ to define which resource sites (`gids`) to run through -`PySAM `_ and how. +`PySAM `_ and how. At its most basic Project Points consists of the resource ``gid``s and the ``SAM`` configuration file associated it. This can be definited in a variety diff --git a/examples/project_points/reV_project_points.ipynb b/examples/project_points/reV_project_points.ipynb index 293ee686d..d529c8e45 100644 --- a/examples/project_points/reV_project_points.ipynb +++ b/examples/project_points/reV_project_points.ipynb @@ -9,7 +9,7 @@ "[reV Gen](https://nrel.github.io/reV/_autosummary/reV.generation.generation.Gen.html#reV.generation.generation.Gen)\n", "and [reV Econ](https://nrel.github.io/reV/_autosummary/reV.econ.econ.Econ.html#reV.econ.econ.Econ)\n", "use [Project Points](https://nrel.github.io/reV/_autosummary/reV.config.project_points.ProjectPoints.html#reV.config.project_points.ProjectPoints) to define which resource sites (`gids`) to run through\n", - "[PySAM](https://pysam.readthedocs.io/en/latest/) and how.\n", + "[PySAM](https://nrel-pysam.readthedocs.io/en/latest/) and how.\n", "\n", "At its most basic Project Points consists of the resource `gid`s and the\n", "`SAM` configuration file associated it. This can be definited in a variety\n", @@ -31,9 +31,6 @@ "ExecuteTime": { "end_time": "2021-07-07T14:58:35.397005Z", "start_time": "2021-07-07T14:58:32.416695Z" - }, - "vscode": { - "languageId": "python" } }, "outputs": [ @@ -166,9 +163,6 @@ "ExecuteTime": { "end_time": "2021-07-07T14:58:50.285840Z", "start_time": "2021-07-07T14:58:50.264216Z" - }, - "vscode": { - "languageId": "python" } }, "outputs": [ @@ -298,9 +292,6 @@ "ExecuteTime": { "end_time": "2021-07-07T14:59:24.504132Z", "start_time": "2021-07-07T14:59:24.487708Z" - }, - "vscode": { - "languageId": "python" } }, "outputs": [ @@ -403,9 +394,6 @@ "ExecuteTime": { "end_time": "2021-07-07T15:00:00.189414Z", "start_time": "2021-07-07T15:00:00.142362Z" - }, - "vscode": { - "languageId": "python" } }, "outputs": [ @@ -549,9 +537,6 @@ "ExecuteTime": { "end_time": "2021-07-07T15:00:55.441563Z", "start_time": "2021-07-07T15:00:55.407988Z" - }, - "vscode": { - "languageId": "python" } }, "outputs": [ @@ -909,7 +894,7 @@ "from reV.config.project_points import ProjectPoints\n", "\n", "# Of form {region : region_column}\n", - "regions = {'Rhode Island': 'state'} \n", + "regions = {'Rhode Island': 'state'}\n", "# or\n", "regions = {'Providence': 'county', 'Kent': 'county'}\n", "\n", @@ -956,11 +941,7 @@ { "cell_type": "code", "execution_count": 36, - "metadata": { - "vscode": { - "languageId": "python" - } - }, + "metadata": {}, "outputs": [ { "name": "stdout", diff --git a/examples/running_locally/README.rst b/examples/running_locally/README.rst index 4aba865c2..43182456e 100644 --- a/examples/running_locally/README.rst +++ b/examples/running_locally/README.rst @@ -8,7 +8,7 @@ can be run locally using resource .h5 files stored locally. reV Gen ------- -reV Generation uses `PySAM `_ to +reV Generation uses `PySAM `_ to compute technologically specific capcity factor means and profiles. reV Gen uses ``SAM`` technology terms and input configuration files diff --git a/examples/running_locally/running_reV_locally.ipynb b/examples/running_locally/running_reV_locally.ipynb index 5cf6eb8d9..335822bde 100644 --- a/examples/running_locally/running_reV_locally.ipynb +++ b/examples/running_locally/running_reV_locally.ipynb @@ -8,7 +8,7 @@ "\n", "[reV Gen](https://nrel.github.io/reV/_autosummary/reV.generation.generation.Gen.html#reV.generation.generation.Gen) and [reV Econ](https://nrel.github.io/reV/_autosummary/reV.econ.econ.Econ.html#reV.econ.econ.Econ) can be run locally using resource .h5 files stored locally or using the [HDF Groups](https://www.hdfgroup.org) Highly Scalable Distributed Service (HSDS) to access resource .h5 file stored in the cloud (currenly on Amazon Web Services -- AWS).\n", "\n", - "reV use [PySAM](https://pysam.readthedocs.io/en/latest/) to\n", + "reV uses [PySAM](https://nrel-pysam.readthedocs.io/en/latest/) to\n", "compute technologically specific capcity factor means and profiles (`Gen`) and mean levelized cost of energy (`Econ`). `reV`\n", "uses ``SAM`` technology terms and input configuration files. \n", "\n", diff --git a/examples/running_with_hsds/running_reV_with_HSDS.ipynb b/examples/running_with_hsds/running_reV_with_HSDS.ipynb index 913e334b2..c9d3a90aa 100644 --- a/examples/running_with_hsds/running_reV_with_HSDS.ipynb +++ b/examples/running_with_hsds/running_reV_with_HSDS.ipynb @@ -9,7 +9,7 @@ "\n", "[reV Gen](https://nrel.github.io/reV/_autosummary/reV.generation.generation.Gen.html#reV.generation.generation.Gen) and [reV Econ](https://nrel.github.io/reV/_autosummary/reV.econ.econ.Econ.html#reV.econ.econ.Econ) can be run locally using the [HDF Groups](https://www.hdfgroup.org) Highly Scalable Distributed Service (HSDS) to access resource .h5 file stored in the cloud (currenly on Amazon Web Services -- AWS).\n", "\n", - "reV use [PySAM](https://pysam.readthedocs.io/en/latest/) to\n", + "reV uses [PySAM](https://nrel-pysam.readthedocs.io/en/latest/) to\n", "compute technologically specific capcity factor means and profiles (`Gen`) and mean levelized cost of energy (`Econ`). `reV`\n", "uses ``SAM`` technology terms and input configuration files.\n", "\n", From 9cdaa5b56b37118cf7389ff9afec04f504a3fcf9 Mon Sep 17 00:00:00 2001 From: ppinchuk Date: Tue, 13 Aug 2024 20:27:58 -0600 Subject: [PATCH 06/16] Add to git ignore --- .gitattributes | 2 ++ .gitignore | 5 +++++ 2 files changed, 7 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..07fe41c52 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# GitHub syntax highlighting +pixi.lock linguist-language=YAML linguist-generated=true diff --git a/.gitignore b/.gitignore index 1aee32aab..1bb234724 100644 --- a/.gitignore +++ b/.gitignore @@ -116,3 +116,8 @@ tags # Scratch and temp work .tmp/ .scratch/ + + +# pixi environments +.pixi +*.egg-info From edf4ce761c688ebedf9fb6440516ee88df286b6e Mon Sep 17 00:00:00 2001 From: ppinchuk Date: Wed, 14 Aug 2024 16:29:29 -0600 Subject: [PATCH 07/16] Fix spelling --- reV/supply_curve/tech_mapping.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reV/supply_curve/tech_mapping.py b/reV/supply_curve/tech_mapping.py index b35cb51bc..6f72b2225 100644 --- a/reV/supply_curve/tech_mapping.py +++ b/reV/supply_curve/tech_mapping.py @@ -185,7 +185,7 @@ def _get_excl_coords(cls, excl_fpath, gids, sc_row_indices, sc_col_indices, excl_row_slices, excl_col_slices, coord_labels=(LATITUDE, LONGITUDE)): """ - Extract the exclusion coordinates for teh desired gids for TechMapping. + Extract the exclusion coordinates for the desired gids for TechMapping. Parameters ---------- From 9ae0bc0d85c863a9509438a032518b805def8c7d Mon Sep 17 00:00:00 2001 From: ppinchuk Date: Wed, 14 Aug 2024 17:03:50 -0600 Subject: [PATCH 08/16] Bump rex req --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index eea8e393b..2b9a1e612 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ NREL-gaps>=0.6.11 NREL-NRWAL>=0.0.7 NREL-PySAM~=4.1.0 -NREL-rex>=0.2.85 +NREL-rex>=0.2.89 numpy~=1.24.4 packaging>=20.3 plotly>=4.7.1 From bfadc7f74bf85eb995866f972130d3adcfc68cb8 Mon Sep 17 00:00:00 2001 From: ppinchuk Date: Wed, 14 Aug 2024 17:04:26 -0600 Subject: [PATCH 09/16] Bespoke now uses `MultiYearWindResource` handler exclusively + tests --- reV/bespoke/bespoke.py | 68 +++++++++++++----------------------------- tests/test_bespoke.py | 9 +++--- 2 files changed, 26 insertions(+), 51 deletions(-) diff --git a/reV/bespoke/bespoke.py b/reV/bespoke/bespoke.py index 3632dfbe9..640f41641 100644 --- a/reV/bespoke/bespoke.py +++ b/reV/bespoke/bespoke.py @@ -20,7 +20,6 @@ import psutil from rex.joint_pd.joint_pd import JointPD from rex.multi_year_resource import MultiYearWindResource -from rex.renewable_resource import WindResource from rex.utilities.bc_parse_table import parse_bc_table from rex.utilities.execution import SpawnProcessPool from rex.utilities.loggers import create_dirs, log_mem @@ -60,8 +59,13 @@ def __init__(self, res_fpath, sc_gid_to_hh, sc_gid_to_res_gid): Parameters ---------- - res_fpath : str - Path to resource h5 file. + res_fpath : str | list + Unix shell style path (potentially containing wildcard (*) + patterns) to a single or multi-file resource file set(s). + Can also be an explicit list of resource file paths, which + themselves can contain wildcards. This input must be + readable by + :py:class:`rex.multi_year_resource.MultiYearWindResource`. sc_gid_to_hh : dict Dictionary mapping SC GID values to hub-heights. Data for each SC GID will be pulled for the corresponding hub-height @@ -69,7 +73,7 @@ def __init__(self, res_fpath, sc_gid_to_hh, sc_gid_to_res_gid): sc_gid_to_res_gid : dict Dictionary mapping SC GID values to an iterable oif resource GID values. Resource GID values should correspond to GID - values in teh HDF5 file, so any GID map must be applied + values in the HDF5 file, so any GID map must be applied before initializing :class`BespokeMultiPlantData`. """ self.res_fpath = res_fpath @@ -95,12 +99,7 @@ def _pre_load_data(self): } start_time = time.time() - if "*" in self.res_fpath: - handler = MultiYearWindResource - else: - handler = WindResource - - with handler(self.res_fpath) as res: + with MultiYearWindResource(self.res_fpath) as res: self._wind_dirs = { hh: res[f"winddirection_{hh}m", :, gids] for hh, gids in self.hh_to_res_gids.items() @@ -481,8 +480,7 @@ def __init__( self._pre_loaded_data = pre_loaded_data self._outputs = {} - Handler = self.get_wind_handler(res) - res = res if not isinstance(res, str) else Handler(res) + res = res if not isinstance(res, str) else MultiYearWindResource(res) self._sc_point = AggSCPoint( gid, @@ -1142,29 +1140,6 @@ def get_lcoe_kwargs(self): lcoe_kwargs["capital_cost"] = lcoe_kwargs["capital_cost"] + bos return lcoe_kwargs - @staticmethod - def get_wind_handler(res): - """Get a wind resource handler for a resource filepath. - - Parameters - ---------- - res : str - Resource filepath to wtk .h5 file. Can include * wildcards - for multi year resource. - - Returns - ------- - handler : WindResource | MultiYearWindResource - Wind resource handler or multi year handler - """ - handler = res - if isinstance(res, str): - if "*" in res: - handler = MultiYearWindResource - else: - handler = WindResource - return handler - @classmethod def check_dependencies(cls): """Check special dependencies for bespoke""" @@ -1521,14 +1496,15 @@ def __init__(self, excl_fpath, res_fpath, tm_dset, objective_function, uniquely defined (i.e.only appear once and in a single input file). res_fpath : str - Filepath to wind resource data in NREL WTK format. This - input can be path to a single resource HDF5 file or a path - including a wildcard input like ``/h5_dir/prefix*suffix`` to - run bespoke on multiple years of resource data. The former - must be readable by - :py:class:`rex.renewable_resource.WindResource` while the - latter must be readable by - or :py:class:`rex.multi_year_resource.MultiYearWindResource` + Unix shell style path to wind resource HDF5 file in NREL WTK + format. Can also be a path including a wildcard input like + ``/h5_dir/prefix*suffix`` to run bespoke on multiple years + of resource data. Can also be an explicit list of resource + HDF5 file paths, which themselves can contain wildcards. If + multiple files are specified in this way, they must have the + same coordinates but can have different time indices (i.e. + different years). This input must be readable by + :py:class:`rex.multi_year_resource.MultiYearWindResource` (i.e. the resource data conform to the `rex data format `_). This means the data file(s) must contain a 1D ``time_index`` @@ -2119,8 +2095,7 @@ def _check_files(self): ) # just check that this file exists, cannot check res_fpath if *glob - Handler = BespokeSinglePlant.get_wind_handler(self._res_fpath) - with Handler(self._res_fpath) as f: + with MultiYearWindResource(self._res_fpath) as f: assert any(f.dsets) def _pre_load_data(self, pre_load_data): @@ -2480,14 +2455,13 @@ def run_serial(cls, excl_fpath, res_fpath, tm_dset, exclusion_shape = sc.exclusions.shape cls._check_inclusion_mask(inclusion_mask, gids, exclusion_shape) - Handler = BespokeSinglePlant.get_wind_handler(res_fpath) # pre-extract handlers so they are not repeatedly initialized file_kwargs = { "excl_dict": excl_dict, "area_filter_kernel": area_filter_kernel, "min_area": min_area, - "h5_handler": Handler, + "h5_handler": MultiYearWindResource, } with AggFileHandler(excl_fpath, res_fpath, **file_kwargs) as fh: diff --git a/tests/test_bespoke.py b/tests/test_bespoke.py index 9a9bb3da5..6986a2cbc 100644 --- a/tests/test_bespoke.py +++ b/tests/test_bespoke.py @@ -1498,12 +1498,13 @@ def test_cli(runner, clear_loggers): fn_out = "{}_{}.h5".format(dirname, ModuleName.BESPOKE) out_fpath = os.path.join(td, fn_out) - res_fp = os.path.join(td, "ri_100_wtk_{}.h5") + res_fp_1 = os.path.join(td, "ri_100_wtk_{}.h5") + res_fp_2 = os.path.join(td, "another_name_{}.h5") excl_fp = os.path.join(td, "ri_exclusions.h5") shutil.copy(EXCL, excl_fp) - shutil.copy(RES.format(2012), res_fp.format(2012)) - shutil.copy(RES.format(2013), res_fp.format(2013)) - res_fp = res_fp.format("*") + shutil.copy(RES.format(2012), res_fp_1.format(2012)) + shutil.copy(RES.format(2013), res_fp_2.format(2013)) + res_fp = [res_fp_1.format(2012), res_fp_2.format("*")] TechMapping.run(excl_fp, RES.format(2012), dset=TM_DSET, max_workers=1) From 1fbf34715afd1b9d475c3d88d85681ea0be4f9ce Mon Sep 17 00:00:00 2001 From: ppinchuk Date: Wed, 14 Aug 2024 17:04:47 -0600 Subject: [PATCH 10/16] Bump reV version --- reV/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reV/version.py b/reV/version.py index 50fc0c0d9..45fa73838 100644 --- a/reV/version.py +++ b/reV/version.py @@ -2,4 +2,4 @@ reV Version number """ -__version__ = "0.9.3" +__version__ = "0.9.4" From eda63ec8e482d402ac62772877e45479bdf9df2c Mon Sep 17 00:00:00 2001 From: ppinchuk Date: Sun, 18 Aug 2024 13:50:17 -0600 Subject: [PATCH 11/16] Fix typo in docstring --- reV/supply_curve/sc_aggregation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reV/supply_curve/sc_aggregation.py b/reV/supply_curve/sc_aggregation.py index 1b40b63f5..2a594ad09 100644 --- a/reV/supply_curve/sc_aggregation.py +++ b/reV/supply_curve/sc_aggregation.py @@ -1410,7 +1410,7 @@ def run( ``multi-year``, ``collect``, or ``econ``. However, note that duplicate executions of any of these commands within the pipeline may invalidate this parsing, meaning the - `econ_fpath` input will have to be specified manually. + `gen_fpath` input will have to be specified manually. By default, ``None``. args : tuple | list, optional From 81befcec3c89685badf674bd91f604bb6bd46471 Mon Sep 17 00:00:00 2001 From: ppinchuk Date: Tue, 27 Aug 2024 18:46:33 -0600 Subject: [PATCH 12/16] Remove unused import --- reV/supply_curve/sc_aggregation.py | 1 - 1 file changed, 1 deletion(-) diff --git a/reV/supply_curve/sc_aggregation.py b/reV/supply_curve/sc_aggregation.py index 2a594ad09..40c3bf7c3 100644 --- a/reV/supply_curve/sc_aggregation.py +++ b/reV/supply_curve/sc_aggregation.py @@ -18,7 +18,6 @@ from rex.resource import Resource from rex.utilities.execution import SpawnProcessPool -from reV.generation.base import BaseGen from reV.handlers.exclusions import ExclusionLayers from reV.supply_curve.aggregation import ( AbstractAggFileHandler, From d9905b1a5e8deb63bf6cf6d50c4f5f50b2fed71e Mon Sep 17 00:00:00 2001 From: ppinchuk Date: Wed, 11 Sep 2024 19:07:04 -0600 Subject: [PATCH 13/16] Add wind direction to resource kwargs if layout found in any SAM config --- reV/SAM/SAM.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/reV/SAM/SAM.py b/reV/SAM/SAM.py index aa33d4863..32a447ca2 100644 --- a/reV/SAM/SAM.py +++ b/reV/SAM/SAM.py @@ -195,6 +195,11 @@ def _make_res_kwargs( # make precip rate available for curtailment analysis kwargs["precip_rate"] = True + sam_configs = project_points.sam_inputs.values() + needs_wd = any(_sam_config_contains_turbine_layout(sam_config) + for sam_config in sam_configs) + kwargs["require_wind_dir"] = needs_wd + elif res_handler == GeothermalResource: args += (project_points.d,) @@ -954,3 +959,8 @@ def _add_sys_capacity(sam_inputs): cap = sam_inputs.get("nameplate") sam_inputs["system_capacity"] = cap + + +def _sam_config_contains_turbine_layout(sam_config): + """Detect wether SAM config contains multiple turbines in layout. """ + return len(sam_config.get("wind_farm_xCoordinates", ())) > 1 From 2f8095f134f794a934314b981f1c4dcbc46f11c6 Mon Sep 17 00:00:00 2001 From: ppinchuk Date: Wed, 11 Sep 2024 19:07:13 -0600 Subject: [PATCH 14/16] Add tests for wind direction in resource --- tests/test_config.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/test_config.py b/tests/test_config.py index eceebf739..b3775628e 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -48,6 +48,28 @@ def test_clearsky(): RevPySam.get_sam_res(res_file, pp, pp.tech) +def test_no_wind_direction(): + """Test getting resource without wind direction (no layout specified)""" + + res_file = os.path.join(TESTDATADIR, 'wtk/ri_100_wtk_2012.h5') + sam_file = os.path.join(TESTDATADIR, 'SAM/wind_gen_standard_losses_0.json') + pp = ProjectPoints(slice(0, 1), sam_file, "windpower", res_file=res_file) + out = RevPySam.get_sam_res(res_file, pp, pp.tech) + assert "winddirection" not in out.var_list + assert (out._get_res_df(0)[0]["winddirection"] == 0).all() + + +def test_wind_direction(): + """Test getting resource with wind direction (layout was specified)""" + res_file = os.path.join(TESTDATADIR, 'wtk/ri_100_wtk_2012.h5') + sam_file = os.path.join(TESTDATADIR, 'SAM/i_windpower_lcoe.json') + pp = ProjectPoints(slice(0, 1), sam_file, "windpower", res_file=res_file) + + out = RevPySam.get_sam_res(res_file, pp, pp.tech) + assert "winddirection" in out.var_list + assert (out._get_res_df(0)[0]["winddirection"] != 0).any() + + @pytest.mark.parametrize( ("start", "interval"), [[0, 1], [13, 1], [10, 2], [13, 3]] ) From 2fb6f2a04987ff6e03bf6dadf733a422c39a85a0 Mon Sep 17 00:00:00 2001 From: ppinchuk Date: Wed, 11 Sep 2024 19:07:46 -0600 Subject: [PATCH 15/16] Bump version for new functionality of vanilla reV with wind farm layouts --- reV/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reV/version.py b/reV/version.py index 50fc0c0d9..45fa73838 100644 --- a/reV/version.py +++ b/reV/version.py @@ -2,4 +2,4 @@ reV Version number """ -__version__ = "0.9.3" +__version__ = "0.9.4" From 534bb7194b13c1c4627b34ede7741642ea9dc1f2 Mon Sep 17 00:00:00 2001 From: ppinchuk Date: Wed, 11 Sep 2024 22:31:43 -0600 Subject: [PATCH 16/16] Fix baseline values for new wakes --- tests/test_econ_windbos.py | 78 ++++++++------------------------------ 1 file changed, 16 insertions(+), 62 deletions(-) diff --git a/tests/test_econ_windbos.py b/tests/test_econ_windbos.py index 6f758213a..f3f5e5d95 100644 --- a/tests/test_econ_windbos.py +++ b/tests/test_econ_windbos.py @@ -67,62 +67,18 @@ # baseline single owner + windbos results using 2012 RI WTK data. BASELINE = { - "project_return_aftertax_npv": np.array( - [ - 7876459.5, - 7875551.5, - 7874505.0, - 7875270.0, - 7875349.5, - 7872819.5, - 7871078.5, - 7871352.5, - 7871153.5, - 7869134.5, - ] - ), - "lcoe_real": np.array( - [ - 71.007614, - 69.21741, - 67.24552, - 68.67675, - 68.829605, - 64.257965, - 61.39551, - 61.83265, - 61.51415, - 58.43599, - ] - ), - "lcoe_nom": np.array( - [ - 89.433525, - 87.17878, - 84.6952, - 86.497826, - 86.69034, - 80.932396, - 77.327156, - 77.87773, - 77.476585, - 73.59966, - ] - ), - "flip_actual_irr": np.array( - [ - 10.999977, - 10.999978, - 10.999978, - 10.999978, - 10.999978, - 10.999978, - 10.999979, - 10.999979, - 10.999979, - 10.99998, - ] - ), + "project_return_aftertax_npv": np.array([7876966.0, 7876048.0, 7875001.5, + 7875783.5, 7875860.5, 7873335.5, + 7871586.5, 7871840.5, 7871639.0, + 7869647.0]), + "lcoe_real": np.array([72.039345, 70.18582, 68.16827, 69.666306, 69.81716, + 65.14765, 62.207253, 62.621048, 62.29249, + 59.192535]), + "lcoe_nom": np.array([90.73299, 88.39848, 85.85738, 87.744156, 87.93416, + 82.05295, 78.34954, 78.87071, 78.456894, 74.55253]), + "flip_actual_irr": np.array([10.999977, 10.999978, 10.999978, 10.999978, + 10.999978, 10.999978, 10.999979, 10.999979, + 10.999979, 10.999979]), "total_installed_cost": np.array(10 * [88892234.91311586]), "turbine_cost": np.array(10 * [52512000.0]), "sales_tax_cost": np.array(10 * [0.0]), @@ -132,13 +88,11 @@ # baseline single owner + windbos results when sweeping sales tax basis BASELINE_SITE_BOS = { - "total_installed_cost": np.array( - [88892230.0, 88936680.0, 88981130.0, 89025576.0, 89070020.0] - ), + "total_installed_cost": np.array([88892230.0, 88936680.0, 88981130.0, + 89025576.0, 89070020.0]), "turbine_cost": np.array(5 * [52512000.0]), - "sales_tax_cost": np.array( - [0.0, 44446.117, 88892.234, 133338.36, 177784.47] - ), + "sales_tax_cost": np.array([0.0, 44446.117, 88892.234, 133338.36, + 177784.47]), "bos_cost": np.array(5 * [36380234.91311585]), }