// Edit the Mast, height and input time series to be those stored in your project public void Execute() { Toolbox.Log("Start Procedure", LogLevel.Warn); try { // Set the reference Mast - this should be the mast holding the time series data Workbook.Climate.ReferenceMeasurementSite = Workbook.Climate.MeasurementSites["M3"]; Workbook.Climate.ReferenceMeasurementHeight = 94.0; // Height of wind climate to consider // set the speedups model to WindClimateData to use mast-to-mast speedups in wake calculations Workbook.ModelSettings.FlowSettings.SpeedupsModel = SpeedupsModel.WindClimateData; // Loading required signals on reference mast in to memory SpeedTimeSeries speedMeans = Toolbox.MeasurementCampaign.GetTimeSeries("M3~ws94NW~Mean"); SpeedTimeSeries speedStdDevs = Toolbox.MeasurementCampaign.GetTimeSeries("M3~ws94NW~StdDev"); DirectionTimeSeries directions = Toolbox.MeasurementCampaign.GetTimeSeries("M3~wd94~Mean"); TemperatureTimeSeries temperatures = Toolbox.MeasurementCampaign.GetTimeSeries("M3~Tlogger~Mean"); PressureTimeSeries pressures = Toolbox.MeasurementCampaign.GetTimeSeries("M3~Plogger~Mean"); List timeStampsList = null; List airDensitiesList = null; List flowCasesList = null; // if you input pressure and temperature in to this method time series air densities are calculated, relative humidity time series is optional // If no temperatures or pressures are input a fixed air density is taken from the workbook PrepareEnergyFlowCasesCalculationInputsFromTimeSeries(out timeStampsList, out airDensitiesList, out flowCasesList, speedMeans, speedStdDevs, directions, pressures, temperatures); Toolbox.Log("Time series matched and prepared for time series energy calculation"); Scenario timeSeriesEnergyResultsScenario = CalculateTimeSeriesEnergy(timeStampsList, flowCasesList, airDensitiesList); // export results string exportFilePath = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(Toolbox.CurrentWorkbookPath), string.Format("{0}_WFTimeSeriesEnergyResults NEW.tsv", DateTime.Now.ToString("yyyyMMdd_hhmm"))); ExportTimeSeriesEnergyResultsToFile(timeSeriesEnergyResultsScenario, timeStampsList, exportFilePath); Toolbox.Log("Time Series Energy Exported"); Toolbox.Log("End Procedure", LogLevel.Warn); } catch (Exception e) { Toolbox.Log(e.Message, LogLevel.Error); Toolbox.Log(e.StackTrace, LogLevel.Error); } } /// /// Takes TimeSeries inputs as lists, calculates and energy time series and exports the results in a file next to the project /// public Scenario CalculateTimeSeriesEnergy(List timeStampsList, List flowCasesList, List airDensitiesList) { Toolbox.MeasurementCampaign.SuppressReporting = true; // Assure workbook is not temporary if (string.IsNullOrEmpty(Toolbox.CurrentWorkbookPath)) { throw new Exception("Workbook needs to be saved before running this procedure. Exiting."); } // Run time series energy calculation Toolbox.Log(String.Format("Calculating time series energy for {0} records", timeStampsList.Count.ToString())); Scenario scenario = Toolbox.ScenarioFromWorkbook(); Toolbox.CalculateFlowCases(scenario, timeStampsList, flowCasesList, airDensitiesList); Toolbox.Log("Time Series Energy Calculation complete"); return scenario; } #region preparation of inputs for flow case calculation (lists) from TimeSeries /// /// Preparation of inputs for flow case calculation (lists) from TimeSeries /// /// Returned list of time DateTimes /// Returned list of AirDensities /// Returned list of flow cases /// /// /// /// If not specified we assume fixed air density based on reference workbook settings /// If not specified we assume fixed air density based on reference workbook settings /// If not specified we assume RH of 0.7 (70%) public void PrepareEnergyFlowCasesCalculationInputsFromTimeSeries(out List timeStampsList, out List airDensitiesList, out List flowCasesList, SpeedTimeSeries windSpeedMeanTimeSeries, SpeedTimeSeries windSpeedStdDevTimeSeries, DirectionTimeSeries directionTimeSeries, PressureTimeSeries pressureTimeSeries = null, TemperatureTimeSeries tempuratureTimeSeries = null, RelativeHumidityTimeSeries relativeHumidityTimeSeries = null) { bool calcuculateAirDensityTimeSeries = true; bool calcuculateAirDensityTimeSeriesWithRelHum = false; AirDensity fixedAirDensityAtReferenceLocation = null; if (pressureTimeSeries == null || tempuratureTimeSeries == null) { calcuculateAirDensityTimeSeries = false; fixedAirDensityAtReferenceLocation = CalculateAirDensity(Workbook.Climate.ReferenceMeasurementSite.HeightAboveSeaLevel + Workbook.Climate.ReferenceMeasurementHeight); Toolbox.Log(string.Format("Assuming a fixed air density at the reference measurement height of {0} kg/m3 based on extrapolation of the air density model and lapse rate set in the workbook", fixedAirDensityAtReferenceLocation.ToString()), LogLevel.Warn); } else { if (relativeHumidityTimeSeries != null) { calcuculateAirDensityTimeSeriesWithRelHum = true; } } flowCasesList = new List(); airDensitiesList = new List(); timeStampsList = new List(); #region match all time series inputs // get dummy time series matched with all others so we can match multiple series var timeSeriesToMatch = new List() { windSpeedMeanTimeSeries, windSpeedStdDevTimeSeries, directionTimeSeries }; if (calcuculateAirDensityTimeSeries) { timeSeriesToMatch.AddRange(new List() { pressureTimeSeries, tempuratureTimeSeries }); if (calcuculateAirDensityTimeSeriesWithRelHum) { timeSeriesToMatch.Add(relativeHumidityTimeSeries); } } // match individual series TimeSeries matchedWindSpeedMeans = Toolbox.MeasurementCampaign.MatchMultipleSeries(MoveItemToStartOfList(timeSeriesToMatch, windSpeedMeanTimeSeries)); TimeSeries matchedWindSpeedStdDevs = Toolbox.MeasurementCampaign.MatchMultipleSeries(MoveItemToStartOfList(timeSeriesToMatch, windSpeedStdDevTimeSeries)); TimeSeries matchedDirections = Toolbox.MeasurementCampaign.MatchMultipleSeries(MoveItemToStartOfList(timeSeriesToMatch, directionTimeSeries)); TimeSeries matchedPressures = null; TimeSeries matchedTempuratures = null; TimeSeries matchedRelativeHumidities = null; if (calcuculateAirDensityTimeSeries) { matchedPressures = Toolbox.MeasurementCampaign.MatchMultipleSeries(MoveItemToStartOfList(timeSeriesToMatch, pressureTimeSeries)); matchedTempuratures = Toolbox.MeasurementCampaign.MatchMultipleSeries(MoveItemToStartOfList(timeSeriesToMatch, tempuratureTimeSeries)); if (calcuculateAirDensityTimeSeriesWithRelHum) { matchedRelativeHumidities = Toolbox.MeasurementCampaign.MatchMultipleSeries(MoveItemToStartOfList(timeSeriesToMatch, relativeHumidityTimeSeries)); } } #endregion #region export matched time series to string and parse this in to Lists TimeSeriesExportSettings exportSettings = new TimeSeriesExportSettings(); exportSettings.IsPadded = false; exportSettings.DecimalPlaces = 6; exportSettings.ExpandToYearBoundaries = false; var matchedSeriesList = new List() { matchedWindSpeedMeans, matchedWindSpeedStdDevs, matchedDirections }; if (calcuculateAirDensityTimeSeries) { matchedSeriesList.AddRange(new List() { matchedPressures, matchedTempuratures }); if (calcuculateAirDensityTimeSeriesWithRelHum) { matchedSeriesList.Add(matchedRelativeHumidities); } } string matchedTimeSeriesString = Toolbox.MeasurementCampaign.ExportTimeSeriesToString(matchedSeriesList, exportSettings); List timeRecordsStringList = matchedTimeSeriesString.Split('\n').ToList(); timeRecordsStringList.RemoveAt(timeRecordsStringList.Count - 1); foreach (string timeSeriesRecordString in timeRecordsStringList.Skip(1)) { string[] splitTimeSeriesRecord = timeSeriesRecordString.Split('\t'); // flow case ISpeed windSpeed = new Speed(double.Parse(splitTimeSeriesRecord[1])); IStandardDeviation windSpeedStdDev = new StandardDeviation(double.Parse(splitTimeSeriesRecord[2])); IDirectionBearing direction = new DirectionBearing(double.Parse(splitTimeSeriesRecord[3])); if (double.IsNaN(windSpeed.ToDouble()) || double.IsNaN(windSpeedStdDev.Value) || double.IsNaN(direction.Value)) { continue; } // air density if (calcuculateAirDensityTimeSeries) { double pressure = double.Parse(splitTimeSeriesRecord[4]); double tempurature = double.Parse(splitTimeSeriesRecord[5]); double relativeHumidity = double.NaN; if (calcuculateAirDensityTimeSeriesWithRelHum) { double.Parse(splitTimeSeriesRecord[6]); } if (double.IsNaN(pressure) || double.IsNaN(tempurature)) { continue; } airDensitiesList.Add(CalculateAirDensity(pressure, tempurature, relativeHumidity)); } else // working with fixed air density { airDensitiesList.Add(new AirDensity(fixedAirDensityAtReferenceLocation)); } flowCasesList.Add(new PointLocationFlowCase(windSpeed, direction, windSpeedStdDev)); // timestamp DateTime parsedTime = new DateTime(); if (DateTime.TryParse(splitTimeSeriesRecord[0], out parsedTime)) { timeStampsList.Add(parsedTime); } else { throw new Exception(string.Format("Can't parse date time {0}", splitTimeSeriesRecord[0])); } } #endregion } /// /// Swaps the desired item with the one at the start of the list. /// /// /// public List MoveItemToStartOfList(List list, TimeSeries item) { int index = list.FindIndex(t => t == item); if (index == -1) { throw new Exception(string.Format("Item {0} not found in list when trying to re-order", item.Name)); } var tempItem = list[index]; list[index] = list[0]; list[0] = tempItem; return list; } #endregion #region Air density methods public AirDensity CalculateAirDensity(double pressure, double temperature, double relativeHumidity) { double temperatureInK = temperature + 273.15; double pressureInPa = pressure * 100; double Ro = 287.05; // [J/Kg/K] double Rw = 461.5; // [J/Kg/K] double Pw = 0.0000205 * Math.Exp(0.0631846 * temperatureInK); // [Pa] double phi = (double.IsNaN(relativeHumidity) == true) ? 0.7 : relativeHumidity / 100; // Relative humidity between 0 and 1, default of 0.7 double rho = 1 / temperatureInK * (pressureInPa / Ro - phi * Pw * (1 / Ro - 1 / Rw)); return new AirDensity(rho); } public AirDensity CalculateAirDensity(double heightOfMeasurementAboveSeaLevel) { // No measurements available so correct workbooks air density to measurement height double lapseRate = Workbook.Climate.AirDensityModel.AirDensityLapseRate; double rho = Workbook.Climate.AirDensityModel.ReferenceAirDensity + lapseRate * (heightOfMeasurementAboveSeaLevel - Workbook.Climate.AirDensityModel.ReferenceElevation); return new AirDensity(rho); } #endregion #region Export Results to file public void ExportTimeSeriesEnergyResultsToFile(Scenario scenario, List timeStampsList, string exportFilePath) { var resultsToExport = new List>(); #region construct header var headerLine = new List(); headerLine.Add("Timestamp"); headerLine.Add("wind speed mean[m/s]"); headerLine.Add("wind speed standard deviation [m/s]"); headerLine.Add("wind direction [degrees]"); // add wind farm names foreach (ReadOnlyWindFarm windFarm in scenario.WindFarms.Where(t => t.ExcludeFromCalculation == false)) { headerLine.Add(string.Format("Wind Farm Total Power: {0} [kW]", windFarm.Name)); } // add turbine names foreach (ReadOnlyWindFarm windFarm in scenario.WindFarms.Where(t => t.ExcludeFromCalculation == false)) { foreach (ReadOnlyTurbine turbine in windFarm.Turbines.Where(t => t.ExcludeFromCalculation == false)) { headerLine.Add(string.Format("{0} [kW]", turbine.Name)); } } resultsToExport.Add(headerLine); #endregion #region construct time series results foreach (DateTime timestamp in timeStampsList) { SingleFlowCase flowCaseResult = scenario.GetForDateTime(timestamp); var resultsLine = new List(); resultsLine.Add(timestamp.ToString("yyyy-MM-dd HH:mm")); // wind speed mean[m/s] // wind speed standard deviation [m/s] // wind direction [degrees] resultsLine.Add(flowCaseResult.ReferenceLocationFlowCase.WindSpeed.ToDouble().ToString("0.00")); resultsLine.Add(flowCaseResult.ReferenceLocationFlowCase.WindSpeedStandardDeviation.Value.ToString("0.00")); resultsLine.Add(flowCaseResult.ReferenceLocationFlowCase.Direction.Value.ToString("0.0")); var windFarmPowersLine = new List(); var turbinePowersLine = new List(); foreach (ReadOnlyWindFarm windFarm in scenario.WindFarms.Where(t => t.ExcludeFromCalculation == false)) { double totalWindFarmPower = 0.0; foreach (ReadOnlyTurbine turbine in windFarm.Turbines) { double flowCaseTurbinePower = flowCaseResult.TurbinePowers[turbine] / 1000; // Turbine powers are in W but we want to report in kW totalWindFarmPower += flowCaseTurbinePower; turbinePowersLine.Add(flowCaseTurbinePower.ToString("0.")); } windFarmPowersLine.Add(totalWindFarmPower.ToString("0.")); totalWindFarmPower = 0; } resultsLine.AddRange(windFarmPowersLine); resultsLine.AddRange(turbinePowersLine); resultsToExport.Add(resultsLine); } #endregion Toolbox.Log("Processing results complete"); PrintResults(resultsToExport, exportFilePath); } public void PrintResults(List> results, string filePath) { Toolbox.Log(string.Format("Printing results to file: {0}", filePath)); List> contents = GlobalHeader(); contents.AddRange(results); Write(contents, filePath); } public List> GlobalHeader() { return new List> { new List {DateTime.Now.ToString()}, new List {"Version", System.Reflection.Assembly.GetEntryAssembly().GetName().Version.ToString()}, new List {"CalculationType", "Time series energy" } , new List {"Reference measurement site", Workbook.Climate.ReferenceMeasurementSite.Name } , new List {"Reference measurement height", Workbook.Climate.ReferenceMeasurementHeight.ToString() }, new List {"Mast-to-mast speedups model", Workbook.ModelSettings.FlowSettings.SpeedupsModel.ToString() } }; } public void Write(List> contents, string filePath) { using (System.IO.StreamWriter file = new System.IO.StreamWriter(filePath)) { foreach (List line in contents) { foreach (string value in line) { file.Write(value + "\t"); } file.Write("\n"); } } } #endregion