ClassIdentifier_1.py_bak 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755
  1. import numpy as np
  2. from pandas import DataFrame
  3. from service.plt_service import get_base_wind_and_power
  4. from utils.file.trans_methods import read_file_to_df
  5. class ClassIdentifier(object):
  6. def __init__(self, wind_turbine_number, file_path: str = None, origin_df: DataFrame = None, index='time_stamp',
  7. wind_velocity='wind_velocity',
  8. active_power='active_power'):
  9. """
  10. :param wind_turbine_number: The wind turbine number.
  11. :param file_path: The file path of the input data.
  12. :param origin_df: The pandas DataFrame containing the input data.
  13. :param index: 索引字段
  14. :param wind_velocity: 风速字段
  15. :param active_power: 有功功率字段
  16. """
  17. self.wind_turbine_number = wind_turbine_number
  18. self.index = index
  19. self.wind_velocity = wind_velocity
  20. self.active_power = active_power
  21. self.rated_wind_speed = 'rated_wind_speed'
  22. self.rated_capacity = 'rated_capacity'
  23. if file_path is None and origin_df is None:
  24. raise ValueError("Either file_path or origin_df should be provided.")
  25. if file_path:
  26. self.df = read_file_to_df(file_path)
  27. else:
  28. self.df = origin_df
  29. self.df = self.df.set_index(keys=self.index)
  30. def identifier(self):
  31. # 风速 和 有功功率 df
  32. wind_and_power_df = self.df[[self.wind_velocity, self.active_power]]
  33. wind_and_power_df.reset_index(inplace=True)
  34. wind_and_power_df_count = wind_and_power_df.shape[0]
  35. PowerMax = wind_and_power_df[self.active_power].max()
  36. PowerRated = np.ceil(PowerMax / 100) * 100
  37. PRated = 1500 # 额定功率1500kw,可改为2000kw
  38. VCutOut = 25
  39. VCutIn = 3
  40. VRated = 10
  41. # 网格法确定风速风向分区数量,功率方向分区数量,
  42. # PNum = (PRated+100)/25 #功率分区间隔25kW
  43. PNum = int(np.ceil(PowerRated / 25)) # 功率分区间隔25kW
  44. VNum = int(np.ceil(VCutOut / 0.25)) # 风速分区间隔0.25m/s
  45. # 实发电量
  46. EPActualTotal = 0 # 实发电量
  47. for i in range(wind_and_power_df_count):
  48. if wind_and_power_df.loc[i, self.active_power] >= 0:
  49. EPActualTotal = EPActualTotal + wind_and_power_df.loc[i, self.active_power] / 6
  50. print("EPActualTotal", EPActualTotal)
  51. # 平均风速
  52. WindSpeedAvr = 0
  53. WindSum = 0
  54. for i in range(wind_and_power_df_count):
  55. if wind_and_power_df.loc[i, self.wind_velocity] >= 0:
  56. WindSum = WindSum + wind_and_power_df.loc[i, self.wind_velocity]
  57. WindSpeedAvr = WindSum / wind_and_power_df_count
  58. print("windSpeedAvr", WindSpeedAvr)
  59. # 用于计算损失电量的标杆功率曲线,可更换为风机设计功率曲线
  60. # base_wind_and_power_df = get_base_wind_and_power(self.wind_turbine_number)
  61. base_wind_and_power_df = read_file_to_df(r"D:\中能智能\matlib计算相关\好点坏点matlib计算\A型风机设计功率曲线.csv", header=None)
  62. base_wind_and_power_df.columns = [self.rated_wind_speed, self.rated_capacity]
  63. if base_wind_and_power_df.empty:
  64. raise ValueError("风场编号:" + self.wind_turbine_number + "未查询到风速功率信息")
  65. base_wind_and_power_count = base_wind_and_power_df.shape[0]
  66. # 风机可利用率,计算方法:大于切入风速但发电功率小于0
  67. TurbineRunRate = 0
  68. nShouldGP = 0
  69. nRealGP = 0
  70. for i in range(wind_and_power_df_count):
  71. if wind_and_power_df.loc[i, self.wind_velocity] >= VCutIn:
  72. nShouldGP = nShouldGP + 1
  73. if wind_and_power_df.loc[i, self.active_power] > 0:
  74. nRealGP = nRealGP + 1
  75. if nShouldGP > 0:
  76. TurbineRunRate = nRealGP / nShouldGP * 100
  77. print("disp(TurbineRunRate)", TurbineRunRate)
  78. # 理论电量-
  79. EPIdealTotalAAA = 0 # 理论电量-
  80. nWhichBin = 0
  81. IdealPower = 0
  82. for i in range(wind_and_power_df_count):
  83. # 应发电量-理论
  84. nWhichBin = 0
  85. for m in range(base_wind_and_power_count - 1):
  86. if base_wind_and_power_df.loc[m, self.rated_wind_speed] < wind_and_power_df.loc[
  87. i, self.wind_velocity] <= \
  88. base_wind_and_power_df.loc[m + 1, self.rated_wind_speed]:
  89. nWhichBin = m
  90. break
  91. # 插值计算对应设计功率
  92. if nWhichBin > base_wind_and_power_count - 1 or nWhichBin == 0:
  93. continue
  94. IdealPower = (wind_and_power_df.loc[i, self.wind_velocity] - base_wind_and_power_df.loc[nWhichBin,
  95. self.rated_wind_speed]) / (
  96. base_wind_and_power_df.loc[nWhichBin + 1, self.rated_wind_speed] -
  97. base_wind_and_power_df.loc[nWhichBin, self.rated_wind_speed]) * (
  98. base_wind_and_power_df.loc[nWhichBin + 1, self.rated_capacity] -
  99. base_wind_and_power_df.loc[nWhichBin, self.rated_capacity]) \
  100. + base_wind_and_power_df.loc[nWhichBin, self.rated_capacity]
  101. EPIdealTotalAAA = EPIdealTotalAAA + IdealPower / 6
  102. print('EPIdealTotalAAA', EPIdealTotalAAA)
  103. #
  104. # 存储功率大于零的运行数据
  105. DzMarch809 = np.zeros([wind_and_power_df_count, 2], dtype=float)
  106. nCounter1 = 0
  107. for i in range(wind_and_power_df_count):
  108. if wind_and_power_df.loc[i, self.active_power] > 0:
  109. DzMarch809[nCounter1, 0] = wind_and_power_df.loc[i, self.wind_velocity]
  110. DzMarch809[nCounter1, 1] = wind_and_power_df.loc[i, self.active_power]
  111. nCounter1 = nCounter1 + 1
  112. print('nCounter1', nCounter1)
  113. # 统计各网格落入的散点个数
  114. XBoxNumber = np.ones([PNum, VNum], dtype=int)
  115. nWhichP = -1
  116. nWhichV = -1
  117. for i in range(nCounter1):
  118. for m in range(PNum):
  119. if m * 25 < DzMarch809[i, 1] <= (m + 1) * 25:
  120. nWhichP = m
  121. break
  122. for n in range(VNum):
  123. if ((n + 1) * 0.25 - 0.125) < DzMarch809[i, 0] <= ((n + 1) * 0.25 + 0.125):
  124. nWhichV = n
  125. break
  126. if nWhichP > -1 and nWhichV > -1:
  127. XBoxNumber[nWhichP, nWhichV] = XBoxNumber[nWhichP, nWhichV] + 1
  128. for m in range(PNum):
  129. for n in range(VNum):
  130. XBoxNumber[m, n] = XBoxNumber[m, n] - 1
  131. print('XBoxNumber', XBoxNumber)
  132. # 在功率方向将网格内散点绝对个数转换为相对百分比,备用
  133. PBoxPercent = np.zeros([PNum, VNum], dtype=float)
  134. PBinSum = np.zeros(PNum, dtype=int)
  135. for i in range(PNum):
  136. for m in range(VNum):
  137. PBinSum[i] = PBinSum[i] + XBoxNumber[i, m]
  138. for m in range(VNum):
  139. if PBinSum[i] > 0:
  140. PBoxPercent[i, m] = XBoxNumber[i, m] / PBinSum[i] * 100
  141. # 在风速方向将网格内散点绝对个数转换为相对百分比,备用
  142. VBoxPercent = np.zeros([PNum, VNum], dtype=float)
  143. VBinSum = np.zeros(VNum, dtype=int)
  144. for i in range(VNum):
  145. for m in range(PNum):
  146. VBinSum[i] = VBinSum[i] + XBoxNumber[m, i]
  147. for m in range(PNum):
  148. if VBinSum[i] > 0:
  149. VBoxPercent[m, i] = XBoxNumber[m, i] / VBinSum[i] * 100
  150. # 以水平功率带方向为准,分析每个水平功率带中,功率主带中心,即找百分比最大的网格位置。
  151. PBoxMaxIndex = np.zeros(PNum, dtype=int) # 水平功率带最大网格位置索引
  152. PBoxMaxP = np.zeros(PNum, dtype=int) # 水平功率带最大网格百分比
  153. for m in range(PNum):
  154. # 确定每一水平功率带的最大网格位置索引即百分比值
  155. PBoxMaxP[m], PBoxMaxIndex[m] = PBoxPercent[m, :].max(), PBoxPercent[m, :].argmax()
  156. # 以垂直风速方向为准,分析每个垂直风速带中,功率主带中心,即找百分比最大的网格位置。
  157. VBoxMaxIndex = np.zeros(VNum, dtype=int)
  158. VBoxMaxV = np.zeros(VNum, dtype=int)
  159. for m in range(VNum):
  160. [VBoxMaxV[m], VBoxMaxIndex[m]] = VBoxPercent[:, m].max(), VBoxPercent[:, m].argmax()
  161. # 切入风速特殊处理,如果切入风速过于偏右,向左拉回
  162. if PBoxMaxIndex[0] > 14:
  163. PBoxMaxIndex[0] = 9
  164. # 以水平功率带方向为基准,进行分析
  165. DotDense = np.zeros(PNum, dtype=int) # 每一水平功率带的功率主带包含的网格数
  166. DotDenseLeftRight = np.zeros([PNum, 2], dtype=int) # 存储每一水平功率带的功率主带以最大网格为中心,向向左,向右扩展的网格数
  167. DotValve = 90 # 从中心向左右对称扩展网格的散点百分比和的阈值。
  168. PDotDenseSum = 0
  169. iSpreadLeft = 1 # 向左扩展网格计数,初值为1
  170. iSpreadRight = 1 # 向右扩展网格技术,初值为1
  171. for i in range(PNum - 6): # 从最下层水平功率带1开始,向上到第PNum-6个水平功率带(额定功率一下水平功率带),逐一分析
  172. PDotDenseSum = PBoxMaxP[i] # 以中心最大水平功率带为基准,向左向右对称扩展网格,累加各网格散点百分比
  173. iSpreadRight = 1
  174. iSpreadLeft = 1
  175. while PDotDenseSum < DotValve:
  176. if (PBoxMaxIndex[i] + iSpreadRight) < VNum - 1:
  177. PDotDenseSum = PDotDenseSum + PBoxPercent[i, PBoxMaxIndex[i] + iSpreadRight] # 向右侧扩展
  178. iSpreadRight = iSpreadRight + 1
  179. if (PBoxMaxIndex[i] + iSpreadRight) > VNum - 1:
  180. break
  181. if (PBoxMaxIndex[i] - iSpreadLeft) > 0:
  182. PDotDenseSum = PDotDenseSum + PBoxPercent[i, PBoxMaxIndex[i] - iSpreadLeft] # 向左侧扩展
  183. iSpreadLeft = iSpreadLeft + 1
  184. if (PBoxMaxIndex[i] - iSpreadLeft) <= 0:
  185. break
  186. iSpreadRight = iSpreadRight - 1
  187. iSpreadLeft = iSpreadLeft - 1
  188. # 向左右对称扩展完毕
  189. DotDenseLeftRight[i, 0] = iSpreadLeft
  190. DotDenseLeftRight[i, 1] = iSpreadRight
  191. DotDense[i] = iSpreadLeft + iSpreadRight + 1
  192. # 各行功率主带右侧宽度的中位数最具有代表性
  193. DotDenseWidthLeft = np.zeros([PNum - 6, 1], dtype=int)
  194. for i in range(PNum - 6):
  195. DotDenseWidthLeft[i] = DotDenseLeftRight[i, 1]
  196. MainBandRight = np.median(DotDenseWidthLeft)
  197. # 散点向右显著延展分布的水平功率带为限功率水平带
  198. PowerLimit = np.zeros([PNum, 1], dtype=int) # 各水平功率带是否为限功率标识,==1:是;==0:不是
  199. WidthAverage = 0 # 功率主带平均宽度
  200. WidthVar = 0 # 功率主带方差
  201. # PowerLimitValve = 6 #限功率主带判别阈值
  202. PowerLimitValve = np.ceil(MainBandRight) + 3 # 限功率主带判别阈值
  203. nCounterLimit = 0
  204. nCounter = 0
  205. for i in range(PNum - 6):
  206. if DotDenseLeftRight[i, 1] > PowerLimitValve and PBinSum[i] > 20: # 如果向右扩展网格数大于阈值,且该水平功率带点总数>20,是
  207. PowerLimit[i] = 1
  208. nCounterLimit = nCounterLimit + 1
  209. if DotDenseLeftRight[i, 1] <= PowerLimitValve:
  210. WidthAverage = WidthAverage + DotDenseLeftRight[i, 1] # 统计正常水平功率带右侧宽度
  211. nCounter = nCounter + 1
  212. WidthAverage = WidthAverage / nCounter # 功率主带平均宽度
  213. print("WidthAverage", WidthAverage)
  214. # 各水平功率带的功率主带宽度的方差,反映从下到上宽度是否一致,或是否下宽上窄等异常情况
  215. for i in range(PNum - 6):
  216. if DotDenseLeftRight[i, 1] <= PowerLimitValve:
  217. WidthVar = WidthVar + (DotDenseLeftRight[i, 1] - WidthAverage) * (
  218. DotDenseLeftRight[i, 1] - WidthAverage)
  219. WidthVar = np.sqrt(WidthVar / nCounter)
  220. # 各水平功率带,功率主带的风速范围,右侧扩展网格数*2*0.25
  221. PowerBandWidth = WidthAverage * 2 * 0.25
  222. # 对限负荷水平功率带的最大网格较下面相邻层显著偏右,拉回
  223. for i in range(1, PNum - 6):
  224. if PowerLimit[i] == 1 and abs(PBoxMaxIndex[i] - PBoxMaxIndex[i - 1]) > 5:
  225. PBoxMaxIndex[i] = PBoxMaxIndex[i - 1] + 1
  226. # 输出各层功率主带的左右边界网格索引
  227. DotDenseInverse = np.zeros([PNum, 2], dtype=int)
  228. for i in range(PNum):
  229. DotDenseInverse[i, :] = DotDenseLeftRight[PNum - i - 1, :]
  230. # print('DotDenseInverse', DotDenseInverse)
  231. # 功率主带的右边界
  232. CurveWidthR = int(np.ceil(WidthAverage) + 2)
  233. # CurveWidthL = 6 #功率主带的左边界
  234. CurveWidthL = CurveWidthR
  235. BBoxLimit = np.zeros([PNum, VNum], dtype=int) # 网格是否为限功率网格的标识,如果为限功率水平功率带,从功率主带右侧边缘向右的网格为限功率网格
  236. for i in range(2, PNum - 6):
  237. if PowerLimit[i] == 1:
  238. for j in range(PBoxMaxIndex[i] + CurveWidthR, VNum):
  239. BBoxLimit[i, j] = 1
  240. BBoxRemove = np.zeros([PNum, VNum], dtype=int) # 数据异常需要剔除的网格标识,标识==1:功率主带右侧的欠发网格;==2:功率主带左侧的超发网格
  241. for m in range(PNum - 6):
  242. for n in range(PBoxMaxIndex[m] + CurveWidthR - 1, VNum):
  243. BBoxRemove[m, n] = 1
  244. for n in range(PBoxMaxIndex[m] - CurveWidthL - 1, 0, -1):
  245. BBoxRemove[m, n] = 2
  246. # 确定功率主带的左上拐点,即额定风速位置的网格索引
  247. CurveTop = np.zeros(2, dtype=int)
  248. CurveTopValve = 3 # 网格的百分比阈值
  249. BTopFind = 0
  250. for m in range(PNum - 4 - 1, 0, -1):
  251. for n in range(VNum):
  252. if VBoxPercent[m, n] > CurveTopValve and XBoxNumber[m, n] >= 10: # 如左上角网格的百分比和散点个数大于阈值。
  253. CurveTop[0] = m
  254. CurveTop[1] = n
  255. BTopFind = 1
  256. break
  257. if BTopFind == 1:
  258. break
  259. IsolateValve = 3
  260. for m in range(PNum - 6):
  261. for n in range(PBoxMaxIndex[m] + CurveWidthR - 1, VNum):
  262. if PBoxPercent[m, n] < IsolateValve:
  263. BBoxRemove[m, n] = 1
  264. # 功率主带顶部宽度
  265. CurveWidthT = 2
  266. for m in range(PNum - CurveWidthT - 1, PNum):
  267. for n in range(VNum):
  268. BBoxRemove[m, n] = 3 # 网格为额定功率以上的超发点
  269. # 功率主带拐点左侧的欠发网格标识
  270. for m in range(PNum - 5 - 1, PNum):
  271. for n in range(CurveTop[1] - 2 - 1):
  272. BBoxRemove[m, n] = 2
  273. # 以网格的标识,决定该网格内数据的标识。Dzwind_and_power_dfSel功率非零数据的标识位。散点在哪个网格,此网格的标识即为该点的标识
  274. Dzwind_and_power_dfSel = np.zeros(nCounter1, dtype=int) # is ==1,欠发功率点;==2,超发功率点;==3,额定风速以上的超发功率点 ==4, 限电
  275. nWhichP = 0
  276. nWhichV = 0
  277. nBadA = 0
  278. for i in range(nCounter1):
  279. for m in range(PNum):
  280. if DzMarch809[i, 1] > (m - 1) * 25 and DzMarch809[i, 1] <= m * 25:
  281. nWhichP = m
  282. break
  283. for n in range(VNum):
  284. if DzMarch809[i, 0] > (n * 0.25 - 0.125) and DzMarch809[i, 0] <= (n * 0.25 + 0.125):
  285. nWhichV = n
  286. break
  287. if nWhichP > 0 and nWhichV > 0:
  288. if BBoxRemove[nWhichP, nWhichV] == 1:
  289. Dzwind_and_power_dfSel[i] = 1
  290. nBadA = nBadA + 1
  291. if BBoxRemove[nWhichP, nWhichV] == 2:
  292. Dzwind_and_power_dfSel[i] = 2
  293. if BBoxRemove[nWhichP, nWhichV] == 3:
  294. Dzwind_and_power_dfSel[i] = 0 # 3 # 额定风速以上的超发功率点认为是正常点,不再标识。
  295. if BBoxLimit[nWhichP, nWhichV] == 1 and nWhichP>16:
  296. Dzwind_and_power_dfSel[i] = 4
  297. print("nWhichP", nWhichP)
  298. print("nWhichV", nWhichV)
  299. print("nBadA", nBadA)
  300. # 限负荷数据标识方法2:把数据切割为若干个窗口。对每一窗口,以第一个点为基准,连续nWindowLength个数据的功率在方差范围内,呈现显著水平分布的点
  301. PVLimit = np.zeros([nCounter1, 2], dtype=int) # 存储限负荷数据
  302. nLimitTotal = 0
  303. nWindowLength = 3
  304. LimitWindow = np.zeros(nWindowLength, dtype=int)
  305. UpLimit = 0 # 上限
  306. LowLimit = 0 # 下限
  307. PowerStd = 15 # 功率波动方差
  308. bAllInUpLow = 1 # ==1:窗口内所有数据均在方差上下限之内,限负荷==0,不满足条件
  309. bAllInAreas = 1 # ==1:窗口所有数据均在200~PRated-300kW范围内;==0:不满足此条件
  310. nWindowNum = int(np.floor(nCounter1 / nWindowLength))
  311. PowerLimitUp = PRated - 300
  312. PowerLimitLow = 200
  313. for i in range(nWindowNum):
  314. for j in range(nWindowLength):
  315. LimitWindow[j] = DzMarch809[i * nWindowLength + j, 1]
  316. bAllInAreas = 1
  317. for j in range(nWindowLength):
  318. if LimitWindow[j] < PowerLimitLow or LimitWindow[j] > PowerLimitUp:
  319. bAllInAreas = 0
  320. if bAllInAreas == 0:
  321. continue
  322. UpLimit = LimitWindow[0] + PowerStd
  323. LowLimit = LimitWindow[0] - PowerStd
  324. bAllInUpLow = 1
  325. for j in range(1, nWindowLength):
  326. if LimitWindow[j] < LowLimit or LimitWindow[j] > UpLimit:
  327. bAllInUpLow = 0
  328. if bAllInUpLow == 1:
  329. for j in range(nWindowLength):
  330. Dzwind_and_power_dfSel[i * nWindowLength + j] = 4 # 标识窗口内的数据为限负荷数据
  331. for j in range(nWindowLength):
  332. PVLimit[nLimitTotal, :] = DzMarch809[i * nWindowLength + j, :]
  333. nLimitTotal = nLimitTotal + 1
  334. print("nLimitTotal", nLimitTotal)
  335. # 相邻水平功率主带的锯齿平滑
  336. PVLeftDown = np.zeros(2, dtype=int)
  337. PVRightUp = np.zeros(2, dtype=int)
  338. nSmooth = 0
  339. for i in range(PNum - 6 - 1):
  340. PVLeftDown = np.zeros(2, dtype=int)
  341. PVRightUp = np.zeros(2, dtype=int)
  342. if (PBoxMaxIndex[i + 1] - PBoxMaxIndex[i]) >= 1:
  343. PVLeftDown[0] = (PBoxMaxIndex[i] + CurveWidthR) * 0.25 - 0.125
  344. PVLeftDown[1] = (i - 1) * 25
  345. PVRightUp[0] = (PBoxMaxIndex[i + 1] + CurveWidthR) * 0.25 - 0.125
  346. PVRightUp[1] = (i + 1 - 1) * 25
  347. for m in range(nCounter1):
  348. if DzMarch809[m, 0] > PVLeftDown[0] and DzMarch809[m, 0] < PVRightUp[0] and PVLeftDown[1] < \
  349. DzMarch809[m, 1] < PVRightUp[1]: # 在该锯齿中
  350. if (DzMarch809[m, 1] - PVLeftDown[1]) / (DzMarch809[m, 0] - PVLeftDown[0]) > (
  351. PVRightUp[1] - PVLeftDown[1]) / (
  352. PVRightUp[0] - PVLeftDown[0]): # 斜率大于对角连线,则在锯齿左上三角形中,选中
  353. Dzwind_and_power_dfSel[m] = 0
  354. nSmooth = nSmooth + 1
  355. print("nSmooth", nSmooth)
  356. # 存储好点
  357. nCounterPV = 0
  358. PVDot = np.zeros([nCounter1, 2], dtype=int)
  359. for i in range(nCounter1):
  360. if Dzwind_and_power_dfSel[i] == 0:
  361. PVDot[nCounterPV, :] = DzMarch809[i, :]
  362. nCounterPV = nCounterPV + 1
  363. nCounterVP = nCounterPV
  364. print("nCounterVP", nCounterVP)
  365. # 存储坏点
  366. nCounterBad = 0
  367. PVBad = np.zeros([nCounter1, 2], dtype=int)
  368. for i in range(nCounter1):
  369. if Dzwind_and_power_dfSel[i] == 1 or Dzwind_and_power_dfSel[i] == 2 or Dzwind_and_power_dfSel[i] == 3:
  370. PVBad[nCounterBad, :] = DzMarch809[i, :]
  371. nCounterBad = nCounterBad + 1
  372. print("nCounterBad", nCounterBad)
  373. # 用功率主带中的好点绘制实测功率曲
  374. XBinNumber = np.ones(50, dtype=int)
  375. PCurve = np.zeros([50, 2], dtype=int)
  376. PCurve[:, 0] = [i / 2 for i in range(1, 51)]
  377. XBinSum = np.zeros([50, 2], dtype=int)
  378. nWhichBin = 0
  379. for i in range(nCounterVP):
  380. nWhichBin = 0
  381. for b in range(50):
  382. if PVDot[i, 0] > (b * 0.5 - 0.25) and PVDot[i, 0] <= (b * 0.5 + 0.25):
  383. nWhichBin = b
  384. break
  385. if nWhichBin > 0:
  386. XBinSum[nWhichBin, 0] = XBinSum[nWhichBin, 0] + PVDot[i, 0] # wind speed
  387. XBinSum[nWhichBin, 1] = XBinSum[nWhichBin, 1] + PVDot[i, 1] # Power
  388. XBinNumber[nWhichBin] = XBinNumber[nWhichBin] + 1
  389. for b in range(50):
  390. XBinNumber[b] = XBinNumber[b] - 1
  391. for b in range(50):
  392. if XBinNumber[b] > 0:
  393. PCurve[b, 0] = XBinSum[b, 0] / XBinNumber[b]
  394. PCurve[b, 1] = XBinSum[b, 1] / XBinNumber[b]
  395. # 对额定风速以上的功率直接赋额定功率
  396. VRatedNum = int(VRated / 0.5)
  397. for m in range(VRatedNum, 50):
  398. if PCurve[m, 1] == 0:
  399. PCurve[m, 1] = PRated
  400. # print("PCurve", PCurve)
  401. # 绘制标准正则功率曲线,以0.5m/s标准为间隔
  402. # 15m/s以上为额定功率,15m/s以下为计算得到
  403. PCurveNorm = np.zeros([50, 2], dtype=int)
  404. for i in range(30, 50):
  405. PCurveNorm[i, 0] = i * 0.5
  406. PCurveNorm[i, 1] = PRated
  407. # 15m/s一下正则功率曲线
  408. CurveData = np.zeros([30, 2], dtype=int)
  409. for i in range(30):
  410. CurveData[i, :] = PCurve[i, :]
  411. CurveNorm = np.zeros([30, 2], dtype=int)
  412. VSpeed = [i / 2 for i in range(1, 31)]
  413. WhichBin = 0
  414. K = 0
  415. a = 0
  416. for m in range(30):
  417. K = 0
  418. a = 0
  419. for n in range(30):
  420. if abs(CurveData[n, 0] - VSpeed[m]) < 0.1:
  421. WhichBin = n
  422. break
  423. if WhichBin > 1:
  424. if CurveData[WhichBin, 0] - CurveData[WhichBin - 1, 0] > 0:
  425. K = (CurveData[WhichBin, 1] - CurveData[WhichBin - 1, 1]) / (
  426. CurveData[WhichBin, 0] - CurveData[WhichBin - 1, 0])
  427. a = CurveData[WhichBin, 1] - K * CurveData[WhichBin, 0]
  428. CurveNorm[m, 0] = VSpeed[m]
  429. CurveNorm[m, 1] = a + K * VSpeed[m]
  430. for i in range(30):
  431. PCurveNorm[i, :] = CurveNorm[i, :]
  432. # 子模块3:损失电量计算及发电性能评价
  433. CC = len(PCurve[:, 0])
  434. EPIdealTotal = 0
  435. # 计算停机损失
  436. EPLostStopTotal = 0
  437. EPLost = 0
  438. nWhichBin = 0
  439. IdealPower = 0
  440. nStopTotal = 0
  441. for i in range(wind_and_power_df_count):
  442. if wind_and_power_df.loc[i, self.active_power] <= 0:
  443. nWhichBin = 0
  444. for m in range(base_wind_and_power_count - 1):
  445. if wind_and_power_df.loc[i, self.wind_velocity] > base_wind_and_power_df.loc[
  446. m, self.rated_wind_speed] and wind_and_power_df.loc[i, self.wind_velocity] <= \
  447. base_wind_and_power_df.loc[
  448. m + 1, self.rated_wind_speed]:
  449. nWhichBin = m
  450. break
  451. if nWhichBin > base_wind_and_power_count - 1 or nWhichBin == 0:
  452. continue
  453. IdealPower = (wind_and_power_df.loc[i, self.wind_velocity] - base_wind_and_power_df.loc[
  454. nWhichBin, self.rated_wind_speed]) / (
  455. base_wind_and_power_df.loc[nWhichBin + 1, self.rated_wind_speed] -
  456. base_wind_and_power_df.loc[
  457. nWhichBin, self.rated_wind_speed]) * (
  458. base_wind_and_power_df.loc[nWhichBin + 1, self.rated_capacity]
  459. - base_wind_and_power_df.loc[nWhichBin, self.rated_capacity]) \
  460. + base_wind_and_power_df.loc[nWhichBin, self.rated_capacity]
  461. EPLost = IdealPower / 6
  462. EPLostStopTotal = EPLostStopTotal + EPLost
  463. nStopTotal = nStopTotal + 1
  464. print("EPLost", EPLost)
  465. print("nStopTotal", nStopTotal)
  466. print("EPLostStopTotal", EPLostStopTotal)
  467. nWhichP = 0
  468. nWhichV = 0
  469. nWhichBin = 0
  470. IdealPower = 0
  471. # 计算欠发损失,此欠发损失已不包括限电损失,限电点在前面已经从欠发点中去除。
  472. EPLostBadTotal = 0
  473. EPLost = 0
  474. nBadTotal = 0
  475. LostBadPercent = 0
  476. EPOverTotal = 0
  477. EPOver = 0
  478. nOverTotal = 0
  479. for i in range(nCounter1):
  480. if Dzwind_and_power_dfSel[i] == 1:
  481. nWhichBin = 0
  482. for m in range(base_wind_and_power_count - 1):
  483. if DzMarch809[i, 0] > base_wind_and_power_df.loc[m, self.rated_wind_speed] \
  484. and DzMarch809[i, 0] <= base_wind_and_power_df.loc[m + 1, self.rated_wind_speed]:
  485. nWhichBin = m
  486. break
  487. if nWhichBin > base_wind_and_power_count - 1 or nWhichBin == 0:
  488. continue
  489. IdealPower = (DzMarch809[i, 0] - base_wind_and_power_df.loc[nWhichBin, self.rated_wind_speed]) / (
  490. base_wind_and_power_df.loc[nWhichBin + 1, self.rated_wind_speed] - base_wind_and_power_df.loc[
  491. nWhichBin, self.rated_wind_speed]) * (
  492. base_wind_and_power_df.loc[nWhichBin + 1, self.rated_capacity] -
  493. base_wind_and_power_df.loc[nWhichBin, self.rated_capacity]) + \
  494. base_wind_and_power_df.loc[nWhichBin, self.rated_capacity]
  495. EPLost = abs(IdealPower - DzMarch809[i, 1]) / 6
  496. EPLostBadTotal = EPLostBadTotal + EPLost
  497. nBadTotal = nBadTotal + 1
  498. # 额定风速以上超发电量
  499. if Dzwind_and_power_dfSel[i] == 3:
  500. EPOver = (DzMarch809[i, 1] - PRated) / 6
  501. EPOverTotal = EPOverTotal + EPOver
  502. nOverTotal = nOverTotal + 1
  503. print("EPLost", EPLost)
  504. print("nBadTotal", nBadTotal)
  505. print("EPLostBadTotal", EPLostBadTotal)
  506. print("EPOverTotal", EPOverTotal)
  507. print("nOverTotal", nOverTotal)
  508. # 功率曲线未达标损失
  509. EPLostPerformTotal = 0
  510. nWhichBinI = 0
  511. IdealPower = 0
  512. for i in range(nCounterVP):
  513. for m in range(base_wind_and_power_count - 1):
  514. if PVDot[i, 0] > base_wind_and_power_df.loc[m, self.rated_wind_speed] and PVDot[i, 0] <= \
  515. base_wind_and_power_df.loc[m + 1, self.rated_wind_speed]:
  516. nWhichBinI = m
  517. break
  518. if nWhichBinI > base_wind_and_power_count - 1 or nWhichBinI == 0:
  519. continue
  520. IdealPower = (PVDot[i, 0] - base_wind_and_power_df.loc[nWhichBinI, self.rated_wind_speed]) / (
  521. base_wind_and_power_df.loc[nWhichBinI + 1, self.rated_wind_speed] - base_wind_and_power_df.loc[
  522. nWhichBinI, self.rated_wind_speed]) * \
  523. (base_wind_and_power_df.loc[nWhichBinI + 1, self.rated_capacity] -
  524. base_wind_and_power_df.loc[nWhichBinI, self.rated_capacity]) + \
  525. base_wind_and_power_df.loc[nWhichBinI, self.rated_capacity]
  526. EPLostPerformTotal = EPLostPerformTotal + (IdealPower - PVDot[i, 1]) / 6
  527. print("EPLostPerformTotal", EPLostPerformTotal)
  528. # 限电损失
  529. EPLostLimitTotal = 0
  530. EPLost = 0
  531. nLimitTotal = 0
  532. PVLimit = np.zeros([nCounter1, 2])
  533. for i in range(nCounter1):
  534. if Dzwind_and_power_dfSel[i] == 4:
  535. nWhichBin = 0
  536. for m in range(base_wind_and_power_count - 1):
  537. if DzMarch809[i, 0] > base_wind_and_power_df.loc[m, self.rated_wind_speed] and DzMarch809[i, 0] <= \
  538. base_wind_and_power_df.loc[m + 1, self.rated_wind_speed]:
  539. nWhichBin = m
  540. break
  541. # 插值计算对应设计功率
  542. if nWhichBin > base_wind_and_power_count - 1 or nWhichBin == 0:
  543. continue
  544. IdealPower = (DzMarch809[i, 0] - base_wind_and_power_df.loc[nWhichBin, self.rated_wind_speed]) / (
  545. base_wind_and_power_df.loc[nWhichBin + 1, self.rated_wind_speed] -
  546. base_wind_and_power_df.loc[nWhichBin, self.rated_wind_speed]) * (
  547. base_wind_and_power_df.loc[nWhichBin + 1, self.rated_capacity] -
  548. base_wind_and_power_df.loc[nWhichBin, self.rated_capacity]) + \
  549. base_wind_and_power_df.loc[nWhichBin, self.rated_capacity]
  550. EPLost = np.abs(IdealPower - DzMarch809[i, 1]) / 6
  551. EPLostLimitTotal = EPLostLimitTotal + EPLost
  552. PVLimit[nLimitTotal, :] = DzMarch809[i, :]
  553. nLimitTotal = nLimitTotal + 1
  554. nLimitTotal = nLimitTotal - 1
  555. print("nLimitTotal", nLimitTotal)
  556. # 欠发和限点损失总和
  557. EPLostBadLimitTotal = EPLostBadTotal + EPLostLimitTotal
  558. # 如果功率曲线未达标损失为正
  559. if EPLostPerformTotal >= 0:
  560. EPIdealTotal = EPActualTotal + EPLostStopTotal + EPLostLimitTotal + EPLostBadTotal + EPLostPerformTotal
  561. # 如果功率曲线未达标损失为负
  562. if EPLostPerformTotal < 0:
  563. EPIdealTotal = EPActualTotal + EPLostStopTotal + EPLostLimitTotal + EPLostBadTotal
  564. print("EPIdealTotal", EPIdealTotal)
  565. # 可以比较求和得到的应发功率EPIdealTotal与理论计算得到的应发功率EPIdealTotalAAA的差别
  566. # 需要去除的超发功率:(1)功率主带左侧的超发点;(2)额定风速以上的超发点。
  567. RemoveOverEP = 0
  568. nType2 = 0
  569. for i in range(nCounter1):
  570. if Dzwind_and_power_dfSel[i] == 2: # 功率主带左侧的超发坏点
  571. nWhichBin = 0
  572. for m in range(base_wind_and_power_count - 1):
  573. if base_wind_and_power_df.loc[m, self.rated_wind_speed] < DzMarch809[i, 0] <= base_wind_and_power_df.loc[m + 1, self.rated_wind_speed]:
  574. nWhichBin = m
  575. break
  576. if nWhichBin > base_wind_and_power_count - 1 or nWhichBin == 0:
  577. continue
  578. IdealPower = (DzMarch809[i, 0] - base_wind_and_power_df.loc[nWhichBin, self.rated_wind_speed]) / (
  579. base_wind_and_power_df.loc[nWhichBin + 1, self.rated_wind_speed] - base_wind_and_power_df.loc[
  580. nWhichBin, self.rated_wind_speed]) * (
  581. base_wind_and_power_df.loc[nWhichBin + 1, self.rated_capacity] -
  582. base_wind_and_power_df.loc[nWhichBin, self.rated_capacity]) + \
  583. base_wind_and_power_df.loc[nWhichBin, self.rated_capacity]
  584. RemoveOverEP = RemoveOverEP + (DzMarch809[i, 1] - IdealPower) / 6
  585. nType2 = nType2 + 1
  586. print("RemoveOverEP", RemoveOverEP)
  587. print("nType2", nType2)
  588. # 额定功率以上的超发点
  589. nTypeOver = 0
  590. for i in range(nCounter1):
  591. if DzMarch809[i, 1] > PRated:
  592. RemoveOverEP = RemoveOverEP + (DzMarch809[i, 1] - PRated) / 6
  593. nTypeOver = nTypeOver + 1
  594. print("RemoveOverEP", RemoveOverEP)
  595. print("nTypeOver", nTypeOver)
  596. def run(self):
  597. # Implement your class identification logic here
  598. self.identifier()
  599. if __name__ == '__main__':
  600. test = ClassIdentifier('test', r"D:\中能智能\matlib计算相关\好点坏点matlib计算\A01.csv", index='时间',
  601. wind_velocity='风速',
  602. active_power='功率')
  603. test.run()