using System.Text;
|
using System.Text.RegularExpressions;
|
|
namespace Yw.EPAnet
|
{
|
/// <summary>
|
/// 管网计算拓展
|
/// </summary>
|
public static class NetworkDirectionExtensions
|
{
|
/// <summary>
|
/// 简单计算
|
/// </summary>
|
public static CalcuResult CalcuDirection(this Network network)
|
{
|
var result = new CalcuResult();
|
|
//Null验证
|
if (network == null)
|
{
|
result.Succeed = false;
|
return result;
|
}
|
|
var linkdict = network.GetAllLinks().ToDictionary(p => p.Id);
|
var nodedict = network.GetAllNodes().ToDictionary(p => p.Id);
|
|
//获取系统临时文件目录,创建inp临时文件
|
var inpFilePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N") + ".inp");
|
//inp文本写入
|
var inpString = network.ToDirectionInpString();
|
File.WriteAllText(inpFilePath, inpString);
|
|
//加载管网
|
var epanet = new HydraulicCore(true);
|
var errOpen = epanet.open(inpFilePath, "", "");
|
if (errOpen != 0)
|
{
|
string errorMsg = epanet.geterrormsg();
|
result.Succeed = false;
|
result.FailedList.Add(new CalcuFailed()
|
{
|
Code = errOpen,
|
Message = $"{errorMsg}"
|
});
|
return result;
|
}
|
|
//水力计算
|
var errCalcu = epanet.solveH();
|
if (errCalcu != 0)
|
{
|
string errorMsg = epanet.geterrormsg();
|
result.Succeed = false;
|
result.FailedList.Add(new CalcuFailed()
|
{
|
Code = errCalcu,
|
Message = $"{errorMsg}"
|
});
|
return result;
|
}
|
|
int nodeCount = 0;
|
int linkCount = 0;
|
epanet.getcount((int)eCountType.Node, ref nodeCount);
|
epanet.getcount((int)eCountType.Link, ref linkCount);
|
|
const int MAXID = 31;
|
|
var sb = new StringBuilder(MAXID);
|
|
for (int i = 1; i <= nodeCount; i++)
|
{
|
epanet.getnodeid(i, sb);
|
var arr = new string[] { "Head", "Press", "Demand" }; //System.Enum.GetValues(typeof(HydraulicModel.NodeValueType));
|
var arrnum = new int[] { 10, 11, 9 };
|
var calcuNode = new CalcuNode()
|
{
|
Id = sb.ToString(),
|
};
|
for (var j = 0; j < arr.Length; j++)
|
{
|
float v = 0;
|
//var t = (EPAcore.Core.NodeValueType)j;
|
epanet.getnodevalue(i, arrnum[j], ref v);
|
switch (arr[j])
|
{
|
case "Head":
|
calcuNode.Head = v;
|
break;
|
case "Press":
|
calcuNode.Press = v;
|
break;
|
case "Demand":
|
calcuNode.Demand = v;
|
break;
|
}
|
}
|
result.NodeList.Add(calcuNode);
|
}
|
var nodeResultDict = result.NodeList.ToDictionary(p => p.Id);
|
for (int i = 1; i <= linkCount; i++)
|
{
|
epanet.getlinkid(i, sb);
|
//var arr = System.Enum.GetValues(typeof(HydraulicModel.LinkValueType));
|
var arr = new string[] { "Flow", "Velocity", "Headloss" }; //System.Enum.GetValues(typeof(HydraulicModel.NodeValueType));
|
var arrnum = new int[] { 8, 9, 10 };
|
var calcuLink = new CalcuLink()
|
{
|
Id = sb.ToString(),
|
};
|
for (var j = 0; j < arr.Length; j++)
|
{
|
float v = 0;
|
//var t = (EPAcore.Core.NodeValueType)j;
|
epanet.getlinkvalue(i, arrnum[j], ref v);
|
switch (arr[j])
|
{
|
case "Flow":
|
calcuLink.Flow = v;
|
break;
|
case "Velocity":
|
calcuLink.Velocity = v;
|
break;
|
case "Headloss":
|
calcuLink.HeadLoss = v;
|
break;
|
|
}
|
if (linkdict[calcuLink.Id] is Pipe p)
|
{
|
double minorloss1 = p.StartMinorLossCoeff * Math.Pow(calcuLink.Velocity, 2) / 2 / 9.81;
|
double minorloss2 = p.EndMinorLossCoeff * Math.Pow(calcuLink.Velocity, 2) / 2 / 9.81;
|
double minorloss = p.MinorLossCoeff * Math.Pow(calcuLink.Velocity, 2) / 2 / 9.81;
|
calcuLink.HeadLoss -= minorloss1 + minorloss2;
|
calcuLink.MinorLoss = minorloss;
|
calcuLink.FrictionLoss = calcuLink.HeadLoss - minorloss;
|
//if (nodedict[p.StartNode.Id] is Elbow)
|
{
|
nodeResultDict[p.StartNode.Id].MinorLoss += minorloss1;
|
}
|
//if (nodedict[p.EndNode.Id] is Elbow)
|
{
|
nodeResultDict[p.EndNode.Id].MinorLoss += minorloss2;
|
}
|
|
}
|
else if (!(linkdict[calcuLink.Id] is Pump))
|
{
|
calcuLink.MinorLoss = v;
|
}
|
|
|
|
|
}
|
result.LinkList.Add(calcuLink);
|
}
|
|
return result;
|
}
|
|
|
|
/// <summary>
|
/// 转换至Inp字符串
|
/// </summary>
|
public static string ToDirectionInpString(this Network network, CalcuResult minorResult = null)
|
{
|
//模板文件
|
var templateFilePath = TemplateHelper.GetPath();
|
if (!File.Exists(templateFilePath))
|
{
|
throw new Exception("模板文件不存在," + templateFilePath);
|
}
|
var templateString = File.ReadAllText(templateFilePath);
|
|
//状态
|
var statusSb = new StringBuilder();
|
statusSb.AppendLine(";ID \tStatus/Setting\r\n");
|
|
//扩散器
|
var emitterSb = new StringBuilder();
|
emitterSb.AppendLine(";Junction \tCoefficient");
|
|
//坐标
|
var coorStringBuilder = new StringBuilder();
|
coorStringBuilder.AppendLine(";Node X-Coord Y-Coord");
|
|
Dictionary<string, string> dictExchange = new Dictionary<string, string>() {
|
{"{junctions}","{0}" },
|
{"{reservoirs}","{1}" },
|
{"{tanks}","{2}" },
|
{"{pipes}","{3}" },
|
{"{valves}","{4}" },
|
{"{pumps}","{5}" },
|
|
{"{coor}","{6}" },
|
{"{curve}","{7}" },
|
{"{emitters}","{8}" },
|
};
|
dictExchange.ToList().ForEach(m => templateString = templateString.Replace(m.Key, m.Value));
|
|
//连接节点
|
var junctionStringBuilder = new StringBuilder();
|
junctionStringBuilder.AppendLine(";ID Elev Demand Pattern Type");
|
//连接节点处理
|
network.Junctions?.ForEach(x =>
|
{
|
var demandPattern = x.DemandPattern;
|
if (string.IsNullOrEmpty(demandPattern) || demandPattern == ";" || demandPattern == "_")
|
{
|
demandPattern = string.Empty;
|
}
|
junctionStringBuilder.AppendLine($"{x.Id}\t{x.Elev}\t{x.Demand}\t{demandPattern}\t;\t" + $"0\tJunction");
|
coorStringBuilder.AppendLine(x.Id + " " + x.Position.X + " " + x.Position.Y);
|
});
|
|
network.GetAllCouplings()?.ForEach(x =>
|
{
|
var demandPattern = x.DemandPattern;
|
if (string.IsNullOrEmpty(demandPattern) || demandPattern == ";" || demandPattern == "_")
|
{
|
demandPattern = string.Empty;
|
}
|
junctionStringBuilder.AppendLine($"{x.Id}\t{x.Elev}\t{x.Demand}\t{demandPattern}\t;\t" + $"0\tCoupling");
|
coorStringBuilder.AppendLine(x.Id + " " + x.Position.X + " " + x.Position.Y);
|
});
|
|
network.GetAllInstruments()?.ForEach(x =>
|
{
|
var demandPattern = x.DemandPattern;
|
if (string.IsNullOrEmpty(demandPattern) || demandPattern == ";" || demandPattern == "_")
|
{
|
demandPattern = string.Empty;
|
}
|
junctionStringBuilder.AppendLine($"{x.Id}\t{x.Elev}\t{x.Demand}\t{demandPattern}\t;\t" + $"0\tInstrument");
|
coorStringBuilder.AppendLine(x.Id + " " + x.Position.X + " " + x.Position.Y);
|
});
|
|
//水表处理
|
network.Meters?.ForEach(x =>
|
{
|
var demandPattern = x.DemandPattern;
|
if (string.IsNullOrEmpty(demandPattern) || demandPattern == ";" || demandPattern == "_")
|
{
|
demandPattern = "";
|
}
|
junctionStringBuilder.AppendLine($"{x.Id}\t{x.Elev}\t{x.Demand}\t{demandPattern}\t;\t" + $"0\tMeters");
|
coorStringBuilder.AppendLine(x.Id + " " + x.Position.X + " " + x.Position.Y);
|
});
|
//喷头处理
|
network.Nozzles?.ForEach(x =>
|
{
|
var demandPattern = x.DemandPattern;
|
if (string.IsNullOrEmpty(demandPattern) || demandPattern == ";" || demandPattern == "_")
|
{
|
demandPattern = "";
|
}
|
junctionStringBuilder.AppendLine($"{x.Id}\t{x.Elev}\t{x.Demand}\t{demandPattern}\t;\t" + $"0\tNozzle\t{x.Coefficient}");
|
double coefficient = x.Coefficient * Math.Pow(10 / 101.972, 0.5) / 1000 * 60;
|
emitterSb.AppendLine(x.Id + " " + coefficient);
|
coorStringBuilder.AppendLine(x.Id + " " + x.Position.X + " " + x.Position.Y);
|
});
|
var junctionString = junctionStringBuilder.ToString();
|
|
//水库处理
|
var reservoirSb = new StringBuilder();
|
reservoirSb.AppendLine(";ID Head Pattern ");
|
network.Reservoirs?.ForEach(x =>
|
{
|
reservoirSb.AppendLine($"{x.Id}\t{x.Head}\t{x.HeadPattern}\t;\t" + $"0\t{x.PoolElev}");
|
coorStringBuilder.AppendLine(x.Id + " " + x.Position.X + " " + x.Position.Y);
|
});
|
string reserverString = reservoirSb.ToString();
|
|
//水池处理
|
var tankSb = new StringBuilder();
|
tankSb.AppendLine(";ID Elevation InitLevel MinLevel MaxLevel Diameter MinVol VolCurve Overflow");
|
network.GetTanks()?.ForEach(o =>
|
{
|
// tankStringBuilder.AppendLine($"{o.Id}\t{o.PoolElev}\t{o.InitLevel}\t{o.MinLevel}\t{o.MaxLevel}\t{o.Diameter}\t{o.MinVol}\t{o.VolCurve}\t{o.Overflow}\t;\t");// + $"0");
|
tankSb.AppendLine($"{o.Id}\t{o.PoolElev}\t{o.InitLevel}\t{o.MinLevel}\t{o.MaxLevel}\t{o.Diameter}\t{o.MinVol}\t{o.VolCurve}\t;\t");// + $"0");
|
coorStringBuilder.AppendLine(o.Id + " " + o.Position.X + " " + o.Position.Y);
|
});
|
string tankString = tankSb.ToString();
|
|
//管道处理
|
var pipeSb = new StringBuilder();
|
pipeSb.AppendLine(";ID Node1 Node2 Length Diameter Roughness MinorLoss Status");
|
network.GetAllPipes()?.ForEach(x =>
|
{
|
if (x.Roughness == 0)
|
{
|
x.Roughness = 110;
|
}
|
string statusString = x.LinkStatus == PipeStatus.Closed ? "CLOSED" : "";
|
double minorLoss = x.MinorLossCoeff + x.EndMinorLossCoeff + x.StartMinorLossCoeff;
|
pipeSb.AppendLine($"{x.Id}\t{x.StartNode.Id}\t{x.EndNode.Id}\t{x.Length}\t{x.Diameter}\t{x.Roughness}\t{minorLoss}\t{statusString}\t;\t");// + $"{p.Level}");
|
if (x.LinkStatus != PipeStatus.Open)
|
{
|
statusSb.AppendLine(x.Id + "\t" + statusString);
|
}
|
});
|
string pipeString = pipeSb.ToString();
|
|
//阀门处理
|
var valveSb = new StringBuilder();
|
valveSb.AppendLine(";ID Node1 Node2 Diameter Type Setting MinorLoss ");
|
network.Valves?.ForEach(x =>
|
{
|
valveSb.AppendLine($"{x.Id}\t{x.StartNode.Id}\t{x.EndNode.Id}\t{x.Diameter:F4}\t{x.ValveType}\t{x.ValveSetting}\t{x.MinorLoss:F4}\t;\t");// + $"0");
|
if (!string.IsNullOrEmpty(x.LinkStatus))
|
{
|
statusSb.AppendLine(x.Id + "\t" + x.LinkStatus);
|
}
|
});
|
network.GetAllResistances()?.ForEach(x =>
|
{
|
string type = null;
|
if (x is Exchanger)
|
{
|
type = "Exchanger";
|
}
|
else if (x is Compressor)
|
{
|
type = "Compressor";
|
}
|
valveSb.AppendLine($"{x.Id}\t{x.StartNode.Id}\t{x.EndNode.Id}\t{x.Diameter:F4}\tGPV\t{x.CurveQL}\t{x.MinorLoss:F4}\t;\t{type}");// + $"0");
|
if (!string.IsNullOrEmpty(x.LinkStatus))
|
{
|
statusSb.AppendLine(x.Id + "\t" + x.LinkStatus);
|
}
|
});
|
string valveString = valveSb.ToString();
|
|
//水泵处理
|
var pumpSb = new StringBuilder();
|
pumpSb.AppendLine(";ID Node1 Node2 Parameters ");
|
network.Pumps?.ForEach(o =>
|
{
|
pumpSb.AppendLine($"{o.Id}\t{o.StartNode.Id}\t{o.EndNode.Id}\tHead\t{o.CurveQH}\t;\t");// + $"0");
|
string statusString = null;
|
if (o.LinkStatus == PipeStatus.Closed)
|
{
|
statusString = "\tCLOSED";
|
}
|
else
|
{
|
if (o.SpeedRatio == null)
|
statusString = "\tOPEN";
|
else
|
statusString = $"\t{o.SpeedRatio}";
|
}
|
statusSb.AppendLine(o.Id + statusString);
|
});
|
string pumpString = pumpSb.ToString();
|
|
//曲线处理
|
var curveSb = new StringBuilder();
|
curveSb.AppendLine(@";ID X-Value Y-Value
|
;HEADLOSS:
|
GPVDefault 0 0
|
GPVDefault 100 0
|
;PUMP:
|
PumpDefault 0 45.38
|
PumpDefault 83.33333333 45.25
|
PumpDefault 111.1111111 45.12
|
PumpDefault 138.8888889 44.96
|
PumpDefault 166.6666667 44.76
|
PumpDefault 194.4444444 44.52
|
PumpDefault 222.2222222 44.24
|
PumpDefault 250 43.92
|
PumpDefault 277.7777778 43.56
|
PumpDefault 305.5555556 43.17
|
PumpDefault 333.3333333 42.73
|
PumpDefault 361.1111111 42.25
|
PumpDefault 388.8888889 41.74
|
PumpDefault 416.6666667 41.18
|
PumpDefault 444.4444444 40.58
|
PumpDefault 472.2222222 39.95
|
PumpDefault 500 39.28
|
PumpDefault 527.7777778 38.56
|
PumpDefault 555.5555556 37.81
|
PumpDefault 583.3333333 37.02
|
PumpDefault 611.1111111 36.19
|
PumpDefault 638.8888889 35.32
|
PumpDefault 666.6666667 34.41
|
PumpDefault 694.4444444 33.46
|
PumpDefault 722.2222222 32.47
|
PumpDefault 750 31.44
|
PumpDefault 777.7777778 30.37
|
PumpDefault 805.5555556 29.27 ");
|
network.Curves?.ForEach(o =>
|
{
|
var curvePtList = o.CurveData?.OrderBy(x => x.X).ToList();
|
foreach (var curvePt in curvePtList)
|
{
|
curveSb.AppendLine($"{o.Id} {curvePt.X} {curvePt.Y}");
|
}
|
//curveStringBuilder.AppendLine(o.ToString());
|
});
|
string curveString = curveSb.ToString();
|
|
string coorString = coorStringBuilder.ToString();
|
string output = "";
|
string emitterString = emitterSb.ToString();
|
string statusString = statusSb.ToString();
|
output = templateString;
|
output = ReplaceContent(output, "JUNCTIONS", junctionString);
|
output = ReplaceContent(output, "RESERVOIRS", reserverString);
|
output = ReplaceContent(output, "TANKS", tankString);
|
output = ReplaceContent(output, "PIPES", pipeString);
|
output = ReplaceContent(output, "VALVES", valveString);
|
output = ReplaceContent(output, "PUMPS", pumpString);
|
output = ReplaceContent(output, "CURVES", curveString);
|
output = ReplaceContent(output, "COORDINATES", coorString);
|
output = ReplaceContent(output, "EMITTERS", emitterString);
|
output = ReplaceContent(output, "STATUS", statusString);
|
|
|
|
|
|
|
return output;
|
}
|
|
|
|
private static string ReplaceContent(string text, string content, string replaceString)
|
{
|
|
|
string str = replaceString;
|
|
string replacedText = ReplaceCoordinatesSection(text, content, str);
|
|
return replacedText;
|
//Console.WriteLine(replacedText);
|
}
|
public static string ReplaceCoordinatesSection(string text, string content, string str)
|
{
|
string pattern = $@"(\[{content}\]).*?(\[|$)";
|
|
string replacedText = Regex.Replace(text, pattern, match =>
|
{
|
string section = match.Groups[2].Value.Trim();
|
|
if (!string.IsNullOrEmpty(section))
|
{
|
return $"{match.Groups[1].Value}\n{str}\n[";
|
}
|
else
|
{
|
return $"{match.Groups[1].Value}\n{str}\n[";
|
}
|
}, RegexOptions.Singleline);
|
|
return replacedText;
|
}
|
}
|
}
|