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 _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 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 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) { } } }