| | |
| | | using DevExpress.Mvvm.Native; |
| | | using DevExpress.XtraCharts; |
| | | |
| | | namespace PBS.WinFrmUI.Hydro |
| | | namespace PBS.WinFrmUI.Hydro |
| | | { |
| | | public partial class SystemCurvePage : DocumentPage |
| | | { |
| | | public SystemCurvePage() |
| | | { |
| | | InitializeComponent(); |
| | | this.facilityTreeListCtrl1.SelectFacilityEvent += FacilityTreeListCtrl1_SelectFacilityEvent; |
| | | this.facilityTreeListCtrl1.SelectFacilityEvent += FacilityTreeListCtrl1_SelectFacilityEvent; |
| | | this.facilityTreeListCtrl1.RefreshDataEvent += () => { RefreshData(); }; |
| | | } |
| | | |
| | | |
| | | |
| | | private class ChartPressurePointViewModel |
| | | { |
| | | public ChartPressurePointViewModel() |
| | | { |
| | | } |
| | | public ChartPressurePointViewModel(double totalFlow, double upperPressure, double lowerPressure, double averagePressure) |
| | | { |
| | | this.TotalFlow = totalFlow; |
| | | this.UpperPressure = upperPressure; |
| | | this.LowerPressure = lowerPressure; |
| | | this.AveragePressure = averagePressure; |
| | | } |
| | | |
| | | public double TotalFlow { get; set; } |
| | | public double UpperPressure { get; set; } |
| | | public double LowerPressure { get; set; } |
| | | public double AveragePressure { get; set; } |
| | | } |
| | | |
| | | private class PointViewModel |
| | | { |
| | | public PointViewModel() |
| | | { |
| | | |
| | | } |
| | | public PointViewModel(double x, double y) |
| | | { |
| | | this.X = x; |
| | | this.Y = y; |
| | | } |
| | | |
| | | public double X { get; set; } |
| | | public double Y { get; set; } |
| | | } |
| | | |
| | | public class CalcResultViewModel |
| | | { |
| | | public double TotalFlow { get; set; } |
| | | public double EndPressure { get; set; } |
| | | public List<CalcNodeViewModel> CalcNodeList { get; set; } |
| | | |
| | | } |
| | | |
| | | public class CalcNodeViewModel |
| | | { |
| | | public int NodeIndex { get; set; } |
| | | public double Elevation { get; set; } |
| | | public double Flow { get; set; } |
| | | public double Pressure { get; set; } |
| | | } |
| | | |
| | | |
| | | private XYDiagram _xyDiagram; |
| | | private XYDiagramDefaultPane _xyDiagramPane; |
| | | |
| | | private Series _seriesWaterPoint; |
| | | private Series _seriesUpperPressure; |
| | | private Series _seriesLowerPressure; |
| | | private Series _seriesAveragePressure; |
| | | |
| | | private ConstantLine _constantLineDynamicPressure; |
| | | private ConstantLine _constantLineStaticPressure; |
| | | |
| | | |
| | | private Yw.Ahart.CubicCurve _curveUpperPressure = null; |
| | | private Yw.Ahart.CubicCurve _curveLowerPressure = null; |
| | | private Yw.Ahart.CubicCurve _curveAveragePressure = null; |
| | | private List<PointViewModel> _waterPointList = null; |
| | | |
| | | |
| | | private FacilityVmo _facility = null; |
| | | |
| | | |
| | | public override async void InitialDataSource() |
| | | { |
| | | var overlay = this.ShowOverlay(); |
| | | |
| | | this.chartControl1.Legend.Direction = LegendDirection.LeftToRight; |
| | | //this.chartControl1.AnimationStartMode = ChartAnimationMode.OnDataChanged; |
| | | this.chartControl1.RuntimeHitTesting = true; |
| | | //this.chartControl1.CrosshairEnabled = DevExpress.Utils.DefaultBoolean.False; |
| | | |
| | | _xyDiagram = this.chartControl1.Diagram as XYDiagram; |
| | | _xyDiagramPane = _xyDiagram.DefaultPane; |
| | | |
| | | _seriesWaterPoint = this.chartControl1.GetSeriesByName("SeriesWaterPoint"); |
| | | _seriesUpperPressure = this.chartControl1.GetSeriesByName("SeriesUpperPressure"); |
| | | _seriesLowerPressure = this.chartControl1.GetSeriesByName("SeriesLowerPressure"); |
| | | _seriesAveragePressure = this.chartControl1.GetSeriesByName("SeriesAveragePressure"); |
| | | |
| | | _constantLineDynamicPressure = _xyDiagram.AxisY.ConstantLines.GetElementByName("ConstantLineDynamicPressure") as ConstantLine; |
| | | _constantLineStaticPressure = _xyDiagram.AxisY.ConstantLines.GetElementByName("ConstantLineStaticPressure") as ConstantLine; |
| | | |
| | | _seriesWaterPoint.LegendTextPattern = "用水点"; |
| | | _seriesUpperPressure.LegendTextPattern = "压力-上限"; |
| | | _seriesLowerPressure.LegendTextPattern = "压力-下限"; |
| | | _seriesAveragePressure.LegendTextPattern = "压力-平均"; |
| | | |
| | | _seriesWaterPoint.CrosshairEnabled = DevExpress.Utils.DefaultBoolean.False; |
| | | _seriesUpperPressure.CrosshairEnabled = DevExpress.Utils.DefaultBoolean.True; |
| | | _seriesLowerPressure.CrosshairEnabled = DevExpress.Utils.DefaultBoolean.True; |
| | | _seriesAveragePressure.CrosshairEnabled = DevExpress.Utils.DefaultBoolean.True; |
| | | |
| | | this.chartControl1.CrosshairOptions.ShowOnlyInFocusedPane = false; |
| | | this.chartControl1.CrosshairOptions.GroupHeaderPattern = "{A:N2}"; |
| | | this.chartControl1.CrosshairOptions.GroupHeaderTextOptions.EnableAntialiasing = DevExpress.Utils.DefaultBoolean.True; |
| | | _seriesUpperPressure.CrosshairLabelPattern = "{V:N2}"; |
| | | _seriesLowerPressure.CrosshairLabelPattern = "{V:N2}"; |
| | | _seriesAveragePressure.CrosshairLabelPattern = "{V:N2}"; |
| | | |
| | | _constantLineDynamicPressure.ShowInLegend = false; |
| | | _constantLineStaticPressure.ShowInLegend = false; |
| | | |
| | | _constantLineDynamicPressure.Title.Alignment = ConstantLineTitleAlignment.Far; |
| | | _constantLineStaticPressure.Title.Alignment = ConstantLineTitleAlignment.Far; |
| | | |
| | | _constantLineDynamicPressure.LineStyle.Thickness = 2; |
| | | _constantLineStaticPressure.LineStyle.Thickness = 2; |
| | | |
| | | _xyDiagram.EnableAxisXZooming = true; |
| | | _xyDiagram.EnableAxisYZooming = true; |
| | | _xyDiagram.EnableAxisXScrolling = true; |
| | | _xyDiagram.EnableAxisYScrolling = true; |
| | | } |
| | | |
| | | |
| | | private FacilityVmo _facility = null; |
| | | |
| | | /// <summary> |
| | | /// 初始化数据源 |
| | | /// </summary> |
| | | public override async void InitialDataSource() |
| | | { |
| | | base.InitialDataSource(); |
| | | |
| | | var allFacilityList = await BLLFactory<BLL.Facility>.Instance.GetAll(); |
| | | this.facilityTreeListCtrl1.SetBindingData(allFacilityList); |
| | | |
| | | overlay.Close(); |
| | | this.facilityTreeListCtrl1.SetBindingData(allFacilityList); |
| | | |
| | | } |
| | | |
| | |
| | | { |
| | | InitialData(obj); |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | /// <summary> |
| | | /// 初始化数据 |
| | | /// </summary> |
| | | public void InitialData(FacilityVmo obj) |
| | | { |
| | | if (obj == null) |
| | | { |
| | | _facility = null; |
| | | return; |
| | | } |
| | | |
| | | _facility = obj; |
| | | this.facilityPropertyCtrl1.SetBindingData(obj); |
| | | this.systemCurveChartCtrl1.Clear(); |
| | | |
| | | } |
| | | |
| | |
| | | TipFormHelper.ShowWarn("请选择设施!"); |
| | | return; |
| | | } |
| | | |
| | | //嘉定 |
| | | ////var file_path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "1843913603388936192.inp"); |
| | | //var minDemand = 0; // 最小总需水量(m³/h) |
| | | //var maxDemand = 43; // 最大总需水量(m³/h) |
| | | //var reservoirElevation = -4; //水池标高 |
| | | //var calcCount = 1000; // 计算次数 |
| | | //var staticPressure = 22.5; //静压 |
| | | //var requiredEndPressure = 18; //静压 |
| | | |
| | | |
| | | |
| | | var file_path = _facility.ModelPath; |
| | | var minDemand = 0; // 最小总需水量(m³/h) |
| | | var maxDemand = _facility.MaxWaterDemand??45; // 最大总需水量(m³/h) |
| | | var reservoirElevation = -4; //水池标高 |
| | | var calcCount = 10; // 计算次数 |
| | | var staticPressure = 22.5; //静压 |
| | | var requiredEndPressure = _facility.TerminalPressure??15; //静压 |
| | | |
| | | CalcSystemCurve(file_path, minDemand, maxDemand, reservoirElevation, staticPressure, requiredEndPressure, calcCount); |
| | | var maxDemand = _facility.MaxWaterDemand??45; // 最大总需水量(m³/h) |
| | | var calcCount = 20000; // 计算次数 |
| | | var maxHeight = 22.5; //顶楼标高 |
| | | var requiredEndPressure = _facility.TerminalPressure ?? 15; //静压 |
| | | CalcSystemCurve(file_path, minDemand, maxDemand, maxHeight, requiredEndPressure, calcCount); |
| | | |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 计算系统曲线 |
| | | /// </summary> |
| | | /// <param name="inpFilePath"></param> |
| | | /// <param name="minDemand"></param> |
| | | /// <param name="maxDemand"></param> |
| | | /// <param name="reservoirElevation"></param> |
| | | /// <param name="maxHeight"></param> |
| | | /// <param name="requiredEndPressure"></param> |
| | | /// <param name="calcCount"></param> |
| | | private void CalcSystemCurve( |
| | | string inpFilePath, |
| | | double minDemand, |
| | | double maxDemand, |
| | | double reservoirElevation, |
| | | double staticPressure, |
| | | //double reservoirElevation, |
| | | double maxHeight, |
| | | double requiredEndPressure, |
| | | int calcCount) |
| | | { |
| | | |
| | | var pressure = 0d; |
| | | var nodeConfig = new List<WaterDistributionCalculator.NodeConfig>(); |
| | | var calcResultList = new List<CalcResultViewModel>(calcCount); |
| | | var waterPointCalcList = new List<WaterPointCalcViewModel>(calcCount); |
| | | using (var helper = new Yw.Epanet.InteropXHelper()) |
| | | { |
| | | var code = helper.Open(inpFilePath, "", ""); |
| | | CheckCode(code); |
| | | code = helper.GetCount(Yw.Epanet.eCountType.Node, out int totalNodeCount); |
| | | |
| | | CheckCode(code); |
| | | var rand = new Random(); |
| | | for (int nodeIndex = 1; nodeIndex <= totalNodeCount; nodeIndex++) |
| | | { |
| | | helper.GetNodeType(nodeIndex, out Yw.Epanet.eNodeType nodeType); |
| | | if (nodeType == Yw.Epanet.eNodeType.Junction) |
| | | { |
| | | code = helper.GetNodeId(nodeIndex, out string id); |
| | | if (!id.Contains("M")) |
| | | { |
| | | continue; |
| | | } |
| | | helper.GetNodeValue(nodeIndex, Yw.Epanet.eNodeProperty.Elevation, out double elevation); |
| | | nodeConfig.Add(new WaterDistributionCalculator.NodeConfig() |
| | | { |
| | |
| | | MinFlow = 0, |
| | | MaxFlow = rand.NextDouble() * 5 |
| | | }); |
| | | } |
| | | else if (nodeType == Yw.Epanet.eNodeType.Reservoir) |
| | | { |
| | | code = helper.SetNodeValue(nodeIndex, Yw.Epanet.eNodeProperty.Elevation, reservoirElevation); |
| | | CheckCode(code); |
| | | } |
| | | } |
| | | } |
| | | |
| | | var userNodeCount = nodeConfig.Count; |
| | |
| | | var allDemands = calculator.CalculateDistributions(); |
| | | |
| | | code = helper.OpenH(); |
| | | CheckCode(code); |
| | | for (int count = 0; count < allDemands.Count; count++) |
| | | { |
| | | var calcNodelDict = new Dictionary<int, CalcNodeViewModel>(userNodeCount); |
| | | var calcNodelDict = new Dictionary<int, WaterPointNodeViewModel>(userNodeCount); |
| | | var calcEndPressure = double.MaxValue; |
| | | |
| | | code = helper.InitH(false); |
| | | CheckCode(code); |
| | | var demands = allDemands[count]; |
| | | foreach (var node in nodeConfig) |
| | | { |
| | |
| | | code = helper.SetNodeValue(userNodeIndex, Yw.Epanet.eNodeProperty.BaseDemand, demand); |
| | | CheckCode(code); |
| | | |
| | | calcNodelDict[userNodeIndex] = new CalcNodeViewModel() |
| | | calcNodelDict[userNodeIndex] = new WaterPointNodeViewModel() |
| | | { |
| | | NodeIndex = userNodeIndex, |
| | | Index = userNodeIndex, |
| | | Flow = demand |
| | | }; |
| | | } |
| | | |
| | | code = helper.RunH(out long t); |
| | | |
| | | CheckCode(code); |
| | | |
| | | foreach (var node in nodeConfig) |
| | | { |
| | | var userNodeIndex = node.NodeIndex; |
| | | pressure = 0; |
| | | |
| | | //code = helper.GetNodeValue(userNodeIndex, Yw.Epanet.eNodeProperty.Pressure, out pressure); |
| | | //CheckCode(code); |
| | | //var endPressure = pressure + node.Elevation; |
| | | |
| | | code = helper.GetNodeValue(userNodeIndex, Yw.Epanet.eNodeProperty.Pressure, out pressure); |
| | | //自由压力=绝对压力-标高 |
| | | //默认起始点压力为最低水位,所以用 绝对压力 当自由压力 (绝对压力=自由压力+标高) |
| | | code = helper.GetNodeValue(userNodeIndex, Yw.Epanet.eNodeProperty.Head, out double head); |
| | | CheckCode(code); |
| | | var endPressure = pressure + node.Elevation; |
| | | var endPressure = head; |
| | | calcNodelDict[userNodeIndex].Pressure = endPressure; |
| | | |
| | | if (calcEndPressure > endPressure) |
| | | { |
| | | calcEndPressure = endPressure; |
| | | } |
| | | |
| | | if (Math.Abs(calcEndPressure) > 100) |
| | | { |
| | | code = helper.GetNodeId(userNodeIndex, out string id); |
| | | var b = id; |
| | | } |
| | | } |
| | | |
| | | |
| | | code = helper.NextH(out long tstep); |
| | | |
| | | var result = new CalcResultViewModel(); |
| | | result.TotalFlow = demands.TotalDemand; |
| | | result.EndPressure = Math.Abs(calcEndPressure); |
| | | result.CalcNodeList = calcNodelDict.Values.ToList(); |
| | | calcResultList.Add(result); |
| | | CheckCode(code); |
| | | var waterPointCalc = new WaterPointCalcViewModel(); |
| | | waterPointCalc.TotalFlow = demands.TotalDemand; |
| | | waterPointCalc.EndPressure = Math.Abs(calcEndPressure) + maxHeight + requiredEndPressure; |
| | | waterPointCalc.NodeList = calcNodelDict.Values.ToList(); |
| | | waterPointCalcList.Add(waterPointCalc); |
| | | |
| | | } |
| | | |
| | | |
| | | code = helper.Close(); |
| | | CheckCode(code); |
| | | } |
| | | |
| | | |
| | | |
| | | this.chartControl1.BeginInit(); |
| | | |
| | | |
| | | _curveUpperPressure = null; |
| | | _curveLowerPressure = null; |
| | | _curveAveragePressure = null; |
| | | _waterPointList = null; |
| | | |
| | | |
| | | _seriesWaterPoint.Points.Clear(); |
| | | _seriesUpperPressure.Points.Clear(); |
| | | _seriesLowerPressure.Points.Clear(); |
| | | _seriesAveragePressure.Points.Clear(); |
| | | |
| | | _constantLineDynamicPressure.Visible = false; |
| | | _constantLineStaticPressure.Visible = false; |
| | | |
| | | if (calcResultList.Any()) |
| | | { |
| | | _waterPointList = calcResultList.Select(x => new PointViewModel(x.TotalFlow, x.EndPressure + staticPressure + requiredEndPressure)).ToList(); |
| | | //_waterPointList = calcResultList.Select(x => new PointViewModel(x.TotalFlow, x.EndPressure + staticPressure)).ToList(); |
| | | |
| | | var waterPointGroup = _waterPointList.GroupBy(x => Math.Round(x.X, 1)); |
| | | var upperPressurePtList = new List<Yw.Geometry.Point2d>(); |
| | | var lowerPressurePtList = new List<Yw.Geometry.Point2d>(); |
| | | var averagePressurePtList = new List<Yw.Geometry.Point2d>(); |
| | | |
| | | foreach (var item in waterPointGroup) |
| | | { |
| | | var x = item.Key; |
| | | var yList = item.Select(x => x.Y).OrderBy(x => x).ToList(); |
| | | |
| | | var yUpper = CalculatePercentile(yList, 0.95); |
| | | var yLower = CalculatePercentile(yList, 0.05); |
| | | var yNumList = yList.Where(x => x >= yLower && x <= yUpper); |
| | | if (yNumList==null||!yNumList.Any()) |
| | | { |
| | | continue; |
| | | } |
| | | var yAverage = yNumList.Where(x => x >= yLower && x <= yUpper).Average(); |
| | | |
| | | upperPressurePtList.Add(new Yw.Geometry.Point2d(x, yUpper)); |
| | | lowerPressurePtList.Add(new Yw.Geometry.Point2d(x, yLower)); |
| | | averagePressurePtList.Add(new Yw.Geometry.Point2d(x, yAverage)); |
| | | |
| | | } |
| | | |
| | | _curveUpperPressure = new Yw.Ahart.CubicCurve(upperPressurePtList); |
| | | _curveLowerPressure = new Yw.Ahart.CubicCurve(lowerPressurePtList); |
| | | _curveAveragePressure = new Yw.Ahart.CubicCurve(averagePressurePtList); |
| | | |
| | | var upperPressureCurvePtList = _curveUpperPressure.GetPointList(100); |
| | | var lowerPressureCurvePtList = _curveLowerPressure.GetPointList(100); |
| | | var averagePressureCurvePtList = _curveAveragePressure.GetPointList(100); |
| | | |
| | | |
| | | foreach (var item in _waterPointList) |
| | | { |
| | | _seriesWaterPoint.Points.Add(new SeriesPoint(item.X, item.Y)); |
| | | } |
| | | foreach (var item in upperPressureCurvePtList) |
| | | { |
| | | _seriesUpperPressure.Points.Add(new SeriesPoint(item.X, item.Y)); |
| | | } |
| | | foreach (var item in lowerPressureCurvePtList) |
| | | { |
| | | _seriesLowerPressure.Points.Add(new SeriesPoint(item.X, item.Y)); |
| | | } |
| | | foreach (var item in averagePressureCurvePtList) |
| | | { |
| | | _seriesAveragePressure.Points.Add(new SeriesPoint(item.X, item.Y)); |
| | | } |
| | | |
| | | var minX = _waterPointList.Min(x => x.X); |
| | | var maxX = _waterPointList.Max(x => x.X); |
| | | |
| | | var minY = _waterPointList.Min(x => x.Y); |
| | | var maxY = _waterPointList.Max(x => x.Y); |
| | | |
| | | |
| | | //var minDynamicPressure = minY; |
| | | //_constantLineDynamicPressure.AxisValue = minDynamicPressure; |
| | | //_constantLineDynamicPressure.Title.Text = $"最低供水压力:{minDynamicPressure:N2}m"; |
| | | //_constantLineDynamicPressure.Visible = true; |
| | | |
| | | //var totalStaticPressure = staticPressure + Math.Abs(reservoirElevation); |
| | | //_constantLineStaticPressure.AxisValue = totalStaticPressure; |
| | | //_constantLineStaticPressure.Title.Text = $"最高楼层高度:{totalStaticPressure:N2}m"; |
| | | //_constantLineStaticPressure.Visible = true; |
| | | |
| | | //minY = Math.Min(staticPressure, minY); |
| | | //maxY = Math.Max(staticPressure, maxY); |
| | | |
| | | //minY = Math.Min(staticPressure, minY); |
| | | //maxY = Math.Max(staticPressure, maxY); |
| | | |
| | | //maxY = Math.Floor(maxY); |
| | | //maxX = Math.Floor(maxX); |
| | | |
| | | //_xyDiagram.AxisX.WholeRange.Auto = true; |
| | | //_xyDiagram.AxisX.VisualRange.Auto = true; |
| | | //_xyDiagram.AxisX.WholeRange.SideMarginsValue = 0; |
| | | //_xyDiagram.AxisX.VisualRange.SideMarginsValue = 0; |
| | | //_xyDiagram.AxisX.NumericScaleOptions.AutoGrid = true; |
| | | //_xyDiagram.AxisX.WholeRange.SetMinMaxValues(minX, 50); |
| | | //_xyDiagram.AxisX.VisualRange.SetMinMaxValues(minX, 50); |
| | | |
| | | //_xyDiagram.AxisY.WholeRange.Auto = true; |
| | | //_xyDiagram.AxisY.VisualRange.Auto = true; |
| | | //_xyDiagram.AxisY.NumericScaleOptions.AutoGrid = true; |
| | | //_xyDiagram.AxisY.WholeRange.SideMarginsValue = 0; |
| | | //_xyDiagram.AxisY.VisualRange.SideMarginsValue = 0; |
| | | //_xyDiagram.AxisY.WholeRange.SetMinMaxValues(minY, 70); |
| | | //_xyDiagram.AxisY.VisualRange.SetMinMaxValues(minY, 70); |
| | | |
| | | } |
| | | this.chartControl1.EndInit(); |
| | | |
| | | this.systemCurveChartCtrl1.SetBindingData(waterPointCalcList); |
| | | } |
| | | |
| | | private double CalculatePercentile(List<double> sortedData, double percentile) |
| | | { |
| | | int n = sortedData.Count; |
| | | double index = (n - 1) * percentile; |
| | | int lower = (int)Math.Floor(index); |
| | | int upper = (int)Math.Ceiling(index); |
| | | double fraction = index - lower; |
| | | return sortedData[lower] * (1 - fraction) + sortedData[upper] * fraction; |
| | | } |
| | | |
| | | |
| | | private void CheckCode(Yw.Epanet.eErrorCode code) |
| | | { |
| | | if (code != Yw.Epanet.eErrorCode.OK) |
| | | { |
| | | var msg = code.GetDisplayText(); |
| | | throw new Exception(msg); |
| | | if ((int)code>100) |
| | | { |
| | | var msg = code.GetDisplayText(); |
| | | throw new Exception(msg); |
| | | } |
| | | |
| | | } |
| | | } |
| | | |