This code sample uses statistical analyses and visualizations to explore the diurnal and monthly variability of wind resources at Mount Washington in New Hampshire using data from the National Renewable Energy Laboratory (NREL) Wind Integration National Dataset (WIND) Toolkit. Datasets within this tool include meteorological conditions such as temperature, pressure, relative humidity, wind direction, and wind speed. Hourly data is available for the continental United States from 2007 to 2013. This analysis used the dataset of wind speed at 100 meters for the year 2012.
Note
Click the drop down to the right of the title to view all code written for this analysis
Access data
The code below was used to access the data with the h5pyd Python package and NREL Highly Scalable Data Service (HSDS). The data are stored in the file wtk_us.h5 which is in HDF5 format. Data are stored in three dimensions dataset[t, y, x] where x is the longitudinal index, y is the latitudinal index, and t is the temporal index. Timestamps are in the UTC time zone.
import packages
import h5pydimport numpy as npimport pandas as pdimport matplotlib.pyplot as pltimport matplotlib.image as mpimgfrom pyproj import Projimport dateutilimport foliumimport plotly.graph_objects as go
access wind toolkit file
f = h5pyd.File("/nrel/wtk-us.h5", 'r', bucket="nrel-pds-hsds")# access the windspeed_100m datasetwindspeed_100m_dset = f['windspeed_100m']
The file structure organizes the data into 2 kilometer x 2 kilometer grids. The code below takes the latitude/longitude coordinates of an individual site (in this case Mount Washington) and finds the indices and coordinates of the nearest site within the dataset. Latitude and longitude coordinates are in a modified Lambert Conic projection.
# This function finds the nearest x/y indices for a given lat/lon.# Rather than fetching the entire coordinates database, which is 500+ MB, this# uses the Proj4 library to find a nearby point and then converts to x/y indicesdef indicesForCoord(f, lat_index, lon_index): dset_coords = f['coordinates'] projstring ="""+proj=lcc +lat_1=30 +lat_2=60 +lat_0=38.47240422490422 +lon_0=-96.0 +x_0=0 +y_0=0 +ellps=sphere +units=m +no_defs """ projectLcc = Proj(projstring) origin_ll =reversed(dset_coords[0][0]) # Grab origin directly from database origin = projectLcc(*origin_ll) coords = (lon_index,lat_index) coords = projectLcc(*coords) delta = np.subtract(coords, origin) ij = [int(round(x/2000)) for x in delta]returntuple(reversed(ij))nearest_site = indicesForCoord(f, site_coords[0], site_coords[1] )print("y,x indices for", site_name, ": \t\t{}".format(nearest_site))
create map showing location of site and nearest point
site_map
Make this Notebook Trusted to load map: File -> Trust Notebook
Create dataframes
Next, the time series for the specified time range is used to get corresponding wind speed data for the point nearest Mount Washington and the data is converted to a pandas dataframe with columns parsed for month, day, and hour.
get the entire 2012 timeseries data for a point near the site
The pandas dataframe was then aggregated to group the data by month and hour and calculate values for mean and standard deviation. Functions to calculate the first, second, and third quartiles were also used. These dataframes were used for the statistical analysis and visualizations.
<string>:1: FutureWarning: The default value of numeric_only in DataFrameGroupBy.mean is deprecated. In a future version, numeric_only will default to False. Either specify numeric_only or select only columns which should be valid for the function.
create dataframe of average wind speed for each hour grouped by month
<string>:1: FutureWarning: The default value of numeric_only in DataFrameGroupBy.std is deprecated. In a future version, numeric_only will default to False. Either specify numeric_only or select only columns which should be valid for the function.
create dataframe of wind speed standard deviation for each hour by month
Before beginning the statistical analysis, here is a graph of the full time series to get a general idea of the shape and magnitude of the data for this location. Wind speed seems to fluctuate a lot. Toggle the legend lines on/off to see 24 hour, 10 day, and 30 day rolling averages. From the 30 day average, wind speed seems to be higher in the winter than the summer. Let’s explore the monthly and daily wind speed data further to quantify the variability.
code to graph rolling averages
fig = go.Figure([ go.Scatter(x = windspeed_100m_df.index, y = windspeed_100m_df['windspeed_100m'], mode ='lines', legendrank =1, name ='hourly', line=dict(color='blue', width=0.75)), go.Scatter(x = moving_averages_24hr.index, y = moving_averages_24hr['windspeed_100m'], mode ='lines', legendrank =1, name ='24 hour avg', line=dict(color='green', width=1), visible='legendonly'), go.Scatter(x = moving_averages_10day.index, y = moving_averages_10day['windspeed_100m'], mode ='lines', legendrank =1, name ='10 day avg', line=dict(color='red', width=1), visible='legendonly'), go.Scatter(x = moving_averages_30day.index, y = moving_averages_30day['windspeed_100m'], mode ='lines', legendrank =1, name ='30 day avg', line=dict(color='yellow', width=3), visible='legendonly')])fig.update_layout( margin=dict(l=20, r=20, t=30, b=20), paper_bgcolor="#FFFFFF", plot_bgcolor='#f5f5f5', yaxis=dict( title_text="windspeed (m/s)", titlefont=dict(size=16)), title={'text': "Hourly Wind Speed",'y':0.99,'x':0.5,'xanchor': 'center','yanchor': 'top'})
Diurnal variability is important when selecting potential sites for utility scale wind power because it is important to have adequate wind speeds when electricity demand peaks. To begin looking at the diurnal variability for Mount Washington, the graph on the left shows average hourly wind speed for the year 2012. This graph also presents the interquartile range (IQR) and standard deviation of this data. The IQR illustrates the spread of the middle half of wind speed values. The standard deviation quantifies the spread of the data around the mean. The lower the standard deviation, the more the data is clustered around the mean. The greater the standard deviation, the more spread out the data.
Based on the hourly wind speed visualization, wind speed at Mount Washington seems to be lowest in the middle of the day, but based on the standard deviation bars and interquartile range, there is a large spread to the data. Seasonal diurnal variability will be explored further down the page.
The graph on the right shows a similar visual for average monthly wind speed. Here we see that wind speeds tend to be lower in the summer than the winter. Low mid-day wind speeds during the summer result in decreased power generation from wind sources. This could be problematic when electricity demands from air conditioning units are high.
Hourly average
code for graph of hourly averages with IQR and standard deviation
fig = go.Figure([ go.Scatter(name ='mean', y = hourly_avg['mean'], x = hourly_avg['hour'], mode ='lines', line =dict(color ="blue", width =4), error_y =dict(type='data', array = hourly_avg['std'], visible =True)), go.Scatter( name ='IQR 75', y = hourly_avg['quantile75'], x = hourly_avg['hour'], mode='lines', marker=dict(color="#444"), line=dict(width=0),#legendgroup = 'IQR', showlegend =False ),# Create IQR 25 fill color go.Scatter( name='IQR', y = hourly_avg['quantile25'], x = hourly_avg['hour'], marker=dict(color="#444"), line=dict(width=0), mode='lines', fillcolor='rgba(68, 68, 68, 0.3)', fill='tonexty', # fill to next y legendgroup ='IQR', showlegend =True )])fig.update_layout( xaxis=dict( title_text="hour (UTC)", titlefont=dict(size=16), dtick =2), yaxis=dict( title_text="windspeed (m/s)", titlefont=dict(size=16)), title={'text': "Average Hourly Wind Speed for the Year 2012",'y':0.99,'x':0.5,'xanchor': 'center','yanchor': 'top'}, margin=dict(l=20, r=20, t=30, b=20), paper_bgcolor="#FFFFFF", plot_bgcolor='#f5f5f5')
Monthly average
hourly averages with IQR and standard deviation
fig = go.Figure([ go.Scatter(name ='mean', y = monthly_avg['mean'], x = monthly_avg['month'], mode ='lines', line =dict(color ="blue", width =4), error_y =dict(type='data', array = monthly_avg['std'], visible =True)), go.Scatter( name ='IQR 75', y = monthly_avg['quantile75'], x = monthly_avg['month'], mode='lines', marker=dict(color="#444"), line=dict(width=0), showlegend =False ),# Create IQR 25 fill color go.Scatter( name='IQR', y = monthly_avg['quantile25'], x = monthly_avg['month'], marker=dict(color="#444"), line=dict(width=0), mode='lines', fillcolor='rgba(68, 68, 68, 0.3)', fill='tonexty', # fill to next y legendgroup ='IQR', showlegend =True)])fig.update_layout( xaxis=dict( title_text="month", titlefont=dict(size=16), dtick =1), yaxis=dict( title_text="windspeed (m/s)", titlefont=dict(size=16)), title={'text': "Average Monthly Wind Speed for the Year 2012",'y':0.99,'x':0.5,'xanchor': 'center','yanchor': 'top'}, margin=dict(l=20, r=20, t=30, b=20), paper_bgcolor="#FFFFFF", plot_bgcolor='#f5f5f5')
The next graph shows hourly average wind speed with lines for each month. The thick black line represents the annual mean for each hour.
This graph illustrates both the magnitude of wind speed values related to other months as well and the seasonal variability of daily wind speed patterns. For the year 2012, the months of January, February, March, April, October and December had higher than average wind speeds while the months of May, June, July, August, September, and November had lower than average wind speed. For most months, wind speeds are lowest during the middle of the day.
code to graph hourly averages for each month
fig = go.Figure([ go.Scatter(y = hourly_avg_by_month['1'], x = hourly_avg_by_month.index, mode ='lines', legendrank =1, name ='January', line=dict(color='#DC050C', width=2)), go.Scatter(y = hourly_avg_by_month['2'], x = hourly_avg_by_month.index, mode ='lines+markers', legendrank =2, name ='February', line=dict(color='#E8601c', width=2)), go.Scatter(y = hourly_avg_by_month['3'], x = hourly_avg_by_month.index, mode ='lines', legendrank =3, name ='March', line=dict(color='#f4a736', width=2)), go.Scatter(y = hourly_avg_by_month['4'], x = hourly_avg_by_month.index, mode ='lines+markers', legendrank =4, name ='April', line=dict(color='#f7f056', width=2)), go.Scatter(y = hourly_avg_by_month['5'], x = hourly_avg_by_month.index, mode ='lines', legendrank =5, name ='May', line=dict(color='#cae0ab', width=2)), go.Scatter(y = hourly_avg_by_month['6'], x = hourly_avg_by_month.index, mode ='lines+markers', legendrank =6, name ='June', line=dict(color='#4eb265', width=2)), go.Scatter(y = hourly_avg_by_month['7'], x = hourly_avg_by_month.index, mode ='lines', legendrank =7, name ='July', line=dict(color='#7bafde', width=2)), go.Scatter(y = hourly_avg_by_month['8'], x = hourly_avg_by_month.index, mode ='lines+markers', legendrank =8, name ='August', line=dict(color='#5289c7', width=2)), go.Scatter(y = hourly_avg_by_month['9'], x = hourly_avg_by_month.index, mode ='lines', legendrank =9, name ='September', line=dict(color='#1965b0', width=2)), go.Scatter(y = hourly_avg_by_month['10'], x = hourly_avg_by_month.index, mode ='lines+markers', legendrank =10, name ='October', line=dict(color='#882e72', width=2)), go.Scatter(y = hourly_avg_by_month['11'], x = hourly_avg_by_month.index, mode ='lines', legendrank =11, name ='November', line=dict(color='#ae76a3', width=2)), go.Scatter(y = hourly_avg_by_month['12'], x = hourly_avg_by_month.index, mode ='lines+markers', legendrank =12, name ='December', line=dict(color='#d1bbd7', width=2)), go.Scatter(name ='annual mean', y = hourly_avg['mean'], x = hourly_avg['hour'], mode ='lines', line =dict(color ="black", width =4))])fig.update_layout( xaxis=dict( title_text="hour (UTC)", titlefont=dict(size=16), dtick =4), yaxis=dict( title_text="windspeed (m/s)", titlefont=dict(size=16)), title={'text': "Average Hourly Wind Speed by Month",'y':0.99,'x':0.5,'xanchor': 'center','yanchor': 'top'}, margin=dict(l=20, r=20, t=30, b=20), paper_bgcolor="#FFFFFF", plot_bgcolor='#f5f5f5')# fig.write_image("avg_hourly_windspeed_by_month.png")
This heatmap summarizes both the daily and monthly variability of wind speed at Mount Washington in 2012. Here we can clearly see that for this location, wind speeds are lowest in the summer and mid day and highest in the winter and early/late hours.
code for heatmap of hourly and monthly wind speed averages
This next heatmap summarizes the size of the standard deviation varies by hour and month. Wind speed values tend to have a greater spread in the winter than the summer. The hourly pattern for standard deviation is less clear.
code for heatmap of hourly and monthly wind speed standard deviation
This analysis concludes that wind speeds at 100 meters near Mount Washington in New Hampshire varied based on month and time of day. As summarized in the table below, smallest monthly variability occurred in July and the greatest monthly variability occurred in November. August had the smallest average wind speed and February had the greatest average wind speed. Hourly variability was smallest for hour 19 and largest for hour 13. Average hourly wind speed was smallest for hour 16 and greatest for hour 1.
Overall, the diurnal wind speed pattern resulted in higher speeds between hours 1-4; from there, wind speed tended to decrease and reach a minimum between hours 13-18; wind speeds generally increased between hours 19-23. Monthly wind speeds tended to be highest during the winter and lowest during the summer.
Based on the seasonal variability, this site would be better at meeting high winter demands than summer demands. This site may not be ideal for meeting all daytime demands.
In 2012, wind speed at Mount Washington ranged from 0.12 m/s to 36.66 m/s. Hourly average wind speeds ranged from 10.71 m/s to 12.50 m/s. Monthly average wind speeds ranged from 8.78 m/s to 15.81 m/s.
When planning utility scale wind power plants, it is important to evaluate how the diurnal and seasonal wind speed patterns compare to electricity demand patterns. Is wind generated electricity available when people need it? If high wind speeds at a proposed site do not coincide with peak electricity demands, than that site may not be suitable for utility scale wind farms. Renewable energy planners should also consider the frequency with which wind speeds are within the acceptable operating range for a given turbine. For example, how often are wind speeds to slow for turbines to operate, or how often are wind speeds too high?
Expanded geographic scale
A report was created as a tool for expanding the geographic scale of this analysis. This report was generated using the parameterized reporting capabilities of Quarto, an open-source scientific and technical publishing system.
The report can be modified by specifying parameters for:
site name (site_name)
site latitude (site_lat)
site longitude (site_lon)
start date (start_date)
end date (end_date)
turbine cut-in speed (cut_in_speed)
turbine cut-out speed (cut_out_speed)
required annual average wind speed (req_annual_avg_speed)
Default parameters are:
start_date: ‘2012-01-01’
end_date: ‘2013-01-01’
cut_in_speed: 3.6 m/s
cut_out_speed: 24.6 m/s
req_annual_avg_speed: 5.8 m/s
For the specified site, the report answers the following questions:
is the annual average wind speed at least 13 mph (5.8 m/s)? 1
how often the wind is below the cut-in speed of 8 mph (3.6 m/s)? 2
how often the wind exceed the cut-out speed of 55 mph (24.6 m/s)?
A new report can be generated using the command line interface. The code below shows an example that renders a report for New York City in 2010 using default values for cut-in speed, cut-out speed, and required annual average wind speed.
quarto render report.qmd -P site_name:“New York City” -P site_lat:40.7128 -P site_lon:-74.0059 -P start_date:2010-01-01 -P end_date:2011-01-01 --output new_york_city_report.pdf
The function below can be used to generate multiple reports from a dataframe of parameters for different sites.
The next code chunks show an example of how to work with this function from a dataframe of input. Running the function generates four reports, one for each site.
---title: "Wind Resource Temporal Variability"description: "Diurnal and monthly variability of NREL Wind Toolkit Data"author: - name: Marie Rivers url: https://marierivers.github.io/categories: [Python, renewable energy]citation: url: https://marierivers.github.io/code_samples/wind-resource-temporal-variability/ image: avg_hourly_windspeed_by_month.pngdraft: falseeditor: visualtoc-title: Contentstoc-location: lefttoc-depth: 3code-tools: source: true toggle: true caption: view codecode-block-bg: true# code-block-border-left: "#31BAE9"---This code sample uses statistical analyses and visualizations to explore the diurnal and monthly variability of wind resources at Mount Washington in New Hampshire using data from the [National Renewable Energy Laboratory (NREL) Wind Integration National Dataset (WIND) Toolkit](https://www.nrel.gov/grid/wind-toolkit.html). Datasets within this tool include meteorological conditions such as temperature, pressure, relative humidity, wind direction, and wind speed. Hourly data is available for the continental United States from 2007 to 2013. This analysis used the dataset of wind speed at 100 meters for the year 2012.::: callout-note## NoteClick the drop down to the right of the title to view all code written for this analysis:::<center><iframesrc="https://marierivers.github.io/presentations/wind_resource_presentation#/wind-resource-temporal-variability"title="Presentation"width="750px,"height="550px"></iframe></center><divclass="container"><divclass="center"><buttontype="button"class="button-styling"onclick=window.location.href='https://marierivers.github.io/presentations/wind_resource_presentation#/wind-resource-temporal-variability'>`r fontawesome::fa("tv", fill = "#64605f", a11y = "sem")` slides</button><buttontype="button"class="button-styling"onclick=window.location.href='https://github.com/marierivers/marierivers.github.io/tree/main/code_samples/wind-resource-temporal-variability'>`r fontawesome::fa("github-square", fill = "#64605f", a11y = "sem")` source code</button></div></div># Access dataThe code below was used to access the data with the [h5pyd](https://github.com/HDFGroup/h5pyd) Python package and NREL [Highly Scalable Data Service (HSDS)](https://github.com/NREL/hsds-examples). The data are stored in the file `wtk_us.h5` which is in HDF5 format. Data are stored in three dimensions `dataset[t, y, x]` where x is the longitudinal index, y is the latitudinal index, and t is the temporal index. Timestamps are in the UTC time zone.```{r}#| include: falselibrary(tidyverse)library(here)``````{python}#| column: page-right#| code-fold: true#| code-summary: "import packages"import h5pydimport numpy as npimport pandas as pdimport matplotlib.pyplot as pltimport matplotlib.image as mpimgfrom pyproj import Projimport dateutilimport foliumimport plotly.graph_objects as go``````{python}#| column: page-right#| code-fold: true#| code-summary: "access wind toolkit file"f = h5pyd.File("/nrel/wtk-us.h5", 'r', bucket="nrel-pds-hsds")# access the windspeed_100m datasetwindspeed_100m_dset = f['windspeed_100m']``````{python}#| column: page-right#| code-fold: true#| code-summary: "access datetime dataset"datetime_df = f["datetime"]datetime_df = pd.DataFrame({"datetime": datetime_df[:]},index=range(0,datetime_df.shape[0]))datetime_df['datetime'] = datetime_df['datetime'].apply(dateutil.parser.parse)``````{python}#| column: page-right#| code-fold: true#| code-summary: "specify datetime range"start_date ='2012-01-01'end_date ='2013-01-01'datetime_range = datetime_df.loc[(datetime_df.datetime >= start_date) & (datetime_df.datetime < end_date)].index```## Determine nearest timeseries for given Lat/LonThe file structure organizes the data into 2 kilometer x 2 kilometer grids. The code below takes the latitude/longitude coordinates of an individual site (in this case Mount Washington) and finds the indices and coordinates of the nearest site within the dataset. Latitude and longitude coordinates are in a modified Lambert Conic projection.```{python}#| column: page-right#| code-fold: true#| code-summary: "site specific info"site_name ="Mount Washington"site_coords = (44.27, -71.3)``````{python}#| column: page-right#| code-fold: true#| code-summary: "function to find nearest point"# This function finds the nearest x/y indices for a given lat/lon.# Rather than fetching the entire coordinates database, which is 500+ MB, this# uses the Proj4 library to find a nearby point and then converts to x/y indicesdef indicesForCoord(f, lat_index, lon_index): dset_coords = f['coordinates'] projstring ="""+proj=lcc +lat_1=30 +lat_2=60 +lat_0=38.47240422490422 +lon_0=-96.0 +x_0=0 +y_0=0 +ellps=sphere +units=m +no_defs """ projectLcc = Proj(projstring) origin_ll =reversed(dset_coords[0][0]) # Grab origin directly from database origin = projectLcc(*origin_ll) coords = (lon_index,lat_index) coords = projectLcc(*coords) delta = np.subtract(coords, origin) ij = [int(round(x/2000)) for x in delta]returntuple(reversed(ij))nearest_site = indicesForCoord(f, site_coords[0], site_coords[1] )print("y,x indices for", site_name, ": \t\t{}".format(nearest_site))print("Coordinates of", site_name, ": \t{}".format(site_coords))print("Coordinates of nearest point: \t{}".format(f["coordinates"][nearest_site[0]][nearest_site[1]]))```## MapThis map shows the location of Mount Washington and the nearest point from the WIND Toolkit.```{python}#| message: false#| code-fold: true#| code-summary: "create map showing location of site and nearest point"nearest_site_coords = f["coordinates"][nearest_site[0]][nearest_site[1]]site_map = folium.Map(location = site_coords, zoom_start =10)folium.Marker(site_coords, popup = site_name).add_to(site_map)folium.Marker(nearest_site_coords, popup ='Nearest Site').add_to(site_map)site_map```# Create dataframesNext, the time series for the specified time range is used to get corresponding wind speed data for the point nearest Mount Washington and the data is converted to a pandas dataframe with columns parsed for `month`, `day`, and `hour`.```{python}#| column: page-right#| code-fold: true#| code-summary: "get the entire 2012 timeseries data for a point near the site"tseries = windspeed_100m_dset[min(datetime_range):max(datetime_range)+1, nearest_site[0], nearest_site[1]]``````{python}#| column: page-right#| code-fold: true#| code-summary: "convert NumPy array to pandas dataframe"windspeed_100m_df = pd.DataFrame(tseries, columns = ["windspeed_100m"], index = datetime_df.iloc[datetime_range,].datetime)windspeed_100m_df["year"] = windspeed_100m_df.index.yearwindspeed_100m_df["month"] = windspeed_100m_df.index.monthwindspeed_100m_df["day"] = windspeed_100m_df.index.daywindspeed_100m_df["hour"] = windspeed_100m_df.index.hourwindspeed_100m_df["day_of_year"] = windspeed_100m_df.index.dayofyearwindspeed_100m_df["month_name"] = windspeed_100m_df.index.month_name()``````{python}#| include: false#windspeed_100m_df.to_csv("data/windspeed_100m_df.csv", index=True)```## Aggregate dataThe pandas dataframe was then aggregated to group the data by month and hour and calculate values for mean and standard deviation. Functions to calculate the first, second, and third quartiles were also used. These dataframes were used for the statistical analysis and visualizations.```{python}#| column: page-right#| code-fold: true#| code-summary: "functions to calculate quartiles"def quantile25(column):return column.quantile(0.25)def quantile50(column):return column.quantile(0.50)def quantile75(column):return column.quantile(0.75)```::: column-page-inset-right```{python}#| column: page-inset-right#| code-fold: true#| code-summary: "create dataframe of average wind speed for each hour"hourly_avg = windspeed_100m_df.groupby("hour")["windspeed_100m"].agg(["mean", "std", quantile25, quantile50, quantile75])hourly_avg = hourly_avg.reset_index()hourly_avg.head()``````{python}#| column: page-inset-right#| code-fold: true#| code-summary: "create dataframe of average wind speed for each month"monthly_avg = windspeed_100m_df.groupby(["month", "month_name"])["windspeed_100m"].agg(["mean", "std", quantile25, quantile50, quantile75]).reset_index()monthly_avg.head()``````{python}#| column: page-inset-right#| code-fold: true#| code-summary: "create dataframe of average wind speed for each hour grouped by month"hourly_avg_by_month = windspeed_100m_df.groupby(["hour", "month"]).mean()hourly_avg_by_month = hourly_avg_by_month.reset_index().pivot(index ="hour", columns =str("month"), values ="windspeed_100m")hourly_avg_by_month.columns = hourly_avg_by_month.columns.astype(str)hourly_avg_by_month.head()``````{python}#| column: page-inset-right#| code-fold: true#| code-summary: "create dataframe of wind speed standard deviation for each hour by month"hourly_std_by_month = windspeed_100m_df.groupby(["hour", "month"]).std()hourly_std_by_month = hourly_std_by_month.reset_index().pivot(index ="hour", columns =str("month"), values ="windspeed_100m")hourly_std_by_month.columns = hourly_std_by_month.columns.astype(str)hourly_std_by_month.head()```:::```{python}#| column: page-right#| code-fold: true#| code-summary: "calculate moving averages"#| warning: false#| output: false#| error: false#| message: false# 24 hour moving averagewindow_size_24hr =24windows_24hr = windspeed_100m_df.rolling(window_size_24hr)moving_averages_24hr = windows_24hr.mean()# 10 day moving averagewindow_size_10day =240windows_10day = windspeed_100m_df.rolling(window_size_10day)moving_averages_10day = windows_10day.mean()# 30 day moving averagewindow_size_30day =720windows_30day = windspeed_100m_df.rolling(window_size_30day)moving_averages_30day = windows_30day.mean()```# Statistical analysisBefore beginning the statistical analysis, here is a graph of the full time series to get a general idea of the shape and magnitude of the data for this location. Wind speed seems to fluctuate a lot. Toggle the legend lines on/off to see 24 hour, 10 day, and 30 day rolling averages. From the 30 day average, wind speed seems to be higher in the winter than the summer. Let's explore the monthly and daily wind speed data further to quantify the variability.```{python}#| column: page-right#| code-fold: true#| code-summary: "code to graph rolling averages"fig = go.Figure([ go.Scatter(x = windspeed_100m_df.index, y = windspeed_100m_df['windspeed_100m'], mode ='lines', legendrank =1, name ='hourly', line=dict(color='blue', width=0.75)), go.Scatter(x = moving_averages_24hr.index, y = moving_averages_24hr['windspeed_100m'], mode ='lines', legendrank =1, name ='24 hour avg', line=dict(color='green', width=1), visible='legendonly'), go.Scatter(x = moving_averages_10day.index, y = moving_averages_10day['windspeed_100m'], mode ='lines', legendrank =1, name ='10 day avg', line=dict(color='red', width=1), visible='legendonly'), go.Scatter(x = moving_averages_30day.index, y = moving_averages_30day['windspeed_100m'], mode ='lines', legendrank =1, name ='30 day avg', line=dict(color='yellow', width=3), visible='legendonly')])fig.update_layout( margin=dict(l=20, r=20, t=30, b=20), paper_bgcolor="#FFFFFF", plot_bgcolor='#f5f5f5', yaxis=dict( title_text="windspeed (m/s)", titlefont=dict(size=16)), title={'text': "Hourly Wind Speed",'y':0.99,'x':0.5,'xanchor': 'center','yanchor': 'top'})```<br> Diurnal variability is important when selecting potential sites for utility scale wind power because it is important to have adequate wind speeds when electricity demand peaks. To begin looking at the diurnal variability for Mount Washington, the graph on the left shows average hourly wind speed for the year 2012. This graph also presents the interquartile range (IQR) and standard deviation of this data. The IQR illustrates the spread of the middle half of wind speed values. The standard deviation quantifies the spread of the data around the mean. The lower the standard deviation, the more the data is clustered around the mean. The greater the standard deviation, the more spread out the data.Based on the hourly wind speed visualization, wind speed at Mount Washington seems to be lowest in the middle of the day, but based on the standard deviation bars and interquartile range, there is a large spread to the data. Seasonal diurnal variability will be explored further down the page.The graph on the right shows a similar visual for average monthly wind speed. Here we see that wind speeds tend to be lower in the summer than the winter. Low mid-day wind speeds during the summer result in decreased power generation from wind sources. This could be problematic when electricity demands from air conditioning units are high.::: column-screen-right::: columns::: {.column width="50%"}**Hourly average**```{python}#| code-fold: true#| code-summary: "code for graph of hourly averages with IQR and standard deviation"fig = go.Figure([ go.Scatter(name ='mean', y = hourly_avg['mean'], x = hourly_avg['hour'], mode ='lines', line =dict(color ="blue", width =4), error_y =dict(type='data', array = hourly_avg['std'], visible =True)), go.Scatter( name ='IQR 75', y = hourly_avg['quantile75'], x = hourly_avg['hour'], mode='lines', marker=dict(color="#444"), line=dict(width=0),#legendgroup = 'IQR', showlegend =False ),# Create IQR 25 fill color go.Scatter( name='IQR', y = hourly_avg['quantile25'], x = hourly_avg['hour'], marker=dict(color="#444"), line=dict(width=0), mode='lines', fillcolor='rgba(68, 68, 68, 0.3)', fill='tonexty', # fill to next y legendgroup ='IQR', showlegend =True )])fig.update_layout( xaxis=dict( title_text="hour (UTC)", titlefont=dict(size=16), dtick =2), yaxis=dict( title_text="windspeed (m/s)", titlefont=dict(size=16)), title={'text': "Average Hourly Wind Speed for the Year 2012",'y':0.99,'x':0.5,'xanchor': 'center','yanchor': 'top'}, margin=dict(l=20, r=20, t=30, b=20), paper_bgcolor="#FFFFFF", plot_bgcolor='#f5f5f5')```:::::: {.column width="50%"}**Monthly average**```{python}#| code-fold: true#| code-summary: "hourly averages with IQR and standard deviation"fig = go.Figure([ go.Scatter(name ='mean', y = monthly_avg['mean'], x = monthly_avg['month'], mode ='lines', line =dict(color ="blue", width =4), error_y =dict(type='data', array = monthly_avg['std'], visible =True)), go.Scatter( name ='IQR 75', y = monthly_avg['quantile75'], x = monthly_avg['month'], mode='lines', marker=dict(color="#444"), line=dict(width=0), showlegend =False ),# Create IQR 25 fill color go.Scatter( name='IQR', y = monthly_avg['quantile25'], x = monthly_avg['month'], marker=dict(color="#444"), line=dict(width=0), mode='lines', fillcolor='rgba(68, 68, 68, 0.3)', fill='tonexty', # fill to next y legendgroup ='IQR', showlegend =True)])fig.update_layout( xaxis=dict( title_text="month", titlefont=dict(size=16), dtick =1), yaxis=dict( title_text="windspeed (m/s)", titlefont=dict(size=16)), title={'text': "Average Monthly Wind Speed for the Year 2012",'y':0.99,'x':0.5,'xanchor': 'center','yanchor': 'top'}, margin=dict(l=20, r=20, t=30, b=20), paper_bgcolor="#FFFFFF", plot_bgcolor='#f5f5f5')```:::::::::<br> The next graph shows hourly average wind speed with lines for each month. The thick black line represents the annual mean for each hour.This graph illustrates both the magnitude of wind speed values related to other months as well and the seasonal variability of daily wind speed patterns. For the year 2012, the months of January, February, March, April, October and December had higher than average wind speeds while the months of May, June, July, August, September, and November had lower than average wind speed. For most months, wind speeds are lowest during the middle of the day.```{python}#| column: page-right#| code-fold: true#| code-summary: "code to graph hourly averages for each month"fig = go.Figure([ go.Scatter(y = hourly_avg_by_month['1'], x = hourly_avg_by_month.index, mode ='lines', legendrank =1, name ='January', line=dict(color='#DC050C', width=2)), go.Scatter(y = hourly_avg_by_month['2'], x = hourly_avg_by_month.index, mode ='lines+markers', legendrank =2, name ='February', line=dict(color='#E8601c', width=2)), go.Scatter(y = hourly_avg_by_month['3'], x = hourly_avg_by_month.index, mode ='lines', legendrank =3, name ='March', line=dict(color='#f4a736', width=2)), go.Scatter(y = hourly_avg_by_month['4'], x = hourly_avg_by_month.index, mode ='lines+markers', legendrank =4, name ='April', line=dict(color='#f7f056', width=2)), go.Scatter(y = hourly_avg_by_month['5'], x = hourly_avg_by_month.index, mode ='lines', legendrank =5, name ='May', line=dict(color='#cae0ab', width=2)), go.Scatter(y = hourly_avg_by_month['6'], x = hourly_avg_by_month.index, mode ='lines+markers', legendrank =6, name ='June', line=dict(color='#4eb265', width=2)), go.Scatter(y = hourly_avg_by_month['7'], x = hourly_avg_by_month.index, mode ='lines', legendrank =7, name ='July', line=dict(color='#7bafde', width=2)), go.Scatter(y = hourly_avg_by_month['8'], x = hourly_avg_by_month.index, mode ='lines+markers', legendrank =8, name ='August', line=dict(color='#5289c7', width=2)), go.Scatter(y = hourly_avg_by_month['9'], x = hourly_avg_by_month.index, mode ='lines', legendrank =9, name ='September', line=dict(color='#1965b0', width=2)), go.Scatter(y = hourly_avg_by_month['10'], x = hourly_avg_by_month.index, mode ='lines+markers', legendrank =10, name ='October', line=dict(color='#882e72', width=2)), go.Scatter(y = hourly_avg_by_month['11'], x = hourly_avg_by_month.index, mode ='lines', legendrank =11, name ='November', line=dict(color='#ae76a3', width=2)), go.Scatter(y = hourly_avg_by_month['12'], x = hourly_avg_by_month.index, mode ='lines+markers', legendrank =12, name ='December', line=dict(color='#d1bbd7', width=2)), go.Scatter(name ='annual mean', y = hourly_avg['mean'], x = hourly_avg['hour'], mode ='lines', line =dict(color ="black", width =4))])fig.update_layout( xaxis=dict( title_text="hour (UTC)", titlefont=dict(size=16), dtick =4), yaxis=dict( title_text="windspeed (m/s)", titlefont=dict(size=16)), title={'text': "Average Hourly Wind Speed by Month",'y':0.99,'x':0.5,'xanchor': 'center','yanchor': 'top'}, margin=dict(l=20, r=20, t=30, b=20), paper_bgcolor="#FFFFFF", plot_bgcolor='#f5f5f5')# fig.write_image("avg_hourly_windspeed_by_month.png")```<br> This heatmap summarizes both the daily and monthly variability of wind speed at Mount Washington in 2012. Here we can clearly see that for this location, wind speeds are lowest in the summer and mid day and highest in the winter and early/late hours.```{python}#| column: page-right#| code-fold: true#| code-summary: "code for heatmap of hourly and monthly wind speed averages"heatmap_month = hourly_avg_by_month.columns.tolist()heatmap_hour = hourly_avg_by_month.index.tolist()heatmap_windspeed = hourly_avg_by_month.values.tolist()trace = go.Heatmap( x = heatmap_month, y = heatmap_hour, z = heatmap_windspeed,type='heatmap',#colorscale = [(0,"blue"), (1,"red")], colorscale ='mint', colorbar=dict(title='Wind Speed (m/s)'))data = [trace]fig = go.Figure(data = data)fig.update_layout(#width=1000, height=650, xaxis=dict( title_text="month", titlefont=dict(size=16),#dtick = 1, tickmode ='array',# Set tick intervals to correspond with months tickvals = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], ticktext = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], tickfont =dict(size=16)), yaxis=dict( title_text="hour (UTC)", titlefont=dict(size=16), dtick =1, tickfont =dict(size=16)), title={'text': "Average Wind Speed by Month and Hour",'y':0.99,'x':0.5,'xanchor': 'center','yanchor': 'top'}, margin=dict(l=20, r=20, t=30, b=20),)```This next heatmap summarizes the size of the standard deviation varies by hour and month. Wind speed values tend to have a greater spread in the winter than the summer. The hourly pattern for standard deviation is less clear.```{python}#| column: page-right#| code-fold: true#| code-summary: "code for heatmap of hourly and monthly wind speed standard deviation"std_heatmap_month = hourly_std_by_month.columns.tolist()std_heatmap_hour = hourly_std_by_month.index.tolist()std_heatmap_windspeed = hourly_std_by_month.values.tolist()trace = go.Heatmap( x = std_heatmap_month, y = std_heatmap_hour, z = std_heatmap_windspeed,type='heatmap', colorscale ='Blues', colorbar=dict(title='Standard Deviation (m/s)'))data = [trace]fig = go.Figure(data = data)fig.update_layout(#width=1000, height=650, xaxis=dict( title_text="month", titlefont=dict(size=16),#dtick = 1, tickmode ='array',# Set tick intervals to correspond with months tickvals = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], ticktext = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], tickfont =dict(size=16)), yaxis=dict( title_text="hour (UTC)", titlefont=dict(size=16), dtick =1, tickfont =dict(size=16)), title={'text': "Wind Speed Standard Deviation by Month and Hour",'y':0.99,'x':0.5,'xanchor': 'center','yanchor': 'top'}, margin=dict(l=20, r=20, t=30, b=20),)```# Results and ConclusionsThis analysis concludes that wind speeds at 100 meters near Mount Washington in New Hampshire varied based on month and time of day. As summarized in the table below, smallest monthly variability occurred in July and the greatest monthly variability occurred in November. August had the smallest average wind speed and February had the greatest average wind speed. Hourly variability was smallest for hour 19 and largest for hour 13. Average hourly wind speed was smallest for hour 16 and greatest for hour 1.Overall, the diurnal wind speed pattern resulted in higher speeds between hours 1-4; from there, wind speed tended to decrease and reach a minimum between hours 13-18; wind speeds generally increased between hours 19-23. Monthly wind speeds tended to be highest during the winter and lowest during the summer.Based on the seasonal variability, this site would be better at meeting high winter demands than summer demands. This site may not be ideal for meeting all daytime demands.::: panel-tabset## Monthly```{python}#| code-fold: truemin_std_month = monthly_avg.loc[monthly_avg['std'].idxmin()]['month_name']max_std_month = monthly_avg.loc[monthly_avg['std'].idxmax()]['month_name']min_mean_wind_month = monthly_avg.loc[monthly_avg['mean'].idxmin()]['month_name']max_mean_wind_month = monthly_avg.loc[monthly_avg['mean'].idxmax()]['month_name']```## Hourly```{python}#| code-fold: truemin_std_hour = hourly_avg.loc[hourly_avg['std'].idxmin()]['hour']max_std_hour = hourly_avg.loc[hourly_avg['std'].idxmax()]['hour']min_mean_wind_hour = hourly_avg.loc[hourly_avg['mean'].idxmin()]['hour']max_mean_wind_hour = hourly_avg.loc[hourly_avg['mean'].idxmax()]['hour']```## Month & Hour```{python}#| code-fold: truemin_hr_month_std = hourly_std_by_month.values.min()max_hr_month_std = hourly_std_by_month.values.max()min_hr_month_wind = hourly_avg_by_month.values.min()max_hr_month_wind = hourly_avg_by_month.values.max()```:::From this analysis we can conclude that...| | smallest variability | greatest variability | smallest average wind speed | greatest average wind speed ||---------------|:-------------:|:-------------:|:-------------:|:-------------:|| monthly | July | November | August | February || hourly | 19 | 13 | 16 | 1 || month & hour | March, hour 16 | October, hour 13 | August, hour 15 | February, hour 1 || | | | | |: Summary of stats for monthly and hourly wind speed```{python}#| code-fold: true#| code-summary: "code to calculate min and max wind speeds"df_min = windspeed_100m_df['windspeed_100m'].min()df_max = windspeed_100m_df['windspeed_100m'].max()hourly_avg_min = hourly_avg['mean'].min()hourly_avg_max = hourly_avg['mean'].max()monthly_avg_min = monthly_avg['mean'].min()monthly_avg_max = monthly_avg['mean'].max()```In 2012, wind speed at Mount Washington ranged from 0.12 m/s to 36.66 m/s. Hourly average wind speeds ranged from 10.71 m/s to 12.50 m/s. Monthly average wind speeds ranged from 8.78 m/s to 15.81 m/s.When planning utility scale wind power plants, it is important to evaluate how the diurnal and seasonal wind speed patterns compare to electricity demand patterns. Is wind generated electricity available when people need it? If high wind speeds at a proposed site do not coincide with peak electricity demands, than that site may not be suitable for utility scale wind farms. Renewable energy planners should also consider the frequency with which wind speeds are within the acceptable operating range for a given turbine. For example, how often are wind speeds to slow for turbines to operate, or how often are wind speeds too high?# Expanded geographic scaleA [report](report.pdf) was created as a tool for expanding the geographic scale of this analysis. This report was generated using the [parameterized reporting](https://quarto.org/docs/computations/parameters.html) capabilities of [Quarto](https://quarto.org/), an open-source scientific and technical publishing system.The report can be modified by specifying parameters for:- site name (site_name)- site latitude (site_lat)- site longitude (site_lon)- start date (start_date)- end date (end_date)- turbine cut-in speed (cut_in_speed)- turbine cut-out speed (cut_out_speed)- required annual average wind speed (req_annual_avg_speed)Default parameters are:- start_date: '2012-01-01'- end_date: '2013-01-01'- cut_in_speed: 3.6 m/s- cut_out_speed: 24.6 m/s- req_annual_avg_speed: 5.8 m/sFor the specified site, the report answers the following questions:- is the annual average wind speed at least 13 mph (5.8 m/s)? [^1]- how often the wind is below the cut-in speed of 8 mph (3.6 m/s)? [^2]- how often the wind exceed the cut-out speed of 55 mph (24.6 m/s)?[^1]: The U.S. Energy Information Administration recommends an annual average wind speed of at least 9 mph (4 m/s) for small wind turbines and 13 mph (5.8 m/s) for utility-scale turbines. <https://www.eia.gov/energyexplained/wind/where-wind-power-is-harnessed.php#:~:text=Good%20places%20for%20wind%20turbines,)%20for%20utility%2Dscale%20turbines.>[^2]: The Office of Energy Efficiency & Renewable Energy notes a typical cut-in speed of 6 to 9 mpg and cut-out speed of 55 mph. <https://www.energy.gov/eere/articles/how-do-wind-turbines-survive-severe-storms>A new report can be generated using the command line interface. The code below shows an example that renders a report for New York City in 2010 using default values for cut-in speed, cut-out speed, and required annual average wind speed.> quarto render report.qmd -P site_name:"New York City" -P site_lat:40.7128 -P site_lon:-74.0059 -P start_date:2010-01-01 -P end_date:2011-01-01 \--output new_york_city_report.pdfThe function below can be used to generate multiple reports from a dataframe of parameters for different sites.```{r}#| eval: false#| column: page-rightrender_fun <-function(param_df){ quarto::quarto_render(input ="report.qmd",execute_params =list(site_name = param_df$site_name,site_lat = param_df$site_lat,site_lon = param_df$site_lon,start_date = param_df$start_date,end_date = param_df$end_date),output_file = glue::glue("{param_df$site_name}-report.pdf") )}```The next code chunks show an example of how to work with this function from a dataframe of input. Running the function generates four reports, one for each site.```{r}#| column: page-rightreport_parameters =data.frame(site_name =c("University of Delaware", "Umass Amherst", "UC Santa Barbara", "Golden, CO"), site_lat =c(39.68071, 42.39405, 34.413202, 39.74086), site_lon =c(-75.75173, -72.52938, -119.841955, -105.1686), start_date =c("2009-01-01", "2011-01-01", "2007-06-01", "2008-01-01"), end_date =c("2010-01-01", "2012-01-01", "2008-03-01", "2010-01-01"))``````{r}#| include: false#| eval: false#| column: page-rightsource(here("code_samples", "wind-resource-temporal-variability", "R", "render_fun.R"))param_list <-split(report_parameters, seq(nrow(report_parameters))) %>% purrr::walk(render_fun)``````{r}#| eval: false#| column: page-rightsource(here("code_samples", "wind-resource-temporal-variability", "R", "render_fun.R"))param_list <-split(report_parameters, seq(nrow(report_parameters))) %>% purrr::walk(render_fun)```The example reports generated above can be viewed here:[University of Delaware](University%20of%20Delaware-report.pdf)[UMass Amherst](Umass%20Amherst-report.pdf)[UC Santa Barbara](UC%20Santa%20Barbara-report.pdf)[NREL - Golden, CO](Golden,%20CO-report.pdf)# CitationsDraxl, C., B.M. Hodge, A. Clifton, and J. McCaa. 2015. [Overview and Meteorological Validation of the Wind Integration National Dataset Toolkit (Technical Report](https://www.nrel.gov/docs/fy15osti/61740.pdf), NREL/TP-5000-61740). Golden, CO: National Renewable Energy Laboratory.Draxl, C., B.M. Hodge, A. Clifton, and J. McCaa. 2015. "[The Wind Integration National Dataset (WIND) Toolkit.](https://www.sciencedirect.com/science/article/pii/S0306261915004237?via%3Dihub)" Applied Energy 151: 355366.Energy.gov. "How Do Wind Turbines Survive Severe Storms?" Accessed October 11, 2022. <https://www.energy.gov/eere/articles/how-do-wind-turbines-survive-severe-storms>.King, J., A. Clifton, and B.M. Hodge. 2014. [Validation of Power Output for the WIND Toolkit](https://www.nrel.gov/docs/fy14osti/61714.pdf) (Technical Report, NREL/TP-5D00-61714). Golden, CO: National Renewable Energy Laboratory."Where Wind Power Is Harnessed - U.S. Energy Information Administration (EIA)." Accessed October 11, 2022. <https://www.eia.gov/energyexplained/wind/where-wind-power-is-harnessed.php>.