# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# ## The next step in the gap analysis is to calculate the Turbine Ideal Energy (TIE) for the wind farm based on SCADA data

# %load_ext autoreload
# %autoreload 2

# This notebook provides an overview and walk-through of the turbine ideal energy (TIE) method in OpenOA. The TIE metric is defined as the amount of electricity generated by all turbines at a wind farm operating under normal conditions (i.e., not subject to downtime or significant underperformance, but subject to wake losses and moderate turbine performance losses). The approach to calculate TIE is to:
#
# 1. Filter out underperforming data from the power curve for each turbine,
# 2. Develop a statistical relationship between the remaining power data and key atmospheric variables from a long-term    reanalysis product
# 3. Long-term correct the period of record power data using the above statistical relationship
# 4. Sum up the long-term corrected power data across all turbines to get TIE for the wind farm
#
# Here we use different reanalysis products to capture the uncertainty around the modeled wind resource. We also consider uncertainty due to power data accuracy and the power curve filtering choices for identifying normal turbine performance made by the analyst.
#
# In this example, the process for estimating TIE is illustrated both with and without uncertainty quantification.

# +
# Import required packages
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

from project_ENGIE import Project_Engie
from operational_analysis.methods import turbine_long_term_gross_energy
# -

# In the call below, make sure the appropriate path to the CSV input files is specfied. In this example, the CSV files are located directly in the 'examples/data/la_haute_borne' folder

# Load plant object
project = Project_Engie('./data/la_haute_borne/')

# Load and prepare the wind farm data
project.prepare()

# Let's take a look at the columns in the SCADA data frame
project._scada.df.columns

# ### TIE calculation without uncertainty quantification
#
# Next we create a TIE object which will contain the analysis to be performed. The method has the ability to calculate uncertainty in the TIE metric through a Monte Carlo sampling of filtering thresholds, power data, and reanalysis product choices. For now, we turn this option off and run the method a single time.

ta = turbine_long_term_gross_energy.TurbineLongTermGrossEnergy(project) 

# All of the steps in the TI calculation process are pulled under a single run() function. These steps include:
#
# 1. Processing reanalysis data to daily averages.
# 2. Filtering the SCADA data
# 3. Fitting the daily reanalysis data to daily SCADA data using a Generalized Additive Model (GAM)
# 4. Apply GAM results to calculate long-term TIE for the wind farm
#
# By setting UQ = False (the default argument value), we must manually specify key filtering thresholds that would otherwise be sampled from a range of values through Monte Carlo. Specifically, we must set thresholds applied to the bin_filter() function in the toolkits.filtering class of OpenOA. 

# Specify filter threshold values to be used
wind_bin_thresh = 2.0 # Exclude data outside 2 m/s of the median for each power bin
max_power_filter = 0.90 # Don't apply bin filter above 0.9 of turbine capacity

# We also must decide how to deal with missing data when computing daily sums of energy production from each turbine. Here we set the threshold at 0.9 (i.e., if greater than 90% of SCADA data are available for a given day, scale up the daily energy by the fraction of data missing. If less than 90% data recovery, exclude that day from analysis.

# Set the correction threshold to 90%
correction_threshold = 0.90

# Now we'll call the run() method to calculate TIE, choosing two reanalysis products to be used in the TIE calculation process.

# We can choose to save key plots to a file by setting enable_plotting = True and 
# specifying a directory to save the images. For now we turn off this feature. 
ta.run(reanal_subset = ['era5', 'merra2'], enable_plotting = False, plot_dir = None,
       wind_bin_thresh = wind_bin_thresh, max_power_filter = max_power_filter,
       correction_threshold = correction_threshold)

# Now that we've finished the TIE calculation, let's examine results

ta._plant_gross

# What is the long-term annual TIE for whole plant
print('Long-term turbine ideal energy is %s GWh/year' %np.round(np.mean(ta._plant_gross/1e6),1))

# The long-term TIE value of 13.7 GWh/year is based on the mean TIE resulting from the two reanalysis products considered.

# Next, we can examine how well the filtering worked by examining the power curves for each turbine using the plot_filtered_power_curves() function.

# Currently saving figures in examples folder. The folder where figures are saved can be changed if desired.
ta.plot_filtered_power_curves(save_folder = "./", output_to_terminal = True)

# Overall these are very clean power curves, and the filtering algorithms seem to have done a good job of catching the most egregious outliers.

# Now let's look at the daily data and how well the power curve fit worked

# Currently saving figures in examples folder. The folder where figures are saved can be changed if desired.
ta.plot_daily_fitting_result(save_folder = "./", output_to_terminal = True)

# Overall the fit looks good. The modeled data sometimes estimate higher energy at low wind speeds compared to the observed, but keep in mind the model fits to long term wind speed, wind direction, and air density, whereas we are only showing the relationship to wind speed here.
#
# Note that 'imputed' means daily power data that were missing for a specific turbine, but were calculated by establishing statistical relationships with that turbine and its neighbors. This is necessary since a wind farm often has one turbine down and, without imputation, very little daily data would be left if we excluded days when a turbine was down.

# ### TIE calculation including uncertainty quantification
#
# Now we will create a TIE object for calculating TIE and quantifying the uncertainty in our estimate. The method estimates uncertainty in the TIE metric through a Monte Carlo sampling of filtering thresholds, power data, and reanalysis product choices.
#
# Note that we set the number of Monte Carlo simulations to only 100 in this example because of the relatively high computational effort required to perform a single iteration. In practice, a larger number of simulations is recommended (the default value is 2000).

ta = turbine_long_term_gross_energy.TurbineLongTermGrossEnergy(project, 
                                                               UQ = True, # enable uncertainty quantification
                                                               num_sim = 100 # number of Monte Carlo simulations to perform
                                                              ) 

# With uncertainty quantification enabled (UQ = True), we can specify the assumed uncertainty of the SCADA power data (0.5% by default) and ranges of two key filtering thresholds from which the Monte Carlo simulations will sample. Specifically, these thresholds are applied to the bin_filter() function in the toolkits.filtering class of OpenOA.
#
# Note that the following parameters are the default values used in the run() method.

# +
uncertainty_scada=0.005 # Assumed uncertainty of SCADA power data (0.5%)

# Range of filter threshold values to be used by Monte Carlo simulations

# Data outside of a range of wind speeds from 1 to 3 m/s of the median for each power bin are considered
wind_bin_thresh=(1, 3) 

# The bin filter will be applied up to fractions of turbine capacity from 80% to 90%
max_power_filter=(0.8, 0.9) 
# -

# We will consider a range of availability thresholds for dealing with missing data when computing daily sums of energy production from each turbine (i.e., if greater than the given threshold of SCADA data are available for a given day, scale up the daily energy by the fraction of data missing. If less than the given threshold of data are available, exclude that day from analysis. Here we set the range of thresholds as 85% to 95%. 

correction_threshold=(0.85, 0.95)

# Now we'll call the run() method to calculate TIE with uncertainty quantification, again choosing two reanalysis products to be used in the TIE calculation process.
#
# Note that without uncertainty quantification (UQ = False), a separate TIE value is calculated for each reanalysis product specified. However, when UQ = True, the reanalysis product is treated as another Monte Carlo sampling parameter. Thus, the impact of different reanlysis products is considered to be part of the overall uncertainty in TIE. 

# We can choose to save key plots to a file by setting enable_plotting = True and 
# specifying a directory to save the images. For now we turn off this feature. 
ta.run(reanal_subset = ['era5', 'merra2'], enable_plotting = False, plot_dir = None,
       uncertainty_scada = uncertainty_scada, wind_bin_thresh = wind_bin_thresh, 
       max_power_filter = max_power_filter, correction_threshold = correction_threshold)

# Now that we've finished the Monte Carlo TIE calculation simulations, let's examine results

np.mean(ta._plant_gross)

np.std(ta._plant_gross)

# +
# Mean long-term annual TIE for whole plant
print('Mean long-term turbine ideal energy is %s GWh/year' %np.round(np.mean(ta._plant_gross/1e6),1))

# Uncertainty in long-term annual TIE for whole plant
print('Uncertainty in long-term turbine ideal energy is %s GWh/year, or %s percent' % (np.round(np.std(ta._plant_gross/1e6),1), np.round(100*np.std(ta._plant_gross)/np.mean(ta._plant_gross),1)))
# -

# As expected, the mean long-term TIE is close to the earlier estimate without uncertainty quantification. A relatively low uncertainty has been estimated for the TIE calculations. This is a result of the relatively close agreement between the two reanalysis products and the clean power curves plotted earlier.

StarcoderdataJupyterScriptsDedupFiltered