using System.Collections.Concurrent;
|
using System.Runtime.InteropServices;
|
|
|
namespace PBS.WinFrmUI.Hydro
|
{
|
public class SystemCurveCalcHelper
|
{
|
|
|
|
}
|
|
public class ParallelEpanetSimulator : IDisposable
|
{
|
#region EPANET API 声明
|
// 注:需根据实际EPANET 2.2的C导出函数签名调整
|
[DllImport("epanet2.dll", EntryPoint = "EN_open", CallingConvention = CallingConvention.Cdecl)]
|
private static extern int EN_open(string inpFile, string rptFile, string outFile);
|
|
[DllImport("epanet2.dll", EntryPoint = "EN_close", CallingConvention = CallingConvention.Cdecl)]
|
private static extern int EN_close();
|
|
[DllImport("epanet2.dll", EntryPoint = "EN_getcount", CallingConvention = CallingConvention.Cdecl)]
|
private static extern int EN_getcount(int countType);
|
|
[DllImport("epanet2.dll", EntryPoint = "EN_setnodevalue", CallingConvention = CallingConvention.Cdecl)]
|
private static extern int EN_setnodevalue(int index, int paramCode, double value);
|
|
[DllImport("epanet2.dll", EntryPoint = "EN_initH", CallingConvention = CallingConvention.Cdecl)]
|
private static extern int EN_initH(int saveFlag);
|
|
[DllImport("epanet2.dll", EntryPoint = "EN_runH", CallingConvention = CallingConvention.Cdecl)]
|
private static extern int EN_runH(ref long currentTime);
|
|
[DllImport("epanet2.dll", EntryPoint = "EN_nextH", CallingConvention = CallingConvention.Cdecl)]
|
private static extern int EN_nextH(ref long timeStep);
|
|
private const int EN_NODECOUNT = 0;
|
private const int EN_BASEDEMAND = 1;
|
#endregion
|
|
#region 核心参数
|
private readonly string _inpFilePath;
|
private readonly double _minDemand;
|
private readonly double _maxDemand;
|
private readonly int _nodeCount;
|
private readonly int _totalIterations;
|
public ConcurrentBag<SimulationResult> _results = new();
|
#endregion
|
|
public ParallelEpanetSimulator(string inpPath, double minDemand, double maxDemand, int iterations)
|
{
|
_inpFilePath = inpPath;
|
_minDemand = minDemand;
|
_maxDemand = maxDemand;
|
_totalIterations = iterations;
|
|
using (var helper = new Yw.Epanet.InteropXHelper())
|
{
|
// 初始化获取节点数量
|
var err = helper.Open(_inpFilePath, "", "");
|
CheckError((int)err);
|
|
|
err = helper.GetCount(Yw.Epanet.eCountType.Node, out int count);
|
helper.Close();
|
_nodeCount = count;
|
}
|
}
|
|
public List<SimulationResult> RunParallelSimulations(int maxDegreeOfParallelism = -1)
|
{
|
// 预生成所有随机需求(线程安全)
|
var allDemands = GenerateAllDemands(_nodeCount, _totalIterations);
|
|
var parallelOptions = new ParallelOptions
|
{
|
MaxDegreeOfParallelism = maxDegreeOfParallelism,
|
TaskScheduler = TaskScheduler.Default
|
};
|
|
|
|
long currentTime = 0; double pressure = 0;
|
//Parallel.For(0, _totalIterations, parallelOptions, iter =>
|
//{
|
using (var helper = new Yw.Epanet.InteropXHelper())
|
{
|
|
// 初始化获取节点数量
|
var err = helper.Open(_inpFilePath, "", "");
|
CheckError((int)err);
|
err = helper.OpenH();
|
CheckError((int)err);
|
|
for (int i = 0; i < 20000; i++)
|
{
|
helper.InitH(false);
|
|
for (int nodeIdx = 1; nodeIdx <= _nodeCount; nodeIdx++)
|
{
|
double demand = allDemands[i, nodeIdx - 1];
|
err = helper.SetNodeValue(nodeIdx, Yw.Epanet.eNodeProperty.BaseDemand, demand);
|
CheckError((int)err);
|
}
|
|
helper.RunH(out long t);
|
|
var result = new SimulationResult();
|
for (int nodeIdx = 1; nodeIdx <= _nodeCount; nodeIdx++)
|
{
|
pressure = 0;
|
helper.GetNodeValue(nodeIdx, Yw.Epanet.eNodeProperty.Pressure, out pressure);
|
result.UpdateMaxPressure(nodeIdx, pressure);
|
}
|
helper.NextH(out long tstep);
|
_results.Add(result);
|
}
|
|
|
|
helper.Close();
|
|
}
|
|
|
//});
|
|
return _results.ToList();
|
}
|
|
private double[,] GenerateAllDemands(int nodeCount, int totalIterations)
|
{
|
double[,] demands = new double[totalIterations, nodeCount];
|
Parallel.For(0, totalIterations, iter =>
|
{
|
var rnd = new Random(Guid.NewGuid().GetHashCode());
|
for (int node = 0; node < nodeCount; node++)
|
{
|
demands[iter, node] = _minDemand + (_maxDemand - _minDemand) * rnd.NextDouble();
|
}
|
});
|
return demands;
|
}
|
|
|
private void CheckError(int errorCode)
|
{
|
if (errorCode != 0)
|
throw new EpanetException($"EPANET error {errorCode}");
|
}
|
|
public void Dispose() => EN_close();
|
|
public class SimulationResult
|
{
|
public ConcurrentDictionary<int, double> MaxPressures { get; } = new();
|
|
public void UpdateMaxPressure(int nodeId, double pressure)
|
{
|
MaxPressures.AddOrUpdate(nodeId, pressure,
|
(id, old) => Math.Max(old, pressure));
|
}
|
}
|
}
|
|
public class EpanetException : Exception
|
{
|
public EpanetException(string message) : base(message) { }
|
}
|
}
|