tsrWindSpeedAnalyst.py 16 KB

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