Skip to content

Commit ec32add

Browse files
authored
Merge branch 'develop' into v3/generic_plant
2 parents af4971d + 219437b commit ec32add

File tree

20 files changed

+9509
-111
lines changed

20 files changed

+9509
-111
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ solar and storage.
4545
4. Install HOPP and its dependencies:
4646

4747
```bash
48-
conda install -y -c conda-forge coin-or-cbc=2.10.8 glpk
48+
conda install -y -c conda-forge coin-or-cbc glpk
4949
```
5050

5151
Note if you are on Windows, you will have to manually install Cbc: https://github.com/coin-or/Cbc.

RELEASE.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
# Release Notes
22

3-
## Unreleased
3+
4+
## Unreleased, TBD
45

56
* Added GenericPlant model for simulating grid and battery performance without resimulating generation of other systems.
67
* Loosened strictness of comparison for wind turbine config checking and added tests
8+
* Loosened strictness of comparison for wind turbine hub-height and wind resource hub-height
9+
* Updated workflow for specifying wind turbine parameters without specifying a turbine name with PySAM.
10+
* Added ability to download wind resource data from WTK-LED for Alaska
711

812

913
## Version 3.2.0, March 21, 2025

docs/_toc.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ parts:
1919
sections:
2020
- file: api/resource/solar_api
2121
- file: api/resource/wind_api
22+
- file: api/resource/alaska_wind
2223
- file: api/resource/solar_hpc
2324
- file: api/resource/wind_hpc
2425
- file: api/resource/wave_data

docs/api/resource/alaska_wind.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
(resource:ak-wind-resource)=
2+
# Wind Resource for Alaska (API)
3+
4+
Wind resource data can downloaded for Alaska from the NREL Developer Network hosted Wind Integration National Dataset (WIND) Toolkit dataset [Wind Toolkit Data - Alaska V1.0.0](https://developer.nrel.gov/docs/wind/wind-toolkit/wtk-alaska-v1-0-0-download/). Using this functionality requires an NREL API key.
5+
6+
Wind resource data for Alaska can only be downloaded for wind resource years 2018-2020 and is only downloaded if the `wind_resource_region` input to [SiteInfo](../site_info.md) is set to "ak". For example:
7+
8+
```yaml
9+
site:
10+
wind_resource_region: "ak"
11+
```
12+
13+
```{eval-rst}
14+
.. autoclass:: hopp.simulation.technologies.resource.alaska_wind.AlaskaWindData
15+
:members:
16+
:exclude-members: _abc_impl, check_download_dir
17+
```

docs/api/resource/index.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
These are the primary methods for accessing wind and solar resource data.
44

55
- [Solar Resource (API)](resource:solar-resource)
6-
- [Wind Resource (API)](resource:wind-resource)
6+
- [Conus Wind Resource (API)](resource:wind-resource)
7+
- [Alaska Wind Resource (API)](resource:ak-wind-resource)
78
- [Solar Resource (NSRDB Dataset on NREL HPC)](resource:nsrdb-data)
89
- [Wind Resource (Wind Toolkit Dataset on NREL HPC)](resource:wtk-data)
910
- [Wave Resource (Data)](resource:wave-resource)

docs/api/resource/wind_api.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
(resource:wind-resource)=
2-
# Wind Resource (API)
2+
# Wind Resource for Continental U.S. (API)
33

4-
By default, wind resource data is downloaded from the NREL Developer Network hosted Wind Integration National Dataset (WIND) Toolkit dataset [Wind Toolkit Data - SAM format (srw)](https://developer.nrel.gov/docs/wind/wind-toolkit/wtk-srw-download/). Using this functionality requires an NREL API key.
4+
By default, wind resource data is downloaded from the NREL Developer Network hosted Wind Integration National Dataset (WIND) Toolkit dataset [Wind Toolkit Data - SAM format (srw)](https://developer.nrel.gov/docs/wind/wind-toolkit/wtk-srw-download/).
5+
6+
Wind resource data for the continental U.S. can only be downloaded for wind resource years 2007 - 2014. Using this functionality requires an NREL API key.
57

68
```{eval-rst}
79
.. autoclass:: hopp.simulation.technologies.resource.wind_resource.WindResource

hopp/simulation/resource_files/wind/66.68_-162.5_WTK_Alaksa_2019_60min_80m_100m.csv

Lines changed: 8762 additions & 0 deletions
Large diffs are not rendered by default.

hopp/simulation/technologies/resource/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@
88
from hopp.simulation.technologies.resource.cambium_data import CambiumData
99
from hopp.simulation.technologies.resource.nsrdb_data import HPCSolarData
1010
from hopp.simulation.technologies.resource.wind_toolkit_data import HPCWindData
11+
from hopp.simulation.technologies.resource.alaska_wind import AlaskaWindData
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
import csv, os
2+
from pathlib import Path
3+
from typing import Union, Optional, List
4+
import pandas as pd
5+
import urllib.parse
6+
7+
from attrs import define, field
8+
9+
from hopp.utilities.keys import get_developer_nrel_gov_key, get_developer_nrel_gov_email
10+
from hopp.utilities.validators import range_val
11+
from hopp.simulation.technologies.resource.resource import Resource
12+
from hopp import ROOT_DIR
13+
from hopp.tools.resource.pysam_wind_tools import combine_wind_files
14+
15+
AK_BASE_URL = "https://developer.nrel.gov/api/wind-toolkit/v2/wind/wtk-alaska-v1-0-0-download.csv?"
16+
17+
@define
18+
class AlaskaWindData(Resource):
19+
#: latitude corresponding to location for wind resource data
20+
lat: float = field()
21+
#: longitude corresponding to location for wind resource data
22+
lon: float = field()
23+
#: year for resource data. must be between 2018 and 2020
24+
year: int = field(validator=range_val(2018, 2020))
25+
26+
#: the hub-height for wind resource data (meters)
27+
hub_height_meters: float = field(validator=range_val(10.0, 1000.0))
28+
29+
#: filepath to resource_files directory. Defaults to ROOT_DIR/"simulation"/"resource_files".
30+
path_resource: Optional[Union[str, Path]] = field(default = ROOT_DIR / "simulation" / "resource_files")
31+
#: file path of resource file to load or download
32+
filename: Optional[Union[str, Path]] = field(default = None)
33+
#: Make an API call even if there's an existing file. Defaults to False.
34+
use_api: Optional[bool] = field(default = False)
35+
#: dictionary of preloaded and formatted wind resource data. Defaults to None.
36+
resource_data: Optional[dict] = field(default = None)
37+
38+
#: dictionary of heights and filenames to download from Wind Toolkit
39+
file_resource_heights: dict = field(default = None)
40+
41+
#: list of heights that wind resource data is available for downloading (meters)
42+
allowed_hub_height_meters: list[int] = [10, 20, 40, 60, 80, 100, 120, 140, 160, 180, 200, 250, 300, 500, 1000]
43+
44+
45+
def __attrs_post_init__(self):
46+
super().__init__(self.lat, self.lon, self.year)
47+
48+
# if resource_data is input as a dictionary then set_data
49+
if isinstance(self.resource_data,dict):
50+
self.data = self.resource_data
51+
return
52+
53+
# if resource_data is not provided, download or load resource data
54+
if isinstance(self.path_resource,str):
55+
self.path_resource = Path(self.path_resource).resolve()
56+
if self.path_resource.parts[-1]!="wind":
57+
self.path_resource = self.path_resource / 'wind'
58+
59+
if self.filename is None:
60+
self.calculate_heights_to_download()
61+
62+
self.check_download_dir()
63+
64+
if not os.path.isfile(self.filename) or self.use_api:
65+
self.download_resource()
66+
67+
self.format_data()
68+
69+
def calculate_heights_to_download(self):
70+
"""
71+
Given the system hub height, and the available hubheights from WindToolkit,
72+
determine which heights to download to bracket the hub height
73+
"""
74+
hub_height_meters = self.hub_height_meters
75+
76+
# evaluate hub height, determine what heights to download
77+
heights = [hub_height_meters]
78+
if hub_height_meters not in self.allowed_hub_height_meters:
79+
height_low = self.allowed_hub_height_meters[0]
80+
height_high = self.allowed_hub_height_meters[-1]
81+
for h in self.allowed_hub_height_meters:
82+
if h < hub_height_meters:
83+
height_low = h
84+
elif h > hub_height_meters:
85+
height_high = h
86+
break
87+
heights[0] = height_low
88+
heights.append(height_high)
89+
90+
filename_base = f"{self.latitude}_{self.longitude}_WTK_Alaksa_{self.year}_{self.interval}min"
91+
file_resource_full = filename_base
92+
file_resource_heights = dict()
93+
94+
for h in heights:
95+
h_int = int(h)
96+
file_resource_heights[h_int] = self.path_resource/(filename_base + f'_{h_int}m.csv')
97+
file_resource_full += f'_{h_int}m'
98+
file_resource_full += ".csv"
99+
100+
self.file_resource_heights = file_resource_heights
101+
self.filename = self.path_resource / file_resource_full
102+
103+
def update_height(self, hub_height_meters):
104+
"""Update hub-height and corresponding attributes.
105+
Also updates ``file_resource_heights`` and ``filename``.
106+
107+
Args:
108+
hub_height_meters (float): hub-height for wind resource data (meters)
109+
"""
110+
self.hub_height_meters = hub_height_meters
111+
self.calculate_heights_to_download()
112+
113+
def download_resource(self):
114+
success = False
115+
116+
base_attributs = ["temperature","windspeed","winddirection"]
117+
attributes = ["pressure_100m"]
118+
for height, f in self.file_resource_heights.items():
119+
attributes += [f"{a}_{height}m" for a in base_attributs]
120+
121+
attributes_str = ",".join(k for k in attributes)
122+
input_data = {
123+
'attributes': attributes_str,
124+
'interval': self.interval,
125+
'api_key': get_developer_nrel_gov_key(),
126+
'email': get_developer_nrel_gov_email(),
127+
'names': [str(self.year)],
128+
'wkt': f"POINT({self.longitude} {self.latitude})"
129+
}
130+
url = AK_BASE_URL + urllib.parse.urlencode(input_data, True)
131+
success = self.call_api(url, filename=self.filename)
132+
133+
if not success:
134+
raise ValueError('Unable to download wind data')
135+
136+
return success
137+
138+
def format_data(self):
139+
"""
140+
Format as 'wind_resource_data' dictionary for use in PySAM.
141+
"""
142+
if not os.path.isfile(self.filename):
143+
raise FileNotFoundError(f"{self.filename} does not exist. Try `download_resource` first.")
144+
145+
self.data = self.filename
146+
147+
@Resource.data.setter
148+
def data(self, data_info):
149+
"""
150+
Sets the wind resource data to a dictionary in SAM Wind format.
151+
"""
152+
if isinstance(data_info,dict):
153+
self._data = data_info
154+
if isinstance(data_info,(str, Path)):
155+
resource_heights = [k for k in self.file_resource_heights.keys()]
156+
self._data = combine_wind_files(str(data_info),resource_heights)

hopp/simulation/technologies/resource/resource.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,14 @@ def __init__(self, lat, lon, year, **kwargs):
3838
self.affiliation = 'NREL'
3939
self.reason = 'hybrid-analysis'
4040
self.mailing_list = 'true'
41-
4241
# paths
4342
self.path_current = os.path.dirname(os.path.abspath(__file__))
4443
self.path_resource = os.path.join(ROOT_DIR, 'simulation', 'resource_files')
45-
44+
self.filename = None #: filepath of resource data file, defaults to None
45+
4646
# update any passed in
4747
self.__dict__.update(kwargs)
4848

49-
self.filename = None #: filepath of resource data file, defaults to None
5049
self._data = dict()
5150

5251
def check_download_dir(self):
@@ -77,7 +76,8 @@ def call_api(url, filename):
7776
r = requests.get(url)
7877
if r:
7978
localfile = open(filename, mode='w+')
80-
localfile.write(r.text)
79+
txt = r.text.replace("(°C)","(C)").replace("(°)","(deg)")
80+
localfile.write(txt)
8181
localfile.close()
8282
if os.path.isfile(filename):
8383
success = True

0 commit comments

Comments
 (0)