cpWindSpeedAnalyst.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. import os
  2. import math
  3. import pandas as pd
  4. import plotly.graph_objects as go
  5. from algorithmContract.confBusiness import *
  6. from algorithmContract.contract import Contract
  7. from behavior.analystWithGoodPoint import AnalystWithGoodPoint
  8. class CpWindSpeedAnalyst(AnalystWithGoodPoint):
  9. """
  10. 风电机组风能利用系数分析
  11. """
  12. def typeAnalyst(self):
  13. return "cp_windspeed"
  14. def dataReprocess(self, dataFrameTurbines: pd.DataFrame) -> pd.DataFrame:
  15. dataFrame = dataFrameTurbines.groupby([Field_CodeOfTurbine, Field_WindSpeedFloor]).agg(
  16. cp=(Field_Cp, 'median'),
  17. cp_max=(Field_Cp, 'max'),
  18. cp_min=(Field_Cp, 'min'),
  19. ).reset_index()
  20. dataFrame.columns = [Field_CodeOfTurbine, Field_WindSpeedFloor,
  21. Field_Cp, Field_CpMax, Field_CpMin]
  22. dataFrame = dataFrame.sort_values(
  23. by=[Field_CodeOfTurbine, Field_WindSpeedFloor], ascending=[True, True])
  24. return dataFrame
  25. def turbinesAnalysis(self, outputAnalysisDir, conf: Contract, turbineCodes):
  26. dictionary = self.processTurbineData(turbineCodes, conf, [
  27. Field_DeviceCode, Field_Time, Field_WindSpeed, Field_ActiverPower])
  28. dataFrameOfTurbines = self.userDataFrame(
  29. dictionary, conf.dataContract.configAnalysis, self)
  30. # 检查所需列是否存在
  31. required_columns = {Field_WindSpeedFloor, Field_Cp}
  32. if not required_columns.issubset(dataFrameOfTurbines.columns):
  33. raise ValueError(f"DataFrame缺少必要的列。需要的列有: {required_columns}")
  34. turbrineInfos = self.common.getTurbineInfos(
  35. conf.dataContract.dataFilter.powerFarmID, turbineCodes, self.turbineInfo)
  36. groupedOfTurbineModel = turbrineInfos.groupby(Field_MillTypeCode)
  37. returnDatas = []
  38. for turbineModelCode, group in groupedOfTurbineModel:
  39. currTurbineCodes = group[Field_CodeOfTurbine].unique().tolist()
  40. currTurbineModeInfo = self.common.getTurbineModelByCode(
  41. turbineModelCode, self.turbineModelInfo)
  42. currDataFrameOfTurbines = dataFrameOfTurbines[dataFrameOfTurbines[Field_CodeOfTurbine].isin(
  43. currTurbineCodes)]
  44. dataFrame = self.dataReprocess(currDataFrameOfTurbines)
  45. returnData = self.buildChart(
  46. dataFrame, outputAnalysisDir, conf, currTurbineModeInfo)
  47. returnDatas.append(returnData)
  48. returnResult = pd.concat(returnDatas, ignore_index=True)
  49. return returnResult
  50. def buildChart(self, dataFrameOfTurbines: pd.DataFrame, outputAnalysisDir, conf: Contract, turbineModelInfo: pd.Series):
  51. # Create the main Cp distribution plot using Plotly
  52. fig = go.Figure()
  53. # colors = px.colors.sequential.Turbo
  54. # 创建一个列表来存储各个风电机组的数据
  55. turbine_data_list = []
  56. for turbineCode in dataFrameOfTurbines[Field_CodeOfTurbine].unique():
  57. group = dataFrameOfTurbines[dataFrameOfTurbines[Field_CodeOfTurbine] == turbineCode]
  58. currTurbineInfo = self.common.getTurbineInfo(
  59. conf.dataContract.dataFilter.powerFarmID, turbineCode, self.turbineInfo)
  60. fig.add_trace(go.Scatter(x=group[Field_WindSpeedFloor], y=group[Field_Cp],
  61. mode='lines',
  62. # line=dict(color=colors[idx % len(colors)]),
  63. name=currTurbineInfo[Field_NameOfTurbine]))
  64. # 提取数据
  65. turbine_data_total = {
  66. "engineName": currTurbineInfo[Field_NameOfTurbine],
  67. "engineCode": turbineCode,
  68. "xData": group[Field_WindSpeedFloor].tolist(),
  69. # "yData": group[Field_Cp].tolist(),
  70. "yData": [None if math.isinf(val) else val for val in group[Field_Cp].tolist()],
  71. }
  72. turbine_data_list.append(turbine_data_total)
  73. fig.update_layout(title={'text': f'风能利用系数分布-{turbineModelInfo[Field_MachineTypeCode]}', 'x': 0.5},
  74. xaxis_title='风速', yaxis_title='风能利用系数',
  75. legend=dict(
  76. orientation="h", # Horizontal orientation
  77. xanchor="center", # Anchor the legend to the center
  78. x=0.5, # Position legend at the center of the x-axis
  79. y=-0.2, # Position legend below the x-axis
  80. # itemsizing='constant', # Keep the size of the legend entries constant
  81. # itemwidth=50
  82. ),
  83. xaxis=dict(range=[0, 26], tickmode='linear',
  84. dtick=1, tickangle=-45),
  85. yaxis=dict(
  86. dtick=self.axisStepCp,
  87. range=[self.axisLowerLimitCp,
  88. self.axisUpperLimitCp]
  89. )
  90. )
  91. engineTypeCode = turbineModelInfo.get(Field_MillTypeCode, "")
  92. if isinstance(engineTypeCode, pd.Series):
  93. engineTypeCode = engineTypeCode.iloc[0]
  94. engineTypeName = turbineModelInfo.get(Field_MachineTypeCode, "")
  95. if isinstance(engineTypeName, pd.Series):
  96. engineTypeName = engineTypeName.iloc[0]
  97. # 构建最终的JSON对象
  98. json_output = {
  99. "analysisTypeCode": "风电机组风能利用系数分析",
  100. "typecode": turbineModelInfo[Field_MillTypeCode],
  101. "engineCode": engineTypeCode,
  102. "engineTypeName": engineTypeName,
  103. "title": f'风能利用系数分布-{turbineModelInfo[Field_MachineTypeCode]}',
  104. "xaixs": "风速",
  105. "yaixs": "风能利用系数",
  106. "data": turbine_data_list
  107. }
  108. output_json_path = os.path.join(outputAnalysisDir, f"{turbineModelInfo[Field_MillTypeCode]}.json")
  109. with open(output_json_path, 'w', encoding='utf-8') as f:
  110. import json
  111. json.dump(json_output, f, ensure_ascii=False, indent=4)
  112. # 保存HTML
  113. # htmlFileName = f"{self.powerFarmInfo[Field_PowerFarmName].iloc[0]}-{turbineModelInfo[Field_MillTypeCode]}-Cp-Distribution.html"
  114. # htmlFilePath = os.path.join(outputAnalysisDir, htmlFileName)
  115. # fig.write_html(htmlFilePath)
  116. result_rows = []
  117. # result_rows.append({
  118. # Field_Return_TypeAnalyst: self.typeAnalyst(),
  119. # Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  120. # Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  121. # Field_CodeOfTurbine: Const_Output_Total,
  122. # Field_Return_FilePath: htmlFilePath,
  123. # Field_Return_IsSaveDatabase: True
  124. # })
  125. # 如果需要返回DataFrame,可以包含文件路径
  126. result_rows.append({
  127. Field_Return_TypeAnalyst: self.typeAnalyst(),
  128. Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  129. Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  130. Field_CodeOfTurbine: 'total',
  131. Field_MillTypeCode: turbineModelInfo[Field_MillTypeCode],
  132. Field_Return_FilePath: output_json_path,
  133. Field_Return_IsSaveDatabase: True
  134. })
  135. # Generate individual turbine plots
  136. for turbineCode, group in dataFrameOfTurbines.groupby(Field_CodeOfTurbine):
  137. currTurbineInfo = self.common.getTurbineInfo(
  138. conf.dataContract.dataFilter.powerFarmID, turbineCode, self.turbineInfo)
  139. # 创建一个列表来存储各个风电机组的数据
  140. turbine_data_list_each = []
  141. fig = go.Figure()
  142. for turbineCode in dataFrameOfTurbines[Field_CodeOfTurbine].unique():
  143. tempDataFrame = dataFrameOfTurbines[dataFrameOfTurbines[Field_CodeOfTurbine] == turbineCode]
  144. tempTurbineInfo = self.common.getTurbineInfo(
  145. conf.dataContract.dataFilter.powerFarmID, turbineCode, self.turbineInfo)
  146. fig.add_trace(go.Scatter(x=tempDataFrame[Field_WindSpeedFloor],
  147. y=tempDataFrame[Field_Cp],
  148. mode='lines',
  149. line=dict(color='lightgray', width=1),
  150. showlegend=False))
  151. # 提取数据
  152. turbine_data_other_each = {
  153. "engineName": tempTurbineInfo[Field_NameOfTurbine],
  154. "engineCode": turbineCode,
  155. "xData": tempDataFrame[Field_WindSpeedFloor].tolist(),
  156. # "yData": tempDataFrame[Field_Cp].tolist(),
  157. "yData": [None if math.isinf(val) else val for val in tempDataFrame[Field_Cp].tolist()],
  158. }
  159. turbine_data_list_each.append(turbine_data_other_each)
  160. fig.add_trace(go.Scatter(x=group[Field_WindSpeedFloor], y=group[Field_Cp], mode='lines', line=dict(
  161. color='darkblue'), showlegend=False))
  162. fig.update_layout(title=f'风机: {currTurbineInfo[Field_NameOfTurbine]}',
  163. xaxis_title='风速', yaxis_title='风能利用系数',
  164. xaxis=dict(
  165. range=[0, 26], tickmode='linear', dtick=1, tickangle=-45),
  166. yaxis=dict(
  167. dtick=self.axisStepCp,
  168. range=[self.axisLowerLimitCp,
  169. self.axisUpperLimitCp]
  170. )
  171. )
  172. engineTypeCode = turbineModelInfo.get(Field_MillTypeCode, "")
  173. if isinstance(engineTypeCode, pd.Series):
  174. engineTypeCode = engineTypeCode.iloc[0]
  175. engineTypeName = turbineModelInfo.get(Field_MachineTypeCode, "")
  176. if isinstance(engineTypeName, pd.Series):
  177. engineTypeName = engineTypeName.iloc[0]
  178. # 构建最终的JSON对象
  179. json_output = {
  180. "analysisTypeCode": "风电机组风能利用系数分析",
  181. "typecode": turbineModelInfo[Field_MillTypeCode],
  182. "engineCode": engineTypeCode,
  183. "engineTypeName": engineTypeName,
  184. "title": f'风机: {currTurbineInfo[Field_NameOfTurbine]}',
  185. "xaixs": "风速",
  186. "yaixs": "风能利用系数",
  187. "data": turbine_data_list_each
  188. }
  189. # 将JSON对象保存到文件
  190. output_json_path_each = os.path.join(outputAnalysisDir,
  191. f"{currTurbineInfo[Field_NameOfTurbine]}.json")
  192. with open(output_json_path_each, 'w', encoding='utf-8') as f:
  193. import json
  194. json.dump(json_output, f, ensure_ascii=False, indent=4)
  195. # 保存图像
  196. # pngFileName = f"{currTurbineInfo[Field_NameOfTurbine]}.png"
  197. # pngFilePath = os.path.join(outputAnalysisDir, pngFileName)
  198. # fig.write_image(pngFilePath, scale=3)
  199. # 保存HTML
  200. # htmlFileName = f"{currTurbineInfo[Field_NameOfTurbine]}.html"
  201. # htmlFilePath = os.path.join(outputAnalysisDir, htmlFileName)
  202. # fig.write_html(htmlFilePath)
  203. # result_rows.append({
  204. # Field_Return_TypeAnalyst: self.typeAnalyst(),
  205. # Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  206. # Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  207. # Field_CodeOfTurbine: currTurbineInfo[Field_CodeOfTurbine],
  208. # Field_Return_FilePath: pngFilePath,
  209. # Field_Return_IsSaveDatabase: False
  210. # })
  211. # 如果需要返回DataFrame,可以包含文件路径
  212. result_rows.append({
  213. Field_Return_TypeAnalyst: self.typeAnalyst(),
  214. Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  215. Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  216. Field_CodeOfTurbine: turbineCode,
  217. Field_Return_FilePath: output_json_path_each,
  218. Field_Return_IsSaveDatabase: True
  219. })
  220. # result_rows.append({
  221. # Field_Return_TypeAnalyst: self.typeAnalyst(),
  222. # Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  223. # Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  224. # Field_CodeOfTurbine: currTurbineInfo[Field_CodeOfTurbine],
  225. # Field_Return_FilePath: htmlFilePath,
  226. # Field_Return_IsSaveDatabase: True
  227. # })
  228. result_df = pd.DataFrame(result_rows)
  229. return result_df