ningshuxia
2025-03-12 2596928b751bb5edc6ff0537c3950d0cabaf2018
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
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) { }
    }
}