Wind Resource Temporal Variability

Diurnal and monthly variability of NREL Wind Toolkit Data

Data Workflow

Visualization of full time series

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 and Monthly Variability

  • Both wind speed and electricity demands fluctuate throughout the day and seasonally
  • Wind and electricity patterns may not match
  • Unlike water resources, electricity can be challenging to store during low demands
  • When selecting sites for utility scale wind power it is important to have adequate wind speeds at the same time as peak electricity demands
  • Statistics such as the interquartile range and standard deviation can help quantify the spread of data

Diurnal and Monthly Variability

Hourly average

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

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'
)

Diurnal and Monthly Variability

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 = 5))

])

variables_to_hide = ['February', 'March', 'April', 'May', 'June', 'July',
                    'August', 'September', 'October', 'November', 'December']
fig.for_each_trace(lambda trace: trace.update(visible="legendonly") 
                   if trace.name in variables_to_hide else ())
                   
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'
)

Diurnal and Monthly Variability

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),
)

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(
        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),
)

Statistical Summary

min wind speed (m/s) max wind speed (m/s)
hourly 0.1 36.7
hourly average 10.7 12.5
monthly average 8.8 15.8



smallest variability greatest variability smallest average wind speed greatest average wind speed
monthly July November August February
hourly 19 13 16 1

Conclusions

  • Greatest monthly variability in November
  • Smallest monthly variability in July
  • Highest wind speeds in February
  • Lowest wind speeds in August
  • Slower wind speeds mid-day than at night
  • 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.

Expanded Geographic Scale

Created a parameterized report using Quarto as a tool to allow users to generate summary reports based on specified inputs from CLI or with render function for multiple sites.

Modify by specifying parameters for:

  • site name
  • site latitude
  • site longitude
  • start date
  • end date
  • turbine cut-in speed
  • turbine cut-out speed
  • required annual average wind 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
  • required annual avg speed: 5.8 m/s

Expanded Geographic Scale

In the CLI the example code below renders a report for NYC 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 (>)

This function generates multiples reports from a dataframe of parameters for different sites.

render_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"))}

param_list <- split(report_parameters, seq(nrow(report_parameters))) %>% 
  purrr::walk(render_fun)

Report

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)?

University of Delaware

UMass Amherst

UC Santa Barbara

NREL - Golden, CO

Citations

Draxl, C., B.M. Hodge, A. Clifton, and J. McCaa. 2015. Overview and Meteorological Validation of the Wind Integration National Dataset Toolkit (Technical Report, 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.” Applied Energy 151: 355366.

King, J., A. Clifton, and B.M. Hodge. 2014. Validation of Power Output for the WIND Toolkit (Technical Report, NREL/TP-5D00-61714). Golden, CO: National Renewable Energy Laboratory.

https://www.eia.gov/electricity/gridmonitor/dashboard/electric_overview/US48/US48

https://www.eia.gov/energyexplained/wind/where-wind-power-is-harnessed.php#:~:text=Good%20places%20for%20wind%20turbines,)%20for%20utility%2Dscale%20turbines.