Long Term Hub-Height Wind Speed
This script demonstrates one possible approach to calculating a long term hub-height time series.
Hub-height time series covering the on-site measurement period are re-scaled to match a user-entered long-term monthly wind speed profile, then rescaled such that they have an overall wind speed matching a long-term hub-height wind speed predicted in the calculate wind climate task in the UI.
/// <summary>
/// Predicts the long term hub height wind speed time series needed to run the power time series calculation
/// A LTHHWS time series is generated for every set of wind climate data used to initiate turbines
///
/// Process:
/// 1. Get the relevant hub height time series, as calculated in the Hub height time series task with "hhts" in the name
/// 2. Speedup HHTS by month to using a target monthly wind speed profile
/// 3. Scale the monthly-scaled HHTS to the long term mean wind speed on the initiating wind climate data
///
/// Resulting series are saved as calculated signals with the identifier LTHHWS{0}m
/// </summary>
public void Execute()
{
// USER INPUT:
// Specify the target monthly wind speed profile, representing the monthly wind speed to be applied at each site
List<double> longTermMonthlyWindSpeedProfile = new List<double>{
9.9, 10.1, 9.9, 8.8, 8.8, 9.9, 10.1, 9.9, 8.8, 10.2, 7.8, 5.6
};
// END OF USER INPUTS
Toolbox.Log("Predicting monthly profile re-scaled long term hub height wind speed time series for power time series inputs");
MeasurementCampaignToolbox mc = Toolbox.MeasurementCampaign;
// Suppress word reports being generated
bool suppressReportingStatus = Toolbox.MeasurementCampaign.SuppressReporting;
Toolbox.MeasurementCampaign.SuppressReporting = true;
// Iterate through all the wind climates used to initiate flow models
List<Tuple<MeasurementSite, WindClimate>> initiationMastAndWindClimates = GetAllInitiatingMastsAndWindClimates();
foreach (Tuple<MeasurementSite, WindClimate> mastAndWindClimate in initiationMastAndWindClimates)
{
MeasurementSite mast = mastAndWindClimate.Item1;
WindClimate hubHeightWindClimate = mastAndWindClimate.Item2;
double height = hubHeightWindClimate.HeightAboveGroundLevel;
// Get the hub height time series, calculated in the Hub height time series task with hhts in the name
string hubHeightSignalName = mast.CalculatedSignals.Where(s =>s.SignalName.ToLower().Contains("hhts")&& s.HeightAboveGround == height).Select(x => x.SignalName).FirstOrDefault();
SpeedTimeSeries hubHeightTimeSeries = mc.GetSpeedTimeSeries(hubHeightSignalName);
Toolbox.Log(string.Format("Generating a long-term hub height time series corresponding to {0} at {1}m", hubHeightTimeSeries.Name, height));
// Speedup HHTS by month to using the target monthly wind speed profile
List<double> longTermMonthlyMeanSpeeds = longTermMonthlyWindSpeedProfile;
List<double> measurementMonthlyMeanSpeeds = mc.GenerateStatistics(hubHeightTimeSeries).CalendarMonthStatistics.Select(m => m.Mean).ToList();
List<double> monthlyScaleFactors = elementWiseArrayDivision(longTermMonthlyMeanSpeeds, measurementMonthlyMeanSpeeds);
SpeedTimeSeries monthlyScaledHubHeightTimeSeries = (SpeedTimeSeries)mc.SpeedUpSeriesByMonth((TimeSeries)hubHeightTimeSeries, monthlyScaleFactors, Enumerable.Repeat(0.0, 12).ToList());
monthlyScaledHubHeightTimeSeries.Name = hubHeightTimeSeries.Name + "_monthlySpedUp";
Toolbox.Log("\t Sped up time series by month");
// Scale the monthly-scaled HHTS to the long term mean wind speed found of the initiating wind climate data
double targetLongTermHubHeightWindSpeed = hubHeightWindClimate.FrequencyDistribution.MeanWindSpeed;
double currentmeanSpeed = mc.GenerateStatistics(monthlyScaledHubHeightTimeSeries).AnnualOrOverall;
double rescalingFactor = targetLongTermHubHeightWindSpeed / currentmeanSpeed;
SpeedTimeSeries longTermHubHeightTimeSeries = mc.SpeedUpSeries(monthlyScaledHubHeightTimeSeries, rescalingFactor );
// Save resulting series with the identifier LTHHWS{0}m
mc.SaveResultsTimeSeries(longTermHubHeightTimeSeries, mast, string.Format("LTHHWS{0}m", height.ToString()), height, true);
Toolbox.Log(string.Format("\t Saved long term hub height wind speed time series {0} to mast {1}", longTermHubHeightTimeSeries.Name, mast.Name ));
}
Toolbox.MeasurementCampaign.SuppressReporting = suppressReportingStatus;
Toolbox.Log("Complete");
}
/// <summary>
/// Find the initiating data used in the flow model for every turbine
/// </summary>
/// <returns>A list of initiating MeasurementSite - WindClimate tuples</returns>
public List<Tuple<MeasurementSite, WindClimate>> GetAllInitiatingMastsAndWindClimates()
{
List<Tuple<MeasurementSite, WindClimate>> initiationMastAndWindClimates = new List<Tuple<MeasurementSite, WindClimate>>();
// get a list of all masts and wind climate which actually initiate turbines - these are the ones we need LTHHTS for
foreach (WindFarm windFarm in Workbook.WindFarms)
{
foreach(Turbine turbine in windFarm.Turbines)
{
FlowModel turbineFlowModel = Workbook.Climate.FlowModels.GetAtLocation(turbine.HubHeightLocation);
MeasurementSite initiationMast = turbineFlowModel.ReferenceMeasurementSite;
WindClimate initiatingWindClimate = turbineFlowModel.ReferenceWindClimate;
if (false == initiationMastAndWindClimates.Select(t => t.Item2).Contains(initiatingWindClimate))
{
initiationMastAndWindClimates.Add(
new Tuple<MeasurementSite, WindClimate>(initiationMast, initiatingWindClimate));
}
//Toolbox.Log(string.Format("Turbine {0} uses initiation mast {1} wind climate {2} at height {3}", turbine.Name, initiationMast.Name, initiatingWindClimate.FrequencyDistribution.Name, initiatingWindClimate.HeightAboveGroundLevel));
}
string report = "Found the following wind climate data initiating turbines:";
foreach (Tuple<MeasurementSite, WindClimate> mastAndWindClimate in initiationMastAndWindClimates)
{
report += string.Format("\n\tMast {0} uses wind climate FD {1}", mastAndWindClimate.Item1.Name, mastAndWindClimate.Item2.FrequencyDistribution.Name);
}
Toolbox.Log(report);
}
return initiationMastAndWindClimates;
}
public List<double> elementWiseArrayDivision (List<double> numerator, List<double> denominator)
{
if( numerator.Count!= denominator.Count)
{
throw new Exception("Numerator and demoninator lists are of non-equal lengths");
}
List<double> result = numerator.Select((val, index) => val / denominator[index]).ToList();
return result;
}