/// <summary> /// приведение ряда к условиям плоской местности /// </summary> /// <param name="ms_range">ряд</param> /// <param name="expectancy">повторяемости направлений ветра</param> /// <param name="ms_classes">классы открытости МС</param> /// <param name="water_type">расстояние до водной поверхности</param> /// <returns></returns> public static RawRange ToFlatTerrain(RawRange ms_range, StatisticalRange <WindDirections8> expectancy, Dictionary <WindDirections8, double> ms_classes, WaterDistanceType water_type) { //1. найти Кмс //2. пересчитать ряд RawRange result = new RawRange(); double K0 = (int)water_type; //средневзвешенный коэффициент открытости метеостанции double Kms = 0; foreach (WindDirections8 dir in WindDirections8.N.GetEnumItems()) { if (dir != WindDirections8.Calm && dir != WindDirections8.Undefined && dir != WindDirections8.Variable) { Kms += expectancy[dir] * ms_classes[dir]; } } double k0 = K0 / Kms; result.BeginChange(); foreach (RawItem item in ms_range) { RawItem ni = item.Clone(); ni.Speed = item.Speed * k0; result.Add(ni); } result.EndChange(); return(result); }
/// <summary> /// перераспределяет штиль по румбам в пропорциях их вероятностей /// </summary> /// <typeparam name="T">тип градаций направления</typeparam> /// <param name="rawDirectionExpectancy">базовое распределение new StatisticalRange(...)</param> /// <returns></returns> private static StatisticalRange <T> reorganizeCalm <T>(StatisticalRange <T> rawDirectionExpectancy) { if (typeof(T) == typeof(WindDirections16)) { double dirs = rawDirectionExpectancy.Values.Sum() - rawDirectionExpectancy[WindDirections16.Calm]; for (int i = 0; i < 16; i++) { rawDirectionExpectancy[(WindDirections16)i] /= dirs; } return(rawDirectionExpectancy); } else if (typeof(T) == typeof(WindDirections8)) { double dirs = rawDirectionExpectancy.Values.Sum() - rawDirectionExpectancy[WindDirections8.Calm]; for (int i = 0; i < 8; i++) { rawDirectionExpectancy[(WindDirections8)i] /= dirs; } return(rawDirectionExpectancy); } else { throw new ArgumentException("Тип должен быть одним из типов румбов", nameof(T)); } }
/// <summary> /// проверка на соответствие нормальному закону распределния ряда. озвращая критерий согласия Пирсона для этого ряда /// http://www.ekonomstat.ru/kurs-lektsij-po-teorii-statistiki/403-proverka-sootvetstvija-rjada-raspredelenija.html /// https://life-prog.ru/2_84515_proverka-po-kriteriyu-hi-kvadrat.html критерий пирсона /// https://math.semestr.ru/group/example-normal-distribution.php для нормального распределения /// </summary> /// <param name="range">ряд</param> /// <param name="parameter">проверяемый параметр</param> /// <returns></returns> private static double checkNormalLaw(RawRange range, MeteorologyParameters parameter) { GradationInfo <GradationItem> grads; switch (parameter) { case MeteorologyParameters.Speed: grads = new GradationInfo <GradationItem>(0, SPEED_GRADATION_STEP, range.Max((item) => { return(item.Speed); })); //градации скорости break; case MeteorologyParameters.Direction: StatisticalRange <WindDirections16> srwd = StatisticEngine.GetDirectionExpectancy(range, GradationInfo <WindDirections16> .Rhumb16Gradations); return(0); case MeteorologyParameters.Temperature: grads = new GradationInfo <GradationItem>(0, TEMPERATURE_GRADATION_STEP, range.Max((item) => { return(item.Temperature); })); //градации температуры break; case MeteorologyParameters.Wetness: grads = new GradationInfo <GradationItem>(0, WETNESS_GRADATION_STEP, range.Max((item) => { return(item.Wetness); })); //градации влажности break; default: throw new WindEnergyException("Этот параметр не реализован"); } //РАСЧЕТ ДЛЯ ВСЕХ, КРОМЕ НАПРАВЛЕНИЙ StatisticalRange <GradationItem> stat_range = StatisticEngine.GetExpectancy(range, grads, parameter); //статистический ряд // TODO: расчет критерия пирсона для ряда return(0); }
/// <summary> /// получить статистический ряд по заданным значениям и заданным градациям /// </summary> /// <param name="tempr"></param> /// <param name="rhumb8Gradations"></param> /// <returns></returns> public static StatisticalRange <WindDirections8> GetDirectionExpectancy(RawRange tempr, GradationInfo <WindDirections8> rhumb8Gradations) { List <double> spds = new List <double>(from t in tempr select t.Direction); StatisticalRange <WindDirections8> r = new StatisticalRange <WindDirections8>(spds, rhumb8Gradations); return(reorganizeCalm(r)); }
/// <summary> /// получить статистический ряд по заданным значениям и заданным градациям /// </summary> /// <param name="range"></param> /// <param name="gradations"></param> /// <param name="parameter"></param> /// <returns></returns> public static StatisticalRange <GradationItem> GetExpectancy(IList <RawItem> range, GradationInfo <GradationItem> gradations, MeteorologyParameters parameter = MeteorologyParameters.Speed) { List <double> rang; switch (parameter) { case MeteorologyParameters.Speed: rang = new List <double>(from t in range select t.Speed); break; case MeteorologyParameters.Direction: rang = new List <double>(from t in range select t.Direction); break; case MeteorologyParameters.Temperature: rang = new List <double>(from t in range select t.Temperature); break; case MeteorologyParameters.Wetness: rang = new List <double>(from t in range select t.Wetness); break; default: throw new WindEnergyException("Этот параметр не реализован"); } StatisticalRange <GradationItem> r = new StatisticalRange <GradationItem>(rang, gradations); return(r); }
/// <summary> /// возвращает отклонение повторямости скорости от среднемноголетней /// </summary> /// <param name="range"></param> /// <param name="totalRange"></param> /// <returns></returns> private static double getExpectancyDeviation(RawRange range, RawRange totalRange) { StatisticalRange <GradationItem> exp = GetExpectancy(totalRange, Vars.Options.CurrentSpeedGradation); double averSpeed = totalRange.Average((t) => t.Speed); DeviationsInfo dinfo = ProcessRangeDeviations(range, averSpeed, exp); return(dinfo.ExpDeviation); }
/// <summary> /// сохранить энергетические характеристики в файл csv /// </summary> /// <param name="fileName">имя файла</param> /// <param name="range_info">характеристики по ряду наблюдений</param> /// <param name="ext_info">характеристики по градациям</param> /// <param name="stat_directions">повторяемость направлений</param> /// <param name="stat_speeds">повторяемость скоростей</param> /// <param name="append">если истина, то файл будет дописан</param> /// <param name="caption">заголовок, который будет записан в начале</param> /// <param name="month">значение графы месяц</param> /// <param name="year">значение графы год</param> /// <param name="amount">количество измерений в ряде</param> private void saveEnergyInfoLine( string fileName, EnergyInfo range_info, EnergyInfo ext_info, StatisticalRange <WindDirections16> stat_directions, StatisticalRange <GradationItem> stat_speeds, string caption, string year, string month, int amount, bool append ) { StreamWriter sw = new StreamWriter(fileName, append, Encoding.UTF8); //запись заголовка if (!string.IsNullOrEmpty(caption)) { sw.WriteLine(caption); } if (range_info == null || ext_info == null || stat_directions == null || stat_speeds == null) { sw.Close(); return; } // "Год;Месяц;кол-во изм;0.75;2.5;4.5;6.5;8.5;10.5;12.5;14.5;16.5;19;22.5;26.5;31.5;37.5;43.5;Vmin;Vmax;Vср.год;Vэкст50,м/с;δ(P);Cv(V);Nвал уд.;Эвал уд.;С;СВ;В;ЮВ;Ю;ЮЗ;З;СЗ;штиль"; string line = $"{year};{month};{amount}"; //повторяемости скоростей ветра for (int j = 0; j < stat_speeds.Keys.Count; j++) { line += ";" + (stat_speeds.Values[j] * 100).ToString("0.00"); } //по ряду наблюдений line += string.Format(";{0:f2};{1:f2};{2:f2};{3:f2};{4:f2};{5:f2};{6:f2};{7:f2};{8:f2};{9:f2};{10:f2}", range_info.Vmin, range_info.Vmax, range_info.V0, range_info.ExtremalSpeed, range_info.ExpectancyDeviation, range_info.Cv, range_info.StandardDeviationSpeed, range_info.VeybullGamma, range_info.VeybullBeta, range_info.PowerDensity, range_info.EnergyDensity); //повторяемости направлений ветра List <Enum> rs = WindDirections16.Calm.GetEnumItems().GetRange(0, 17); for (int j = 0; j < rs.Count; j++) { WindDirections16 rhumb = (WindDirections16)rs[j]; int index = stat_directions.Keys.IndexOf(rhumb); if (index == -1) { continue; } line += ";" + (stat_directions.Values[index] * 100).ToString("0.00"); } sw.WriteLine(line); sw.Close(); }
/// <summary> /// обновление всех элементов в соответствии с выбранными параметрами /// </summary> private void refreshInfo() { RawRange tempr = range.GetRange( radioButtonSelectPeriod.Checked, radioButtonSelectYearMonth.Checked, dateTimePickerFrom.Value, dateTimePickerTo.Value, comboBoxYear.SelectedItem, comboBoxMonth.SelectedItem ); if (tempr == null) { throw new Exception("что-то совсем не так!!"); } //расчет параметров try { range_info = StatisticEngine.ProcessRange(tempr, range); stat_speeds = StatisticEngine.GetExpectancy(tempr, Vars.Options.CurrentSpeedGradation); stat_directions = StatisticEngine.GetDirectionExpectancy(tempr, GradationInfo <WindDirections16> .Rhumb16Gradations); exp_info = StatisticEngine.ProcessRange(stat_speeds); } catch (Exception ex) { _ = MessageBox.Show(this, ex.Message, "Расчёт энергетических характеристик", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } if (range_info == null || exp_info == null) { _ = MessageBox.Show(this, "Для заданного ряда невозможно расчитать характеристики на выбранном интервале", "Расчёт энергетических характеристик", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } //вывод параметров labelEnergyDensity.Text = (range_info.EnergyDensity / 1000).ToString("0.00") + " кВт*ч/м^2"; labelPowerDensity.Text = range_info.PowerDensity.ToString("0.00") + " Вт/м^2"; labelCv.Text = range_info.Cv.ToString("0.000"); labelV0.Text = range_info.V0.ToString("0.0") + " м/с"; labelExtremalSpeed.Text = range_info.ExtremalSpeed.ToString("0.0") + " м/с"; labelVmax.Text = range_info.Vmax.ToString("0.0") + " м/с"; labelVmin.Text = range_info.Vmin.ToString("0.0") + " м/с"; labelStandardDeviationSpeed.Text = range_info.StandardDeviationSpeed.ToString("0.000") + " м/с"; labelGamma.Text = range_info.VeybullGamma.ToString("0.000"); labelBeta.Text = range_info.VeybullBeta.ToString("0.000") + " м/с"; labelAirDensity.Text = tempr.AirDensity.ToString("0.000") + " кг/м^3" + (Vars.Options.CalculateAirDensity ? " (расчёт)" : ""); labelEnergyDensityTV.Text = (exp_info.EnergyDensity / 1000).ToString("0.00") + " кВт*ч/м^2"; labelPowerDensityTV.Text = exp_info.PowerDensity.ToString("0.00") + " Вт/м^2"; labelCvTV.Text = exp_info.Cv.ToString("0.000"); labelV0TV.Text = exp_info.V0.ToString("0.0") + " м/с"; labelGammaTV.Text = exp_info.VeybullGamma.ToString("0.000"); labelBetaTV.Text = exp_info.VeybullBeta.ToString("0.000") + " м/с"; }
/// <summary> /// расчёт отклонений от заданных многолетних значений /// </summary> /// <param name="r">ряд за небольшой промежуток (год)</param> /// <param name="averSpeed">среднемноголетняя скорость</param> /// <param name="exp">многолетняя повторяемость скорости ветра</param> /// <returns></returns> internal static DeviationsInfo ProcessRangeDeviations(RawRange r, double averSpeed, StatisticalRange <GradationItem> exp) { DeviationsInfo res = new DeviationsInfo(); double aver = r.Average((t) => t.Speed); res.SpeedDeviation = Math.Abs(aver - averSpeed); StatisticalRange <GradationItem> th = GetExpectancy(r, Vars.Options.CurrentSpeedGradation); res.ExpDeviation = Math.Sqrt(exp.Values.Zip(th.Values, (a, b) => Math.Pow(a - b, 2)).Aggregate((x, y) => x + y)); //корень из суммы квадратов разностей повторяемостей многолетней и этого промежутка return(res); }
/// <summary> /// получить преобладающие направления ветра /// </summary> /// <param name="ms_range"></param> /// <param name="expectancy"></param> /// <returns></returns> public static List <WindDirections8> GetRhumbsDominants(RawRange ms_range, StatisticalRange <WindDirections8> expectancy = null) { List <WindDirections8> res = new List <WindDirections8>(); expectancy = expectancy ?? StatisticEngine.GetDirectionExpectancy(ms_range, GradationInfo <WindDirections8> .Rhumb8Gradations); //повторяемости скорости ветра по 8 румбам var maxes = expectancy.Values.OrderByDescending((d) => d).ToList(); res.Add((WindDirections8)expectancy.Keys[expectancy.Values.IndexOf(maxes[0])]); //первый всегда добавляем if (maxes[0] > 0.3 && maxes[0] - maxes[1] < 0.1) //второй добавляем если первый больше 30% и у второго отрыв меньше 10% { res.Add((WindDirections8)expectancy.Keys[expectancy.Values.IndexOf(maxes[1])]); } return(res); }
/// <summary> /// получить характеристики по заданным градациям /// </summary> /// <param name="speeds"></param> /// <returns></returns> public static EnergyInfo ProcessRange(StatisticalRange <GradationItem> speeds) { List <GradationItem> grads = speeds.Gradation.Items.ConvertAll((r) => (GradationItem)r); List <double> expect = speeds.Values; if (grads.Count != expect.Count) { throw new Exception("что-то опять не так"); } //средняя скорость: сумма произведений скорости градации на вероятность IEnumerable <double> en = grads.Zip(expect, (g, e) => g.Average * e); double V0 = en.Aggregate(0d, (x, y) => x + (double.IsNaN(y) ? 0 : y)); //удельная энергия double EDensity = (grads.Zip(expect, (g, e) => Math.Pow(g.Average, 3) * 0.5 * e * 8760 * Vars.Options.AirDensity)).Aggregate((x, y) => x + (double.IsNaN(y) ? 0 : y)); //удельная мощность double PDensity = EDensity / 8760d; //среднеквадратическое отклонение double sigm = Math.Sqrt((grads.Zip(expect, (g, e) => Math.Pow((g.Average - V0), 2) * e)).Aggregate((x, y) => x + (double.IsNaN(y) ? 0 : y))); //Cv double Cv = sigm / V0; double VeyGamma = getVeybullGamma(Cv); double VeyBeta = getVeybullBeta(V0, VeyGamma); return(new EnergyInfo() { Cv = Cv, EnergyDensity = EDensity, PowerDensity = PDensity, StandardDeviationSpeed = sigm, V0 = V0, VeybullBeta = VeyBeta, VeybullGamma = VeyGamma, }); }
/// <summary> /// запись одной строки в лист ВЭК. Возвращет число элементов в этой строке /// </summary> /// <param name="worksheet">страница для записи</param> /// <param name="line">строка</param> /// <param name="range_info">характеристики по ряду наблюдений</param> /// <param name="ext_info">характеристики по градациям</param> /// <param name="stat_directions">повторяемость направлений</param> /// <param name="stat_speeds">повторяемость скоростей</param> /// <param name="year">год</param> /// <param name="month">месяц</param> /// <param name="amount">число измерений</param> /// <returns>количество добавленных ячеек</returns> private int saveEnergyInfoLine(ExcelWorksheet worksheet, int line, EnergyInfo range_info, EnergyInfo ext_info, StatisticalRange <WindDirections16> stat_directions, StatisticalRange <GradationItem> stat_speeds, string year, string month, int amount) { if (range_info == null || ext_info == null || stat_directions == null || stat_speeds == null) { return(0); } // "Год;Месяц;кол-во изм;0.75;2.5;4.5;6.5;8.5;10.5;12.5;14.5;16.5;19;22.5;26.5;31.5;37.5;43.5;Vmin;Vmax;Vср.год;Vэкстр.50, м/с;δ(P);Cv(V);Nвал уд.;Эвал уд.;С;СВ;В;ЮВ;Ю;ЮЗ;З;СЗ;штиль"; List <object> values = new List <object>() { year, month, amount }; //повторяемости скоростей ветра for (int j = 0; j < stat_speeds.Keys.Count; j++) { values.Add(Math.Round((stat_speeds.Values[j] * 100), 2)); } //по ряду наблюдений values.AddRange(new List <object>() { Math.Round(range_info.Vmin, 2), Math.Round(range_info.Vmax, 2), Math.Round(range_info.V0, 2), Math.Round(range_info.ExtremalSpeed, 2), Math.Round(range_info.ExpectancyDeviation, 2), Math.Round(range_info.Cv, 2), Math.Round(range_info.StandardDeviationSpeed, 2), Math.Round(range_info.VeybullGamma, 2), Math.Round(range_info.VeybullBeta, 2), Math.Round(range_info.PowerDensity, 2), Math.Round(range_info.EnergyDensity, 2) }); //повторяемости направлений ветра List <Enum> rs = WindDirections16.Calm.GetEnumItems().GetRange(0, 17); for (int j = 0; j < rs.Count; j++) { WindDirections16 rhumb = (WindDirections16)rs[j]; int index = stat_directions.Keys.IndexOf(rhumb); if (index == -1) { continue; } values.Add(Math.Round((stat_directions.Values[index] * 100), 2)); } //запись всех значений for (int j = 1; j <= values.Count; j++) { worksheet.Cells[line, j].Value = values[j - 1]; } return(values.Count); }
/// <summary> /// сохранение ВЭК /// </summary> /// <param name="filename"></param> /// <param name="range"></param> public override void SaveEnergyInfo(string filename, RawRange range) { using (ExcelPackage excelPackage = new ExcelPackage()) { //Set some properties of the Excel document excelPackage.Workbook.Properties.Author = "Wind Energy"; excelPackage.Workbook.Properties.Title = range.Name; excelPackage.Workbook.Properties.Created = DateTime.Now; //Create the WorkSheet ExcelWorksheet worksheet = excelPackage.Workbook.Worksheets.Add("Лист 1"); List <object> years = new List <object>(); foreach (RawItem item in range) { if (!years.Contains(item.Date.Year)) { years.Add(item.Date.Year); } } double totalAverageSpeed = range.Average((t) => t.Speed); //формирование заголовка List <string> cap = new List <string>() { "Год", "Месяц", "Кол-во измерений" }; foreach (GradationItem grad in Vars.Options.CurrentSpeedGradation.Items) { cap.Add(grad.Average.ToString("0.00")); } cap.AddRange(new string[] { "Vmin, м/с", "Vmax, м/с", "Vср, м/с", "Vэкстр.50, м/с", "δ(P)", "Cv(V)", "𝜎(V)", "γ", "β", "Nвал уд., Вт/м^2", "Эвал уд., Вт*ч/м^2" }); foreach (WindDirections16 wd in WindDirections16.Calm.GetEnumItems().GetRange(0, 17)) { cap.Add(wd.Description()); } //ЗАПИСЬ ФАЙЛА //запись заголовка for (int j = 1; j <= cap.Count; j++) { worksheet.Cells[1, j].Value = cap[j - 1]; } worksheet.Cells[1, 1, 1, cap.Count].Style.Font.Bold = true; worksheet.Cells[1, 1, 1, cap.Count].Style.Fill.PatternType = ExcelFillStyle.Solid; worksheet.Cells[1, 1, 1, cap.Count].Style.Fill.BackgroundColor.SetColor(Color.Gray); worksheet.View.FreezePanes(2, 3); //запись данных обо всём периоде EnergyInfo ri1 = StatisticEngine.ProcessRange(range); StatisticalRange <WindDirections16> sd1 = StatisticEngine.GetDirectionExpectancy(range, GradationInfo <WindDirections16> .Rhumb16Gradations); StatisticalRange <GradationItem> ss1 = StatisticEngine.GetExpectancy(range, Vars.Options.CurrentSpeedGradation); EnergyInfo ei1 = StatisticEngine.ProcessRange(ss1); int cells = saveEnergyInfoLine(worksheet, 2, ri1, ei1, sd1, ss1, "Все года", "Все месяцы", range.Count); worksheet.Cells[2, 1, 2, cells].Style.Font.Bold = true; worksheet.Cells[2, 1, 2, cells].Style.Fill.PatternType = ExcelFillStyle.Solid; worksheet.Cells[2, 1, 2, cells].Style.Fill.BackgroundColor.SetColor(Color.LightGray); //запись данных для каждого года int line = 3; foreach (int year in years) //цикл по годам { //для каждого месяца в году for (int mt = 0; mt <= 12; mt++) //по месяцам, начиная со всех { Months month = (Months)mt; RawRange rn = range.GetRange(false, true, DateTime.Now, DateTime.Now, year, month.Description()); if (rn == null || rn.Count == 0) { continue; } EnergyInfo ri = StatisticEngine.ProcessRange(rn, range); StatisticalRange <WindDirections16> sd = StatisticEngine.GetDirectionExpectancy(rn, GradationInfo <WindDirections16> .Rhumb16Gradations); StatisticalRange <GradationItem> ss = StatisticEngine.GetExpectancy(rn, Vars.Options.CurrentSpeedGradation); EnergyInfo ei = StatisticEngine.ProcessRange(ss); cells = saveEnergyInfoLine(worksheet, line, ri, ei, sd, ss, year.ToString(), month.Description(), rn.Count); if (Math.IEEERemainder(mt + 1, 2) == 0 || mt == 0) //на нечетных строках добавляем серый фон { worksheet.Cells[line, 1, line, cells].Style.Fill.PatternType = ExcelFillStyle.Solid; worksheet.Cells[line, 1, line, cells].Style.Fill.BackgroundColor.SetColor(Color.LightGray); } if (month == Months.All) //для суммарной информации по году выделение жирным { worksheet.Cells[line, 1, line, cells].Style.Font.Bold = true; } line++; } } //запись данных для каждого месяца отдельно line += 3; //для каждого месяца в году for (int mt = 1; mt <= 12; mt++) //по месяцам { Months month = (Months)mt; RawRange rn = range.GetRange(false, true, DateTime.Now, DateTime.Now, "Все", month.Description()); if (rn == null || rn.Count == 0) { continue; } EnergyInfo ri = StatisticEngine.ProcessRange(rn, range); StatisticalRange <WindDirections16> sd = StatisticEngine.GetDirectionExpectancy(rn, GradationInfo <WindDirections16> .Rhumb16Gradations); StatisticalRange <GradationItem> ss = StatisticEngine.GetExpectancy(rn, Vars.Options.CurrentSpeedGradation); EnergyInfo ei = StatisticEngine.ProcessRange(ss); cells = saveEnergyInfoLine(worksheet, line, ri, ei, sd, ss, "Все года", month.Description(), rn.Count); if (Math.IEEERemainder(mt + 1, 2) == 0 || mt == 0) //на нечетных строках добавляем серый фон { worksheet.Cells[line, 1, line, cells].Style.Fill.PatternType = ExcelFillStyle.Solid; worksheet.Cells[line, 1, line, cells].Style.Fill.BackgroundColor.SetColor(Color.LightGray); } line++; } //Save your file FileInfo fi = new FileInfo(filename); excelPackage.SaveAs(fi); } }
/// <summary> /// обработка ряда и получение расчётного года /// </summary> /// <param name="Range"></param> /// <returns></returns> public static CalculateYearInfo ProcessRange(RawRange Range) { if (Range == null || Range.Count < 3) { throw new ArgumentException("Ряд не может быть null или длиной меньше трёх наблюдений"); } List <RawItem> range = new List <RawItem>(Range); range.Sort(new DateTimeComparerRawItem()); if (range[range.Count - 1].Date - range[0].Date < TimeSpan.FromDays(366)) { throw new ArgumentException("Ряд должен быть длиной больше одного года"); } //среднемноголетняя скорость double averSpeed = range.Average((i) => i.Speed); if (double.IsNaN(averSpeed)) { throw new WindEnergyException("Ряд содержит недопустимые для расчета значения скорости"); } //разделяем исходный ряд по годам var years = range.GroupBy((i) => i.Date.Year).ToList().ConvertAll((gr) => new RawRange(gr.ToList())); //ОБРАБОТКА ВСЕХ РЯДОВ И ПОЛУЧЕНИЕ ИНФОРМАЦИИ CalculateYearInfo res = new CalculateYearInfo(); res.AverageSpeed = averSpeed; res.Range = Range; foreach (RawRange r in years) { QualityInfo qinfo = Qualifier.ProcessRange(r); //если null, то ряд очень маленький или не удалось расчитать параметры if (r == null || r[r.Count - 1].Date - r[0].Date < TimeSpan.FromDays(364)) { continue; } StatisticalRange <GradationItem> exp = StatisticEngine.GetExpectancy(range, Vars.Options.CurrentSpeedGradation); DeviationsInfo dinfo = StatisticEngine.ProcessRangeDeviations(r, averSpeed, exp); double aver = r.Average((t) => t.Speed); SinglePeriodInfo spinf = new SinglePeriodInfo() { Interval = qinfo.Intervals.Count == 1 ? qinfo.Intervals[0].Interval : StandartIntervals.Variable, Completness = qinfo.Completeness * 100, Vmax = r.Max((t) => t.Speed), Year = r[0].Date.Year, From = r[0].Date, To = r[r.Count - 1].Date, SpeedDeviation = dinfo.SpeedDeviation, ExpectancyDeviation = dinfo.ExpDeviation, AverageSpeed = aver, SpeedDeviationPercent = (dinfo.SpeedDeviation / averSpeed) * 100d }; res.Years.Add(spinf); } //ВЫБОР РАСЧЕТНОГО ГОДА //получаем года с одинаковым интервалом и полнотой больше 95% List <SinglePeriodInfo> accepts = (from t in res.Years where t.Interval != StandartIntervals.Variable && t.Interval != StandartIntervals.Missing && t.Completness > 95 select t).ToList(); //если даже таких годов не нашлось, то выходим if (accepts.Count == 0) { return(res); } //если только один год остался, то его и оставляем if (accepts.Count == 1) { res.RecomendedYear = accepts[0]; return(res); } //проверка по отклонению скорости var ac = accepts.OrderBy((t) => t.SpeedDeviation).ToList(); //в начале ряда остаются минимальные отклонения скорости double startsDev = ac[0].SpeedDeviation; //запоминаем сааамое мальнкое отклонение (с ним сравниваем остальные, чтоб найти похожие) var ac2 = ac.TakeWhile(e => e.SpeedDeviation - startsDev < Vars.Options.MinimalSpeedDeviation).ToList(); //остаются только ряды с очень похожим отклонением скоростей if (ac2.Count == 1) //если остался только один, то его оставляем { res.RecomendedYear = ac2[0]; return(res); } //проверка по отклонению повторяемости var ac3 = ac2.OrderBy((t) => t.ExpectancyDeviation); //выбираем минимальное отклонение по повторяемости res.RecomendedYear = ac3.ToList()[0]; return(res); }
/// <summary> /// выбор текста для рекомендаций, какой тип рельефа лучше использовать /// </summary> /// <param name="range"></param> /// <param name="pointCoordinates"></param> /// <param name="geoinfo"></param> /// <returns></returns> public static Dictionary <string, Color> GetRecommendation(RawRange range, PointLatLng pointCoordinates, IGeoInfoProvider geoinfo) { if (range.Position.IsEmpty) { throw new WindEnergyException("Не заданы координаты метеостанции"); } if (pointCoordinates.IsEmpty) { throw new WindEnergyException("Не заданы координаты точки ВЭС"); } if (geoinfo == null) { throw new ArgumentNullException(nameof(geoinfo)); } Dictionary <string, Color> result = new Dictionary <string, Color>(); double L = EarthModel.CalculateDistance(range.Position, pointCoordinates) / 1000; //расстояние вкилометрах между МС и точкой ВЭС double Hms = geoinfo.GetElevation(range.Position); //высота точки МС double Hpoint = geoinfo.GetElevation(pointCoordinates); //высота точки ВЭС double Habs = Math.Max(Hms, Hpoint); //максимальная высота на у.м. double dH = Math.Abs(Hpoint - Habs); //перепад высот между МС и ВЭС StatisticalRange <WindDirections8> expectancy = StatisticEngine.GetDirectionExpectancy(range, GradationInfo <WindDirections8> .Rhumb8Gradations); //повторяемости скорости ветра по 8 румбам var maxes = expectancy.Values.OrderByDescending((d) => d).ToList(); if (maxes[0] < 0.2) //если все румбы меньше 20%, то нет преобладающего направления { result.Add("Нет преобладающего направления ветра", Color.Black); } else//выбор преобладающих направлений ветра { var dirs = GetRhumbsDominants(range, expectancy); string ln = dirs.Aggregate("", (str, dir) => str + $"{dir.Description()} ({Math.Round(expectancy[dir] * 100, 2)}%), ").Trim(new char[] { ' ', ',' }); result.Add("Преобладающие направления ветра: " + ln, Color.Black); } if (Hms > 750) { result.Add($"Высота точки МС больше 750 м над у. м. ({Hms:0.0} м), не рекомендуеся выполнять преобразование", Color.Red); } else if (Hpoint > 750) { result.Add($"Высота точки ВЭС больше 750 м над у. м. ({Hpoint:0.0} м), не рекомендуеся выполнять преобразование", Color.Red); } else { if (L > 50 && dH <= 750 && Habs <= 750) //макрорельеф { result.Add($"Рекомендуется выбрать макрорельеф (L={L:0.0} км, Δh={dH:0.0} м)", Color.Green); } if (L > 3 && L <= 50 && dH <= 750 && Habs <= 750) //мезорельеф { result.Add($"Рекомендуется выбрать мезорельеф (L={L:0.0} км, Δh={dH:0.0} м)", Color.Green); } if (L <= 3 && dH <= 80 && Habs <= 750) //микрорельеф { result.Add($"Рекомендуется выбрать микрорельеф (L={L:0.0} км, Δh={dH:0.0} м)", Color.Green); } } return(result); }
/// <summary> /// Сохранение ВЭК в формате CSV /// </summary> /// <param name="filename"></param> /// <param name="range"></param> public override void SaveEnergyInfo(string filename, RawRange range) { List <object> years = new List <object>(); foreach (RawItem item in range) { if (!years.Contains(item.Date.Year)) { years.Add(item.Date.Year); } } //формирование заголовка string cap = "Год;Месяц;кол-во изм"; foreach (GradationItem grad in Vars.Options.CurrentSpeedGradation.Items) { cap += ";" + grad.Average.ToString("0.00"); } cap += ";Vmin, м/с;Vmax, м/с;Vср, м/с;Vэкст50,м/с;Cv(V);𝜎(V);параметр γ;параметр β;Nвал уд., Вт/м^2;Эвал уд., Вт*ч/м^2"; foreach (WindDirections16 wd in WindDirections16.Calm.GetEnumItems().GetRange(0, 17)) { cap += ";" + wd.Description(); } //запись в файл saveEnergyInfoLine(filename, null, null, null, null, cap, "", "", 0, false); //запись заголовка //запись данных обо всём периоде EnergyInfo ri1 = StatisticEngine.ProcessRange(range); StatisticalRange <WindDirections16> sd1 = StatisticEngine.GetDirectionExpectancy(range, GradationInfo <WindDirections16> .Rhumb16Gradations); StatisticalRange <GradationItem> ss1 = StatisticEngine.GetExpectancy(range, Vars.Options.CurrentSpeedGradation); EnergyInfo ei1 = StatisticEngine.ProcessRange(ss1); saveEnergyInfoLine(filename, ri1, ei1, sd1, ss1, null, "Все года", "Все месяцы", range.Count, true); //запись данных для каждого года foreach (int year in years) //цикл по годам { //для каждого месяца в году for (int mt = 0; mt <= 12; mt++)//по месяцам, начиная со всех { Months month = (Months)mt; RawRange rn = range.GetRange(false, true, DateTime.Now, DateTime.Now, year, month.Description()); if (rn == null || rn.Count == 0) { continue; } EnergyInfo ri = StatisticEngine.ProcessRange(rn, range); StatisticalRange <WindDirections16> sd = StatisticEngine.GetDirectionExpectancy(rn, GradationInfo <WindDirections16> .Rhumb16Gradations); StatisticalRange <GradationItem> ss = StatisticEngine.GetExpectancy(rn, Vars.Options.CurrentSpeedGradation); EnergyInfo ei = StatisticEngine.ProcessRange(ss); saveEnergyInfoLine(filename, ri, ei, sd, ss, null, year.ToString(), month.Description(), rn.Count, true); } } //запись данных по месяцам saveEnergyInfoLine(filename, null, null, null, null, ";", "", "", 0, true); saveEnergyInfoLine(filename, null, null, null, null, ";", "", "", 0, true); saveEnergyInfoLine(filename, null, null, null, null, ";", "", "", 0, true); for (int mt = 1; mt <= 12; mt++)//по месяцам, начиная со всех { Months month = (Months)mt; RawRange rn = range.GetRange(false, true, DateTime.Now, DateTime.Now, "Все", month.Description()); if (rn == null || rn.Count == 0) { continue; } EnergyInfo ri = StatisticEngine.ProcessRange(rn, range); StatisticalRange <WindDirections16> sd = StatisticEngine.GetDirectionExpectancy(rn, GradationInfo <WindDirections16> .Rhumb16Gradations); StatisticalRange <GradationItem> ss = StatisticEngine.GetExpectancy(rn, Vars.Options.CurrentSpeedGradation); EnergyInfo ei = StatisticEngine.ProcessRange(ss); saveEnergyInfoLine(filename, ri, ei, sd, ss, null, "Все года", month.Description(), rn.Count, true); } }