using DevExpress.Mvvm.Native; using DevExpress.XtraCharts; using System.IO; using Yw.Untity; using Yw.WinFrmUI; namespace PBS.WinFrmUI.Hydro { public partial class SystemCurvePage : DocumentPage { public SystemCurvePage() { InitializeComponent(); } 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 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 _waterPointList = null; public override void InitialDataSource() { 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 void barBtnCalc_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e) { //嘉定 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; //静压 CalcSystemCurve(file_path, minDemand, maxDemand, reservoirElevation, staticPressure, requiredEndPressure, calcCount); } private void CalcSystemCurve( string inpFilePath, double minDemand, double maxDemand, double reservoirElevation, double staticPressure, double requiredEndPressure, int calcCount) { var pressure = 0d; var nodeConfig = new List(); var calcResultList = new List(calcCount); using (var helper = new Yw.Epanet.InteropXHelper()) { var code = helper.Open(inpFilePath, "", ""); code = helper.GetCount(Yw.Epanet.eCountType.Node, out int totalNodeCount); 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) { helper.GetNodeValue(nodeIndex, Yw.Epanet.eNodeProperty.Elevation, out double elevation); nodeConfig.Add(new WaterDistributionCalculator.NodeConfig() { NodeIndex = nodeIndex, Elevation = elevation, 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 calculator = new WaterDistributionCalculator(nodeConfig, calcCount, minDemand, maxDemand); var allDemands = calculator.CalculateDistributions(); code = helper.OpenH(); for (int count = 0; count < allDemands.Count; count++) { var calcNodelDict = new Dictionary(userNodeCount); var calcEndPressure = double.MaxValue; helper.InitH(false); var demands = allDemands[count]; foreach (var node in nodeConfig) { var userNodeIndex = node.NodeIndex; double demand = demands.NodeFlows[userNodeIndex]; code = helper.SetNodeValue(userNodeIndex, Yw.Epanet.eNodeProperty.BaseDemand, demand); CheckCode(code); calcNodelDict[userNodeIndex] = new CalcNodeViewModel() { NodeIndex = userNodeIndex, Flow = demand }; } helper.RunH(out long t); 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); CheckCode(code); var endPressure = pressure + node.Elevation; calcNodelDict[userNodeIndex].Pressure = endPressure; if (calcEndPressure > endPressure) { calcEndPressure = endPressure; } } 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); } helper.Close(); } 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(); var lowerPressurePtList = new List(); var averagePressurePtList = new List(); 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 yAverage = yList.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(); } private double CalculatePercentile(List 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); } } } }