tsrWindSpeedAnalyst.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. import os
  2. import pandas as pd
  3. import plotly.graph_objects as go
  4. import seaborn as sns
  5. from algorithmContract.confBusiness import *
  6. from algorithmContract.contract import Contract
  7. from behavior.analystWithGoodPoint import AnalystWithGoodPoint
  8. class TSRWindSpeedAnalyst(AnalystWithGoodPoint):
  9. """
  10. 风电机组叶尖速比分析
  11. """
  12. def typeAnalyst(self):
  13. return "tsr_windspeed"
  14. def selectColumns(self):
  15. return [Field_DeviceCode,Field_Time,Field_WindSpeed,Field_ActiverPower,Field_RotorSpeed,Field_GeneratorSpeed]
  16. # def turbineAnalysis(self, dataFrame, outputAnalysisDir, outputFilePath, conf: Contract, Field_NameOfTurbine):
  17. #
  18. # self.tsr(dataFrame, outputFilePath, Field_WindSpeed, Field_RotorSpeed,
  19. # Field_ActiverPower)
  20. def tsr(self, dataFrame : pd.DataFrame):
  21. #Add column field_name
  22. dataFrame[Field_PowerFarmName] = self.currPowerFarmInfo.loc[Field_PowerFarmName]
  23. # Alias the power column
  24. # dataFrame[Field_Power] = dataFrame[Field_ActiverPower]
  25. # Calculate 'wind_speed_floor'
  26. # dataFrame[Field_WindSpeedFloor] = (dataFrame[Field_WindSpeed] / 1).astype(int) + 0.5
  27. # Ensure the necessary columns are of float type
  28. # dataFrame['wind_speed'] = dataFrame[Field_WindSpeed].astype(float)
  29. dataFrame[Field_RotorSpeed] = dataFrame[Field_RotorSpeed].astype(float)
  30. dataFrame[Field_GeneratorSpeed] = dataFrame[Field_GeneratorSpeed].astype(float)
  31. # rotor_diameter = pd.to_numeric(rotor_diameter, errors='coerce')
  32. # # Calculate TSR
  33. # dataFrame['tsr'] = (dataFrame['rotor_speed'] * 0.104667 *
  34. # (rotor_diameter / 2)) / dataFrame['wind_speed']
  35. # Group by 'wind_speed_floor' and calculate median, max, and min of TSR
  36. grouped = dataFrame.groupby([Field_WindSpeedFloor,Field_CodeOfTurbine,Field_NameOfTurbine]).agg({
  37. Field_ActiverPower: 'median',
  38. Field_PowerFloor: 'median',
  39. Field_RotorSpeed : 'median',
  40. Field_GeneratorSpeed: 'median',
  41. Field_TSR : ['median', 'max', 'min'],
  42. Field_PowerFarmName: 'max'
  43. }).reset_index()
  44. # Rename columns for clarity post aggregation
  45. grouped.columns = [Field_WindSpeedFloor, Field_CodeOfTurbine, Field_NameOfTurbine,Field_Power,Field_PowerFloor,
  46. Field_RotorSpeed, Field_GeneratorSpeed, Field_TSR, Field_TSRMax, Field_TSRMin, Field_PowerFarmName]
  47. # Sort by 'wind_speed_floor'
  48. grouped.sort_values(by=[Field_NameOfTurbine, Field_PowerFloor])
  49. return grouped
  50. def turbinesAnalysis(self, outputAnalysisDir, conf: Contract, turbineCodes):
  51. dictionary = self.processTurbineData(turbineCodes,conf,self.selectColumns())
  52. dataFrameOfTurbines = self.userDataFrame(dictionary,conf.dataContract.configAnalysis,self)
  53. # 检查所需列是否存在
  54. required_columns = {Field_WindSpeed, Field_RotorSpeed,Field_PowerFloor,Field_GeneratorSpeed,Field_TSR}
  55. if not required_columns.issubset(dataFrameOfTurbines.columns):
  56. raise ValueError(f"DataFrame缺少必要的列。需要的列有: {required_columns}")
  57. turbrineInfos = self.common.getTurbineInfos(
  58. conf.dataContract.dataFilter.powerFarmID, turbineCodes, self.turbineInfo)
  59. groupedOfTurbineModel = turbrineInfos.groupby(Field_MillTypeCode)
  60. returnDatas = []
  61. for turbineModelCode, group in groupedOfTurbineModel:
  62. currTurbineCodes = group[Field_CodeOfTurbine].unique().tolist()
  63. currTurbineModeInfo = self.common.getTurbineModelByCode(
  64. turbineModelCode, self.turbineModelInfo)
  65. currDataFrameOfTurbines = dataFrameOfTurbines[dataFrameOfTurbines[Field_CodeOfTurbine].isin(
  66. currTurbineCodes)]
  67. #创建一个与dataFrameOfTurbines相同的dataFrame
  68. dataFrame=currDataFrameOfTurbines.copy()
  69. returnData = self.plot_tsr_distribution(
  70. self.tsr(dataFrame), outputAnalysisDir, conf, currTurbineModeInfo)
  71. returnDatas.append(returnData)
  72. returnResult = pd.concat(returnDatas, ignore_index=True)
  73. return returnResult
  74. def plot_tsr_distribution(self, dataFrame: pd.DataFrame, outputAnalysisDir, conf: Contract,turbineModelInfo: pd.Series):
  75. """
  76. Generates tsr distribution plots for turbines in a wind farm.
  77. Parameters:
  78. - csvFileDirOfCp: str, path to the directory containing input CSV files.
  79. - farm_name: str, name of the wind farm.
  80. - encoding: str, encoding of the input CSV files. Defaults to 'utf-8'.
  81. """
  82. x_name = 'wind_speed_floor'
  83. y_name = 'tsr'
  84. # upLimitOfTSR = 20
  85. # 设置绘图样式
  86. sns.set_palette('deep')
  87. # 初始化结果DataFrame
  88. # res = pd.DataFrame()
  89. #
  90. # # 遍历输入目录中的所有文件
  91. # for root, dir_names, file_names in dir.list_directory(csvFileDirOfCp):
  92. # for file_name in file_names:
  93. #
  94. # if not file_name.endswith(CSVSuffix):
  95. # continue
  96. #
  97. # file_path = os.path.join(root, file_name)
  98. #
  99. # # 读取CSV文件
  100. # frame = pd.read_csv(file_path, encoding=encoding)
  101. # frame = frame[(frame[x_name] > 0)]
  102. #
  103. # # 选择需要的列并合并到结果DataFrame中
  104. # res = pd.concat([res, frame], axis=0)
  105. #
  106. # # 重置索引
  107. # ress = res.reset_index()
  108. # dataFrame[Field_NameOfTurbine] = dataFrame[Field_NameOfTurbine].astype(str)
  109. # dataFrame = dataFrame.sort_values(by=[Field_NameOfTurbine, x_name])
  110. # 绘制全场TSR分布图
  111. fig = go.Figure()
  112. # colors = px.colors.sequential.Turbo
  113. # 遍历不同的turbine来添加线条
  114. for turbine in dataFrame[Field_NameOfTurbine].unique():
  115. turbine_data = dataFrame[dataFrame[Field_NameOfTurbine] == turbine]
  116. fig.add_trace(go.Scatter(x=turbine_data[x_name], y=turbine_data[y_name],
  117. mode='lines',
  118. # line=dict(color=colors[idx % len(colors)]),
  119. name=turbine))
  120. fig.update_layout(
  121. title={
  122. "text": f'叶尖速比分布图-{turbineModelInfo[Field_MachineTypeCode]}',
  123. 'x': 0.5
  124. },
  125. # margin=dict(
  126. # t=35, # 顶部 margin,减小这个值可以使标题更靠近图形
  127. # l=60, # 左侧 margin
  128. # r=60, # 右侧 margin
  129. # b=40, # 底部 margin
  130. # ),
  131. # legend=dict(title='Turbine',
  132. # x=1.02,
  133. # y=0.5,
  134. # orientation='v',
  135. # traceorder='normal',
  136. # font=dict(size=12),
  137. # bgcolor='rgba(255, 255, 255, 0)',
  138. # bordercolor='rgba(255, 255, 255, 0)'),
  139. legend=dict(
  140. orientation="h", # Horizontal orientation
  141. xanchor="center", # Anchor the legend to the center
  142. x=0.5, # Position legend at the center of the x-axis
  143. y=-0.2, # Position legend below the x-axis
  144. # itemsizing='constant', # Keep the size of the legend entries constant
  145. # itemwidth=50
  146. ),
  147. xaxis=dict(
  148. title='风速',
  149. dtick=1,
  150. tickangle=-45,
  151. range=[0, 26]),
  152. yaxis=dict(
  153. title='叶尖风速比',
  154. dtick=self.axisStepTSR,
  155. range=[self.axisLowerLimitTSR,
  156. self.axisUpperLimitTSR]
  157. )
  158. )
  159. # 设置x轴标签旋转
  160. fig.update_xaxes(tickangle=-45)
  161. # 保存图形
  162. # fig.write_image(csvFileDirOfCp + r"/{}-TSR-Distibute.png".format(confData.farm_name),format='png',width=800, height=500,scale=3)
  163. # fig.show()
  164. # 保存HTML
  165. htmlFileName = f"{dataFrame[Field_PowerFarmName].iloc[0]}-TSR-Distribution-{turbineModelInfo[Field_MillTypeCode]}.html"
  166. htmlFilePath = os.path.join(outputAnalysisDir, htmlFileName)
  167. fig.write_html(htmlFilePath)
  168. result_rows = []
  169. result_rows.append({
  170. Field_Return_TypeAnalyst: self.typeAnalyst(),
  171. Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  172. Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  173. Field_CodeOfTurbine: 'total',
  174. Field_Return_FilePath: htmlFilePath,
  175. Field_Return_IsSaveDatabase: True
  176. })
  177. # 绘制每个设备的TSR分布图
  178. for name, group in dataFrame.groupby([Field_NameOfTurbine, Field_CodeOfTurbine]):
  179. fig = go.Figure()
  180. # 循环绘制turbine的线条
  181. for turbine in dataFrame[Field_NameOfTurbine].unique():
  182. turbine_data = dataFrame[dataFrame[Field_NameOfTurbine] == turbine]
  183. fig.add_trace(go.Scatter(x=turbine_data[x_name],
  184. y=turbine_data[y_name],
  185. mode='lines',
  186. line=dict(color='lightgrey'),
  187. showlegend=False))
  188. fig.add_trace(go.Scatter(x=group[x_name],
  189. y=group[y_name],
  190. mode='lines',
  191. line=dict(color='darkblue'),
  192. showlegend=False))
  193. fig.update_layout(
  194. title={"text": '机组: {}'.format(name[0])},
  195. # margin=dict(
  196. # t=35, # 顶部 margin,减小这个值可以使标题更靠近图形
  197. # l=60, # 左侧 margin
  198. # r=60, # 右侧 margin
  199. # b=40, # 底部 margin
  200. # ),
  201. xaxis=dict(
  202. title='风速最低阈值 ',
  203. dtick=1,
  204. tickangle=-45,
  205. range=[0, 26]),
  206. yaxis=dict(
  207. title='叶尖速比',
  208. dtick=self.axisStepTSR,
  209. range=[self.axisLowerLimitTSR,
  210. self.axisUpperLimitTSR]
  211. )
  212. )
  213. fig.update_xaxes(tickangle=-45)
  214. # 保存图像
  215. pngFileName = f"{name[0]}.png"
  216. pngFilePath = os.path.join(outputAnalysisDir, pngFileName)
  217. fig.write_image(pngFilePath, scale=3)
  218. # 保存HTML
  219. htmlFileName = f"{name[0]}.html"
  220. htmlFilePath = os.path.join(outputAnalysisDir, htmlFileName)
  221. fig.write_html(htmlFilePath)
  222. result_rows.append({
  223. Field_Return_TypeAnalyst: self.typeAnalyst(),
  224. Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  225. Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  226. Field_CodeOfTurbine: name[1],
  227. Field_Return_FilePath: pngFilePath,
  228. Field_Return_IsSaveDatabase: False
  229. })
  230. result_rows.append({
  231. Field_Return_TypeAnalyst: self.typeAnalyst(),
  232. Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  233. Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  234. Field_CodeOfTurbine: name[1],
  235. Field_Return_FilePath: htmlFilePath,
  236. Field_Return_IsSaveDatabase: True
  237. })
  238. result_df = pd.DataFrame(result_rows)
  239. return result_df