tsrWindSpeedAnalyst.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  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. # 创建一个列表来存储各个风电机组的数据
  114. turbine_data_list = []
  115. # 遍历不同的turbine来添加线条
  116. for turbine in dataFrame[Field_NameOfTurbine].unique():
  117. turbine_data = dataFrame[dataFrame[Field_NameOfTurbine] == turbine]
  118. fig.add_trace(go.Scatter(x=turbine_data[x_name], y=turbine_data[y_name],
  119. mode='lines',
  120. # line=dict(color=colors[idx % len(colors)]),
  121. name=turbine))
  122. # 提取数据
  123. turbine_data_total = {
  124. "engineName": turbine,
  125. "engineCode": turbine_data[Field_CodeOfTurbine].iloc[0],
  126. "xData": turbine_data[x_name].tolist(),
  127. "yData": turbine_data[y_name].tolist(),
  128. }
  129. turbine_data_list.append(turbine_data_total)
  130. fig.update_layout(
  131. title={
  132. "text": f'叶尖速比分布图-{turbineModelInfo[Field_MachineTypeCode]}',
  133. 'x': 0.5
  134. },
  135. # margin=dict(
  136. # t=35, # 顶部 margin,减小这个值可以使标题更靠近图形
  137. # l=60, # 左侧 margin
  138. # r=60, # 右侧 margin
  139. # b=40, # 底部 margin
  140. # ),
  141. # legend=dict(title='Turbine',
  142. # x=1.02,
  143. # y=0.5,
  144. # orientation='v',
  145. # traceorder='normal',
  146. # font=dict(size=12),
  147. # bgcolor='rgba(255, 255, 255, 0)',
  148. # bordercolor='rgba(255, 255, 255, 0)'),
  149. legend=dict(
  150. orientation="h", # Horizontal orientation
  151. xanchor="center", # Anchor the legend to the center
  152. x=0.5, # Position legend at the center of the x-axis
  153. y=-0.2, # Position legend below the x-axis
  154. # itemsizing='constant', # Keep the size of the legend entries constant
  155. # itemwidth=50
  156. ),
  157. xaxis=dict(
  158. title='风速',
  159. dtick=1,
  160. tickangle=-45,
  161. range=[0, 26]),
  162. yaxis=dict(
  163. title='叶尖风速比',
  164. dtick=self.axisStepTSR,
  165. range=[self.axisLowerLimitTSR,
  166. self.axisUpperLimitTSR]
  167. )
  168. )
  169. # 设置x轴标签旋转
  170. fig.update_xaxes(tickangle=-45)
  171. # 保存图形
  172. # fig.write_image(csvFileDirOfCp + r"/{}-TSR-Distibute.png".format(confData.farm_name),format='png',width=800, height=500,scale=3)
  173. # fig.show()
  174. engineTypeCode = turbineModelInfo.get(Field_MillTypeCode, "")
  175. if isinstance(engineTypeCode, pd.Series):
  176. engineTypeCode = engineTypeCode.iloc[0]
  177. engineTypeName = turbineModelInfo.get(Field_MachineTypeCode, "")
  178. if isinstance(engineTypeName, pd.Series):
  179. engineTypeName = engineTypeName.iloc[0]
  180. # 构建最终的JSON对象
  181. json_output = {
  182. "analysisTypeCode": "风电机组叶尖速比和风速分析",
  183. "typecode": turbineModelInfo[Field_MillTypeCode],
  184. "engineCode": engineTypeCode,
  185. "engineTypeName": engineTypeName,
  186. "title": f'叶尖速比分布图-{turbineModelInfo[Field_MachineTypeCode]}',
  187. "xaixs": "风速",
  188. "yaixs": "叶尖风速比",
  189. "data": turbine_data_list
  190. }
  191. # 保存HTML
  192. # htmlFileName = f"{dataFrame[Field_PowerFarmName].iloc[0]}-TSR-Distribution-{turbineModelInfo[Field_MillTypeCode]}.html"
  193. # htmlFilePath = os.path.join(outputAnalysisDir, htmlFileName)
  194. # fig.write_html(htmlFilePath)
  195. result_rows = []
  196. # result_rows.append({
  197. # Field_Return_TypeAnalyst: self.typeAnalyst(),
  198. # Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  199. # Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  200. # Field_CodeOfTurbine: 'total',
  201. # Field_Return_FilePath: htmlFilePath,
  202. # Field_Return_IsSaveDatabase: True
  203. # })
  204. # 将JSON对象保存到文件
  205. output_json_path = os.path.join(outputAnalysisDir, f"{dataFrame[Field_PowerFarmName].iloc[0]}-TSR-Distribution-{turbineModelInfo[Field_MillTypeCode]}.json")
  206. with open(output_json_path, 'w', encoding='utf-8') as f:
  207. import json
  208. json.dump(json_output, f, ensure_ascii=False, indent=4)
  209. # 如果需要返回DataFrame,可以包含文件路径
  210. result_rows.append({
  211. Field_Return_TypeAnalyst: self.typeAnalyst(),
  212. Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  213. Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  214. Field_CodeOfTurbine: 'total',
  215. Field_MillTypeCode: turbineModelInfo[Field_MillTypeCode],
  216. Field_Return_FilePath: output_json_path,
  217. Field_Return_IsSaveDatabase: True
  218. })
  219. # 绘制每个设备的TSR分布图
  220. for name, group in dataFrame.groupby([Field_NameOfTurbine, Field_CodeOfTurbine]):
  221. fig = go.Figure()
  222. # 创建一个列表来存储各个风电机组的数据
  223. turbine_data_list_each = []
  224. # 循环绘制turbine的线条
  225. for turbine in dataFrame[Field_NameOfTurbine].unique():
  226. turbine_data = dataFrame[dataFrame[Field_NameOfTurbine] == turbine]
  227. fig.add_trace(go.Scatter(x=turbine_data[x_name],
  228. y=turbine_data[y_name],
  229. mode='lines',
  230. line=dict(color='lightgrey'),
  231. showlegend=False))
  232. # 提取数据
  233. turbine_data_each = {
  234. "engineName": turbine,
  235. "engineCode": turbine_data[Field_CodeOfTurbine].iloc[0],
  236. "xData": turbine_data[x_name].tolist(),
  237. "yData": turbine_data[y_name].tolist(),
  238. }
  239. turbine_data_list_each.append(turbine_data_each)
  240. fig.add_trace(go.Scatter(x=group[x_name],
  241. y=group[y_name],
  242. mode='lines',
  243. line=dict(color='darkblue'),
  244. showlegend=False))
  245. fig.update_layout(
  246. title={"text": '机组: {}'.format(name[0])},
  247. # margin=dict(
  248. # t=35, # 顶部 margin,减小这个值可以使标题更靠近图形
  249. # l=60, # 左侧 margin
  250. # r=60, # 右侧 margin
  251. # b=40, # 底部 margin
  252. # ),
  253. xaxis=dict(
  254. title='风速最低阈值 ',
  255. dtick=1,
  256. tickangle=-45,
  257. range=[0, 26]),
  258. yaxis=dict(
  259. title='叶尖速比',
  260. dtick=self.axisStepTSR,
  261. range=[self.axisLowerLimitTSR,
  262. self.axisUpperLimitTSR]
  263. )
  264. )
  265. fig.update_xaxes(tickangle=-45)
  266. engineTypeCode = turbineModelInfo.get(Field_MillTypeCode, "")
  267. if isinstance(engineTypeCode, pd.Series):
  268. engineTypeCode = engineTypeCode.iloc[0]
  269. engineTypeName = turbineModelInfo.get(Field_MachineTypeCode, "")
  270. if isinstance(engineTypeName, pd.Series):
  271. engineTypeName = engineTypeName.iloc[0]
  272. # 构建最终的JSON对象
  273. json_output = {
  274. "analysisTypeCode": "风电机组叶尖速比和风速分析",
  275. "typecode": turbineModelInfo[Field_MillTypeCode],
  276. "engineCode": engineTypeCode,
  277. "engineTypeName": engineTypeName,
  278. "title": f'机组:{format(name[0])}',
  279. "xaixs": "功率(kW)",
  280. "yaixs": "叶尖速比",
  281. "data": turbine_data_list_each
  282. }
  283. # 将JSON对象保存到文件
  284. output_json_path_each = os.path.join(outputAnalysisDir, f"{name[0]}.json")
  285. with open(output_json_path_each, 'w', encoding='utf-8') as f:
  286. import json
  287. json.dump(json_output, f, ensure_ascii=False, indent=4)
  288. # 如果需要返回DataFrame,可以包含文件路径
  289. result_rows.append({
  290. Field_Return_TypeAnalyst: self.typeAnalyst(),
  291. Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  292. Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  293. Field_CodeOfTurbine: name[1],
  294. Field_Return_FilePath: output_json_path_each,
  295. Field_Return_IsSaveDatabase: True
  296. })
  297. # 保存图像
  298. pngFileName = f"{name[0]}.png"
  299. pngFilePath = os.path.join(outputAnalysisDir, pngFileName)
  300. fig.write_image(pngFilePath, scale=3)
  301. # 保存HTML
  302. # htmlFileName = f"{name[0]}.html"
  303. # htmlFilePath = os.path.join(outputAnalysisDir, htmlFileName)
  304. # fig.write_html(htmlFilePath)
  305. result_rows.append({
  306. Field_Return_TypeAnalyst: self.typeAnalyst(),
  307. Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  308. Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  309. Field_CodeOfTurbine: name[1],
  310. Field_Return_FilePath: pngFilePath,
  311. Field_Return_IsSaveDatabase: False
  312. })
  313. # result_rows.append({
  314. # Field_Return_TypeAnalyst: self.typeAnalyst(),
  315. # Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  316. # Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  317. # Field_CodeOfTurbine: name[1],
  318. # Field_Return_FilePath: htmlFilePath,
  319. # Field_Return_IsSaveDatabase: True
  320. # })
  321. result_df = pd.DataFrame(result_rows)
  322. return result_df