123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375 |
- import os
- import pandas as pd
- import math
- import numpy as np
- from plotly.subplots import make_subplots
- import plotly.express as px
- import pandas as pd
- import plotly.graph_objects as go
- import seaborn as sns
- from matplotlib.ticker import MultipleLocator
- from behavior.analystWithGoodPoint import AnalystWithGoodPoint
- from utils.directoryUtil import DirectoryUtil as dir
- from algorithmContract.confBusiness import *
- from algorithmContract.contract import Contract
- class TSRCpPowerAnalyst(AnalystWithGoodPoint):
- """
- 风电机组叶尖速比-Cp-功率分析
- """
- def typeAnalyst(self):
- return "tsr_cp_power"
- def turbinesAnalysis(self, outputAnalysisDir, conf: Contract, turbineCodes):
- dictionary = self.processTurbineData(turbineCodes,conf,[Field_DeviceCode,Field_Time,Field_WindSpeed,Field_ActiverPower,Field_RotorSpeed, Field_GeneratorSpeed])
- dataFrameOfTurbines = self.userDataFrame(
- dictionary, conf.dataContract.configAnalysis, self)
- # 检查所需列是否存在
- required_columns = {Field_WindSpeed, Field_RotorSpeed,Field_PowerFloor,Field_GeneratorSpeed,Field_Cp}
- if not required_columns.issubset(dataFrameOfTurbines.columns):
- raise ValueError(f"DataFrame缺少必要的列。需要的列有: {required_columns}")
- turbrineInfos = self.common.getTurbineInfos(
- conf.dataContract.dataFilter.powerFarmID, turbineCodes, self.turbineInfo)
- groupedOfTurbineModel = turbrineInfos.groupby(Field_MillTypeCode)
- returnDatas = []
- for turbineModelCode, group in groupedOfTurbineModel:
- currTurbineCodes = group[Field_CodeOfTurbine].unique().tolist()
- currTurbineModeInfo = self.common.getTurbineModelByCode(
- turbineModelCode, self.turbineModelInfo)
- currDataFrameOfTurbines = dataFrameOfTurbines[dataFrameOfTurbines[Field_CodeOfTurbine].isin(
- currTurbineCodes)]
- #创建一个与dataFrameOfTurbines相同的dataFrameMerge
- dataFrameMerge=currDataFrameOfTurbines.copy()
- # return self.plot_tsr_distribution(self.tsr(dataFrameMerge), outputAnalysisDir, conf)
- dataFrameMerge[Field_PowerFarmName] = self.currPowerFarmInfo.loc[Field_PowerFarmName]
- # Calculate 'power_floor'
- dataFrameMerge[Field_PowerFloor] = (
- dataFrameMerge[Field_ActiverPower] / 10).astype(int) * 10
- # Ensure the necessary columns are of float type
- dataFrameMerge[Field_WindSpeed] = dataFrameMerge[Field_WindSpeed].astype(float)
- dataFrameMerge[Field_RotorSpeed] = dataFrameMerge[Field_RotorSpeed].astype(float)
- dataFrameMerge[Field_Cp] = dataFrameMerge[Field_Cp].astype(float)
- dataFrameMerge[Field_GeneratorSpeed] = dataFrameMerge[Field_GeneratorSpeed].astype(float)
- max_cutin = self.turbineModelInfo[Field_CutInWS].max()
- min_rated = self.turbineModelInfo[Field_RatedWindSpeed].min()
- dataFrameMerge = dataFrameMerge[(dataFrameMerge[Field_WindSpeed] > max_cutin) & (dataFrameMerge[Field_WindSpeed] < min_rated)]
- # Group by 'power_floor' and calculate median, max, and min of TSR
- dataFrameMerge[Field_TSRModified] = dataFrameMerge[Field_TSR] / (dataFrameMerge[Field_Cp] ** (1/3))
- grouped = dataFrameMerge.groupby([Field_PowerFloor, Field_CodeOfTurbine, Field_NameOfTurbine]).agg({
- Field_WindSpeed: 'median',
- Field_RotorSpeed: 'median',
- Field_GeneratorSpeed: 'median',
- 'tsr_modified': 'median',
- Field_Cp: 'median',
- Field_PowerFarmName: 'max'
- }).reset_index()
- # Rename columns for clarity post aggregation
- grouped.columns = [Field_PowerFloor, Field_CodeOfTurbine, Field_NameOfTurbine, Field_WindSpeed,
- Field_RotorSpeed, Field_GeneratorSpeed, Field_TSRModified, Field_Cp, Field_PowerFarmName]
- # Sort by 'power_floor'
- grouped = grouped.sort_values(by=[Field_CodeOfTurbine, Field_PowerFloor])
- returnData = self.plot_tsrcp_distribution(
- grouped, outputAnalysisDir, conf, currTurbineModeInfo)
- returnDatas.append(returnData)
- returnResult = pd.concat(returnDatas, ignore_index=True)
- return returnResult
- # ---------------------------------------------------
- # dataFrameMerge = self.userDataFrame(dictionary,conf.dataContract.configAnalysis,self)
- # # return self.plot_tsr_distribution(self.tsr(dataFrameMerge), outputAnalysisDir, conf)
- # dataFrameMerge[Field_PowerFarmName] = self.currPowerFarmInfo.loc[Field_PowerFarmName]
- # # Calculate 'power_floor'
- # dataFrameMerge[Field_PowerFloor] = (
- # dataFrameMerge[Field_ActiverPower] / 10).astype(int) * 10
- # # Ensure the necessary columns are of float type
- # dataFrameMerge[Field_WindSpeed] = dataFrameMerge[Field_WindSpeed].astype(float)
- # dataFrameMerge[Field_RotorSpeed] = dataFrameMerge[Field_RotorSpeed].astype(float)
- # dataFrameMerge[Field_Cp] = dataFrameMerge[Field_Cp].astype(float)
- # dataFrameMerge[Field_GeneratorSpeed] = dataFrameMerge[Field_GeneratorSpeed].astype(float)
- # max_cutin = self.turbineModelInfo[Field_CutInWS].max()
- # min_rated = self.turbineModelInfo[Field_RatedWindSpeed].min()
- # dataFrameMerge = dataFrameMerge[(dataFrameMerge[Field_WindSpeed] > max_cutin) & (dataFrameMerge[Field_WindSpeed] < min_rated)]
- # # Group by 'power_floor' and calculate median, max, and min of TSR
- # dataFrameMerge[Field_TSRModified] = dataFrameMerge[Field_TSR] / (dataFrameMerge[Field_Cp] ** (1/3))
- # grouped = dataFrameMerge.groupby([Field_PowerFloor, Field_CodeOfTurbine, Field_NameOfTurbine]).agg({
- # Field_WindSpeed: 'median',
- # Field_RotorSpeed: 'median',
- # Field_GeneratorSpeed: 'median',
- # 'tsr_modified': 'median',
- # Field_Cp: 'median',
- # Field_PowerFarmName: 'max'
- # }).reset_index()
- # # Rename columns for clarity post aggregation
- # grouped.columns = [Field_PowerFloor, Field_CodeOfTurbine, Field_NameOfTurbine, Field_WindSpeed,
- # Field_RotorSpeed, Field_GeneratorSpeed, Field_TSRModified, Field_Cp, Field_PowerFarmName]
- # # Sort by 'power_floor'
- # grouped = grouped.sort_values(by=[Field_CodeOfTurbine, Field_PowerFloor])
- # return self.plot_tsrcp_distribution(grouped, outputAnalysisDir, conf)
- def plot_tsrcp_distribution(self, dataFrameMerge: pd.DataFrame, outputAnalysisDir, conf: Contract, turbineModelInfo: pd.Series):
- """
- Generates tsr distribution plots for turbines in a wind farm.
- Parameters:
- - outputAnalysisDir: str, path to the directory containing input CSV files.
- - farm_name: str, name of the wind farm.
- - encoding: str, encoding of the input CSV files. Defaults to 'utf-8'.
- """
- x_name = Field_PowerFloor
- y_name = Field_TSRModified
- upLimitOfTSR = 20
- # 绘制全场TSR分布图
- fig = go.Figure()
- # colors = px.colors.sequential.Turbo
- # 创建一个列表来存储各个风电机组的数据
- turbine_data_list = []
- # 遍历不同的turbine来添加线条
- for turbine in dataFrameMerge[Field_NameOfTurbine].unique():
- turbine_data = dataFrameMerge[dataFrameMerge[Field_NameOfTurbine] == turbine]
- fig.add_trace(go.Scatter(x=turbine_data[x_name], y=turbine_data[y_name],
- mode='lines',
- line=dict(width=1.2),
- # line=dict(color=colors[idx % len(colors)]),
- name=turbine))
- # 提取数据
- turbine_data_total = {
- "engineName": turbine,
- "engineCode": turbine_data[Field_CodeOfTurbine].iloc[0],
- "xData": turbine_data[x_name].tolist(),
- "yData": turbine_data[y_name].tolist(),
- }
- turbine_data_list.append(turbine_data_total)
- fig.update_layout(
- title={
- "text": f'叶尖速比-风能利用系数分析-功率分布图-{turbineModelInfo[Field_MachineTypeCode]}',
- 'x': 0.5
- },
- xaxis=dict(
- title='功率',
- dtick=200,
- tickangle=-45,
- range=[0, 1800]),
- yaxis=dict(
- title= '叶尖速比/风能利用系数分析^(1/3)', # r"$\frac{TSR}{Cp^{1/3}}$" # 仅在.png格式下正确显示
- dtick=self.axisStepTSR,
- range=[self.axisLowerLimitTSR,
- self.axisUpperLimitTSR]
- ),
- legend=dict(
- orientation="h", # Horizontal orientation
- xanchor="center", # Anchor the legend to the center
- x=0.5, # Position legend at the center of the x-axis
- y=-0.2, # Position legend below the x-axis
- # itemsizing='constant', # Keep the size of the legend entries constant
- # itemwidth=50
- )
- )
- # 设置x轴标签旋转
- fig.update_xaxes(tickangle=-45)
- # 保存图形
- # fig.write_image(outputAnalysisDir + r"/{}-TSR-Distibute.png".format(confData.farm_name),format='png',width=800, height=500,scale=3)
- # fig.show()
- engineTypeCode = turbineModelInfo.get(Field_MillTypeCode, "")
- if isinstance(engineTypeCode, pd.Series):
- engineTypeCode = engineTypeCode.iloc[0]
- engineTypeName = turbineModelInfo.get(Field_MachineTypeCode, "")
- if isinstance(engineTypeName, pd.Series):
- engineTypeName = engineTypeName.iloc[0]
- # 构建最终的JSON对象
- json_output = {
- "analysisTypeCode": "叶尖速比-风能利用系数分析-功率分布",
- "typecode": turbineModelInfo[Field_MillTypeCode],
- "engineCode": engineTypeCode,
- "engineTypeName": engineTypeName,
- "title": f'叶尖速比-风能利用系数分析-功率分布图-{turbineModelInfo[Field_MachineTypeCode]}',
- "xaixs": "功率(kW)",
- "yaixs": "叶尖速比/风能利用系数分析^(1/3)",
- "data": turbine_data_list
- }
- # 保存HTML
- # htmlFileName = f"{dataFrameMerge[Field_PowerFarmName].iloc[0]}-TSR-Cp-Power-Distribution.html"
- # htmlFileName = f"{turbineModelInfo[Field_MillTypeCode]}-TSR-Cp-Power-Distribution.html"
- #htmlFilePath = os.path.join(outputAnalysisDir, htmlFileName)
- # fig.write_html(htmlFilePath)
- result_rows = []
- # result_rows.append({
- # Field_Return_TypeAnalyst: self.typeAnalyst(),
- # Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
- # Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
- # Field_CodeOfTurbine: 'total',
- # Field_Return_FilePath: htmlFilePath,
- # Field_Return_IsSaveDatabase: True
- # })
- # 将JSON对象保存到文件
- output_json_path = os.path.join(outputAnalysisDir, f"{turbineModelInfo[Field_MillTypeCode]}.json")
- with open(output_json_path, 'w', encoding='utf-8') as f:
- import json
- json.dump(json_output, f, ensure_ascii=False, indent=4)
- # 如果需要返回DataFrame,可以包含文件路径
- result_rows.append({
- Field_Return_TypeAnalyst: self.typeAnalyst(),
- Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
- Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
- Field_CodeOfTurbine: 'total',
- Field_MillTypeCode: turbineModelInfo[Field_MillTypeCode],
- Field_Return_FilePath: output_json_path,
- Field_Return_IsSaveDatabase: True
- })
- # 绘制每个设备的TSR分布图
- for name, group in dataFrameMerge.groupby([Field_NameOfTurbine, Field_CodeOfTurbine]):
- fig = go.Figure()
- # 创建一个列表来存储各个风电机组的数据
- turbine_data_list_each = []
- # 循环绘制turbine的线条
- for turbine in dataFrameMerge[Field_NameOfTurbine].unique():
- turbine_data = dataFrameMerge[dataFrameMerge[Field_NameOfTurbine] == turbine]
- fig.add_trace(go.Scatter(x=turbine_data[x_name],
- y=turbine_data[y_name],
- mode='lines',
- line=dict(color='lightgrey', width=1.2),
- showlegend=False))
- # 提取数据
- turbine_data_each = {
- "engineName": turbine,
- "engineCode": turbine_data[Field_CodeOfTurbine].iloc[0],
- "xData": turbine_data[x_name].tolist(),
- "yData": turbine_data[y_name].tolist(),
- }
- turbine_data_list_each.append(turbine_data_each)
- fig.add_trace(go.Scatter(x=group[x_name],
- y=group[y_name],
- mode='lines',
- line=dict(color='darkblue', width=1.5),
- showlegend=False))
- fig.update_layout(
- title={"text": '机组: {}'.format(name[0])},
- # margin=dict(
- # t=35, # 顶部 margin,减小这个值可以使标题更靠近图形
- # l=60, # 左侧 margin
- # r=60, # 右侧 margin
- # b=40, # 底部 margin
- # ),
- xaxis=dict(
- title='功率',
- dtick=200,
- tickangle=-45,
- range=[0, 1800]),
- yaxis=dict(
- title= '叶尖速比/风能利用系数分析^(1/3)', # r"$\frac{TSR}{Cp^{1/3}}$" # 仅在.png格式下正确显示
- dtick=self.axisStepTSR,
- range=[self.axisLowerLimitTSR,
- self.axisUpperLimitTSR]
- )
- )
- fig.update_xaxes(tickangle=-45)
- # 保存图像
- pngFileName = f"{name[0]}.png"
- pngFilePath = os.path.join(outputAnalysisDir, pngFileName)
- fig.write_image(pngFilePath, scale=3)
- engineTypeCode = turbineModelInfo.get(Field_MillTypeCode, "")
- if isinstance(engineTypeCode, pd.Series):
- engineTypeCode = engineTypeCode.iloc[0]
- engineTypeName = turbineModelInfo.get(Field_MachineTypeCode, "")
- if isinstance(engineTypeName, pd.Series):
- engineTypeName = engineTypeName.iloc[0]
- # 构建最终的JSON对象
- json_output = {
- "analysisTypeCode": "叶尖速比-风能利用系数分析-功率分布",
- "typecode": turbineModelInfo[Field_MillTypeCode],
- "engineCode": engineTypeCode,
- "engineTypeName": engineTypeName,
- "title": f'机组:{format(name[0])}',
- "xaixs": "功率(kW)",
- "yaixs": "叶尖速比/风能利用系数分析^(1/3)",
- "data": turbine_data_list_each
- }
- # 保存HTML
- # htmlFileName = f"{name[0]}.html"
- # htmlFilePath = os.path.join(outputAnalysisDir, htmlFileName)
- #fig.write_html(htmlFilePath)
- result_rows.append({
- Field_Return_TypeAnalyst: self.typeAnalyst(),
- Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
- Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
- Field_CodeOfTurbine: name[1],
- Field_Return_FilePath: pngFilePath,
- Field_Return_IsSaveDatabase: False
- })
- # 将JSON对象保存到文件
- output_json_path_each = os.path.join(outputAnalysisDir, f"{name[0]}.json")
- with open(output_json_path_each, 'w', encoding='utf-8') as f:
- import json
- json.dump(json_output, f, ensure_ascii=False, indent=4)
- # 如果需要返回DataFrame,可以包含文件路径
- result_rows.append({
- Field_Return_TypeAnalyst: self.typeAnalyst(),
- Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
- Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
- Field_CodeOfTurbine: name[1],
- Field_Return_FilePath: output_json_path_each,
- Field_Return_IsSaveDatabase: True
- })
- # result_rows.append({
- # Field_Return_TypeAnalyst: self.typeAnalyst(),
- # Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
- # Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
- # Field_CodeOfTurbine: name[1],
- # Field_Return_FilePath: htmlFilePath,
- # Field_Return_IsSaveDatabase: True
- # })
- result_df = pd.DataFrame(result_rows)
- return result_df
|