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<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;
|
|
|
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<WaterDistributionCalculator.NodeConfig>();
|
var calcResultList = new List<CalcResultViewModel>(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<int, CalcNodeViewModel>(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<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 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<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);
|
}
|
}
|
|
|
|
}
|
|
}
|