123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540 |
- import numpy as np
- import pandas as pd
- import os
- import plotly.graph_objects as go
- # 声明全局变量
- fieldActivePower = "功率"
- fieldWindSpeed = "风速"
- fieldPitchAngle = "叶片角度"
- PRated = 1500 # 风机额定功率
- VCutOut = 25 # 切出风速
- VCutIn = 3 # 切入风速
- VRated = 10 # 额定风速
- VNum = int(VCutOut / 0.25) # 风速分区数量
- # 读取数据函数
- def read_data():
- # 读取 CSV 文件
- March809 = pd.read_csv(
- "./data/DataClassificationIdentification/A01-G.csv", encoding="utf-8")
- IdealCurve = pd.read_csv(
- "./data/DataClassificationIdentification/A型风机设计功率曲线.csv", encoding="utf-8")
- return March809, IdealCurve
- # 计算统计信息函数
- def calculate_statistics(March809, IdealCurve):
- AA = len(March809)
- BB = len(IdealCurve)
- PowerMax = March809[fieldActivePower].max()
- PowerRated = int(np.ceil(PowerMax / 100) * 100)
- PNum = PowerRated // 25 # 功率分区数量
- # 计算实际发电量
- EPActualTotal = March809[March809[fieldActivePower]
- >= 0][fieldActivePower].sum() / 6
- WindSpeedAvr = March809[fieldWindSpeed].mean()
- # 计算风机可利用率
- nShouldGP = np.sum(March809[fieldWindSpeed] >= VCutIn)
- nRealGP = np.sum((March809[fieldWindSpeed] >= VCutIn)
- & (March809[fieldActivePower] > 0))
- TurbineRunRate = (nRealGP / nShouldGP * 100) if nShouldGP > 0 else 0
- # 计算理论发电量
- EPIdealTotalAAA = 0
- for i in range(AA):
- nWhichBin = 0
- for m in range(BB - 1):
- if IdealCurve.iloc[m][fieldWindSpeed] < March809.iloc[i][fieldWindSpeed] <= IdealCurve.iloc[m + 1][fieldWindSpeed]:
- nWhichBin = m
- break
- if nWhichBin > 0:
- IdealPower = (March809.iloc[i][fieldWindSpeed] - IdealCurve.iloc[nWhichBin][fieldWindSpeed]) / (IdealCurve.iloc[nWhichBin + 1][fieldWindSpeed] - IdealCurve.iloc[nWhichBin]
- [fieldWindSpeed]) * (IdealCurve.iloc[nWhichBin + 1][fieldActivePower] - IdealCurve.iloc[nWhichBin][fieldActivePower]) + IdealCurve.iloc[nWhichBin][fieldActivePower]
- EPIdealTotalAAA += IdealPower / 6
- return AA, BB, PNum, EPActualTotal, WindSpeedAvr, TurbineRunRate, EPIdealTotalAAA
- # 分类数据函数
- def classify_data(March809, PNum, VNum):
- DzMarch809 = March809[March809[fieldActivePower] > 0]
- nCounter1 = len(DzMarch809)
- XBoxNumber = np.ones((PNum, VNum), dtype=int)
- for i in range(nCounter1):
- nWhichP = np.digitize(
- DzMarch809.iloc[i][fieldActivePower], np.arange(0, PNum * 25, 25)) - 1
- nWhichV = np.digitize(
- DzMarch809.iloc[i][fieldWindSpeed], np.arange(0.125, VNum * 0.25, 0.25))
- if nWhichP < PNum and nWhichV < VNum:
- XBoxNumber[nWhichP, nWhichV] += 1
- XBoxNumber -= 1
- return DzMarch809, XBoxNumber
- # 计算百分比函数
- def compute_percentages(XBoxNumber, PNum, VNum):
- PBoxPercent = np.zeros((PNum, VNum))
- PBinSum = XBoxNumber.sum(axis=1)
- for i in range(PNum):
- if PBinSum[i] > 0:
- PBoxPercent[i, :] = XBoxNumber[i, :] / PBinSum[i] * 100
- VBoxPercent = np.zeros((PNum, VNum))
- VBinSum = XBoxNumber.sum(axis=0)
- for i in range(VNum):
- if VBinSum[i] > 0:
- VBoxPercent[:, i] = XBoxNumber[:, i] / VBinSum[i] * 100
- return PBoxPercent, VBoxPercent
- # 查找主带函数
- def find_main_band(PBoxPercent, PNum, VNum, XBoxNumber):
- PBoxMaxIndex = np.argmax(PBoxPercent, axis=1)
- PBoxMaxP = np.max(PBoxPercent, axis=1)
- DotDense = np.zeros(PNum)
- DotDenseLeftRight = np.zeros((PNum, 2), dtype=int)
- DotValve = 90
- for i in range(PNum - 6):
- PDotDenseSum = PBoxMaxP[i]
- iSpreadRight = iSpreadLeft = 1
- while PDotDenseSum < DotValve:
- if (PBoxMaxIndex[i] + iSpreadRight) < VNum - 1:
- PDotDenseSum += PBoxPercent[i, PBoxMaxIndex[i] + iSpreadRight]
- iSpreadRight += 1
- if (PBoxMaxIndex[i] + iSpreadRight) > VNum - 1:
- break
- if (PBoxMaxIndex[i] - iSpreadLeft) > 0:
- PDotDenseSum += PBoxPercent[i, PBoxMaxIndex[i] - iSpreadLeft]
- iSpreadLeft += 1
- if (PBoxMaxIndex[i] - iSpreadLeft) <= 0:
- break
- iSpreadRight -= 1
- iSpreadLeft -= 1
- DotDenseLeftRight[i, :] = [iSpreadLeft, iSpreadRight]
- DotDense[i] = iSpreadLeft + iSpreadRight + 1
- DotDenseWidthLeft = DotDenseLeftRight[:, 1]
- MainBandRight = np.median(DotDenseWidthLeft)
- PowerLimitValve = int(np.ceil(MainBandRight)) + 3
- PowerLimit = np.zeros(PNum)
- nCounterLimit = nCounter = 0
- WidthAverage = 0
- for i in range(PNum - 6):
- if DotDenseLeftRight[i, 1] > PowerLimitValve and XBoxNumber[i, :].sum() > 20:
- PowerLimit[i] = 1
- nCounterLimit += 1
- else:
- WidthAverage += DotDenseLeftRight[i, 1]
- nCounter += 1
- WidthAverage /= nCounter
- WidthVar = np.sqrt(np.mean((DotDenseLeftRight[:, 1] - WidthAverage) ** 2))
- PowerBandWidth = WidthAverage * 2 * 0.25
- return DotDense, DotDenseLeftRight, PowerLimit, WidthAverage, WidthVar, PowerBandWidth, PBoxMaxIndex
- # 标记坏点函数
- def mark_bad_points(DzMarch809, DotDenseLeftRight, PBoxMaxIndex, PowerLimit, PNum, VNum, XBoxNumber, PBoxPercent):
- CurveWidthR = int(np.ceil(DotDenseLeftRight[:, 1].mean())) + 2
- CurveWidthL = CurveWidthR
- BBoxLimit = np.zeros((PNum, VNum))
- for i in range(3, PNum - 6):
- if PowerLimit[i] == 1:
- BBoxLimit[i, PBoxMaxIndex[i] + CurveWidthR + 1: VNum] = 1
- BBoxRemove = np.zeros((PNum, VNum))
- for m in range(PNum - 6):
- BBoxRemove[m, PBoxMaxIndex[m] + CurveWidthR: VNum] = 1
- BBoxRemove[m, :PBoxMaxIndex[m] - CurveWidthL + 1] = 2
- CurveTop = [0, 0]
- CurveTopValve = 3
- BTopFind = False
- for m in range(PNum - 4, 0, -1):
- for n in range(VNum):
- if PBoxPercent[m, n] > CurveTopValve and XBoxNumber[m, n] >= 10:
- CurveTop = [m, n]
- BTopFind = True
- break
- if BTopFind:
- break
- IsolateValve = 3
- for m in range(PNum - 6):
- for n in range(PBoxMaxIndex[m] + CurveWidthR, VNum):
- if PBoxPercent[m, n] < IsolateValve:
- BBoxRemove[m, n] = 1
- for m in range(PNum - 6, PNum):
- BBoxRemove[m, :] = 3
- for m in range(PNum - 5, PNum):
- BBoxRemove[m, :CurveTop[1] - 2] = 2
- DzMarch809Sel = np.zeros(len(DzMarch809), dtype=int)
- for i in range(len(DzMarch809)):
- nWhichP = np.digitize(
- DzMarch809.iloc[i][fieldActivePower], np.arange(0, PNum * 25, 25)) - 1
- nWhichV = np.digitize(
- DzMarch809.iloc[i][fieldWindSpeed], np.arange(0.125, VNum * 0.25, 0.25))
-
- # if (
- # (DzMarch809.iloc[i][fieldActivePower] < PRated * 0.75) and
- # (DzMarch809.iloc[i][fieldPitchAngle] > 0.5) or
- # (DzMarch809.iloc[i][fieldActivePower] < PRated * 0.85) and
- # (DzMarch809.iloc[i][fieldPitchAngle] > 1.5) or
- # (DzMarch809.iloc[i][fieldActivePower] < PRated * 0.9) and
- # (DzMarch809.iloc[i][fieldPitchAngle] > 2.5)
- # ):
- # continue
- if nWhichP < PNum and nWhichV < VNum:
- if BBoxRemove[nWhichP, nWhichV] == 1:
- DzMarch809Sel[i] = 1
- elif BBoxRemove[nWhichP, nWhichV] == 2:
- DzMarch809Sel[i] = 2
- elif BBoxRemove[nWhichP, nWhichV] == 3:
- DzMarch809Sel[i] = 0
- return DzMarch809Sel
- def identify_limit_load_data(DzMarch809: pd.DataFrame, DzMarch809Sel, PRated):
- nCounter1 = len(DzMarch809)
- PVLimit = np.zeros((nCounter1, 2))
- nLimitTotal = 0
- nWindowLength = 3
- PowerStd = 15 # 功率波动方差
- PowerLimitUp = PRated - 300
- PowerLimitLow = 5 # 200kW
- nWindowNum = nCounter1 // nWindowLength
- for i in range(nWindowNum):
- LimitWindow = DzMarch809.iloc[i * nWindowLength:(i + 1) * nWindowLength][fieldActivePower].values
-
- # 检查所有数据是否在 PowerLimitLow值~PowerLimitUp值范围内
- if not ((LimitWindow >= PowerLimitLow) & (LimitWindow <= PowerLimitUp)).all():
- continue
-
- """
- 限功率识别策略(参考):
- 1. 功率<额定功率(PRated)*0.75 and 叶片角度>0.5 ; 2. 功率<额定功率*0.85 and 叶片角度>1.5 ; 3. 功率<额定功率*0.9 and 叶片角度>2.5;
- 示例:
- 1. 功率<1100,and 叶片角度>0.5 ; 2. 功率<1250 and 叶片角度>1.5 ; 3. 功率<1400 and 叶片角度>2.5 ;
- """
- # 额外的限功率识别策略
- pitch_angle_window = DzMarch809.iloc[i * nWindowLength:(i + 1) * nWindowLength][fieldPitchAngle].values
- if (
- (DzMarch809.iloc[i * nWindowLength:(i + 1) * nWindowLength][fieldActivePower] < PRated * 0.75).any() and
- (pitch_angle_window > 0.5).any() or
- (DzMarch809.iloc[i * nWindowLength:(i + 1) * nWindowLength][fieldActivePower] < PRated * 0.85).any() and
- (pitch_angle_window > 1.5).any() or
- (DzMarch809.iloc[i * nWindowLength:(i + 1) * nWindowLength][fieldActivePower] < PRated * 0.9).any() and
- (pitch_angle_window > 2.5).any()
- ):
- # 标识限负荷数据
- DzMarch809Sel[i * nWindowLength:(i + 1) * nWindowLength] = 4
- UpLimit = LimitWindow[0] + PowerStd
- LowLimit = LimitWindow[0] - PowerStd
- # 检查所有数据是否在方差范围内
- if not ((LimitWindow[1:] >= LowLimit) & (LimitWindow[1:] <= UpLimit)).all():
- continue
- # 标识限负荷数据
- DzMarch809Sel[i * nWindowLength:(i + 1) * nWindowLength] = 4
- for j in range(nWindowLength):
- # 只提取功率和风速数据
- PVLimit[nLimitTotal] = DzMarch809.iloc[i * nWindowLength + j][[fieldWindSpeed, fieldActivePower]]
- nLimitTotal += 1
- PVLimit = PVLimit[:nLimitTotal] # 截取实际数据部分
- return PVLimit, DzMarch809Sel
- # 计算能量损失函数
- def calculate_energy_loss(DzMarch809Sel, DzMarch809, IdealCurve, PNum, BB, PRated, EPIdealTotalAAA, EPActualTotal):
- EPLostStopTotal = 0
- nStopTotal = 0
- for i in range(len(DzMarch809)):
- if DzMarch809.iloc[i][fieldActivePower] <= 0:
- nWhichBin = 0
- for m in range(BB - 1):
- if IdealCurve.iloc[m][fieldWindSpeed] < DzMarch809.iloc[i][fieldWindSpeed] <= IdealCurve.iloc[m + 1][fieldWindSpeed]:
- nWhichBin = m
- break
- if nWhichBin > 0:
- IdealPower = (DzMarch809.iloc[i][fieldWindSpeed] - IdealCurve.iloc[nWhichBin][fieldWindSpeed]) / (IdealCurve.iloc[nWhichBin + 1][fieldWindSpeed] - IdealCurve.iloc[nWhichBin]
- [fieldWindSpeed]) * (IdealCurve.iloc[nWhichBin + 1][fieldActivePower] - IdealCurve.iloc[nWhichBin][fieldActivePower]) + IdealCurve.iloc[nWhichBin][fieldActivePower]
- EPLostStopTotal += IdealPower / 6
- nStopTotal += 1
- EPLostBadTotal = 0
- EPLost = 0
- nBadTotal = 0
- for i in range(len(DzMarch809)):
- if DzMarch809Sel[i] == 1:
- nWhichBin = 0
- for m in range(BB - 1):
- if IdealCurve.iloc[m][fieldWindSpeed] < DzMarch809.iloc[i][fieldWindSpeed] <= IdealCurve.iloc[m + 1][fieldWindSpeed]:
- nWhichBin = m
- break
- if nWhichBin > 0:
- IdealPower = (DzMarch809.iloc[i][fieldWindSpeed] - IdealCurve.iloc[nWhichBin][fieldWindSpeed]) / (IdealCurve.iloc[nWhichBin + 1][fieldWindSpeed] - IdealCurve.iloc[nWhichBin]
- [fieldWindSpeed]) * (IdealCurve.iloc[nWhichBin + 1][fieldActivePower] - IdealCurve.iloc[nWhichBin][fieldActivePower]) + IdealCurve.iloc[nWhichBin][fieldActivePower]
- EPLost += abs(IdealPower -
- DzMarch809.iloc[i][fieldActivePower]) / 6
- EPLostBadTotal += EPLost
- nBadTotal += 1
- EPOverTotal = 0
- nOverTotal = 0
- for i in range(len(DzMarch809)):
- if DzMarch809Sel[i] == 3:
- EPOver = (DzMarch809.iloc[i][fieldActivePower] - PRated) / 6
- EPOverTotal += EPOver
- nOverTotal += 1
- EPLostPerformTotal = 0
- for i in range(len(DzMarch809)):
- nWhichBinI = 0
- for m in range(BB - 1):
- if IdealCurve.iloc[m][fieldWindSpeed] < DzMarch809.iloc[i][fieldWindSpeed] <= IdealCurve.iloc[m + 1][fieldWindSpeed]:
- nWhichBinI = m
- break
- if nWhichBinI > 0:
- IdealPower = (DzMarch809.iloc[i][fieldWindSpeed] - IdealCurve.iloc[nWhichBinI][fieldWindSpeed]) / (IdealCurve.iloc[nWhichBinI + 1][fieldWindSpeed] - IdealCurve.iloc[nWhichBinI]
- [fieldWindSpeed]) * (IdealCurve.iloc[nWhichBinI + 1][fieldActivePower] - IdealCurve.iloc[nWhichBinI][fieldActivePower]) + IdealCurve.iloc[nWhichBinI][fieldActivePower]
- EPLostPerformTotal += (IdealPower -
- DzMarch809.iloc[i][fieldActivePower]) / 6
- EPIdealTotal = EPActualTotal + EPLostStopTotal + EPLostBadTotal + \
- EPLostPerformTotal if EPLostPerformTotal >= 0 else EPActualTotal + \
- EPLostStopTotal + EPLostBadTotal
- RemoveOverEP = 0
- for i in range(len(DzMarch809)):
- if DzMarch809Sel[i] == 2:
- nWhichBin = 0
- for m in range(BB - 1):
- if IdealCurve.iloc[m][fieldWindSpeed] < DzMarch809.iloc[i][fieldWindSpeed] <= IdealCurve.iloc[m + 1][fieldWindSpeed]:
- nWhichBin = m
- break
- if nWhichBin > 0:
- IdealPower = (DzMarch809.iloc[i][fieldWindSpeed] - IdealCurve.iloc[nWhichBin][fieldWindSpeed]) / (IdealCurve.iloc[nWhichBin + 1][fieldWindSpeed] - IdealCurve.iloc[nWhichBin]
- [fieldWindSpeed]) * (IdealCurve.iloc[nWhichBin + 1][fieldActivePower] - IdealCurve.iloc[nWhichBin][fieldActivePower]) + IdealCurve.iloc[nWhichBin][fieldActivePower]
- RemoveOverEP += (DzMarch809.iloc[i]
- [fieldActivePower] - IdealPower) / 6
- for i in range(len(DzMarch809)):
- if DzMarch809.iloc[i][fieldActivePower] > PRated:
- RemoveOverEP += (DzMarch809.iloc[i][fieldActivePower] - PRated) / 6
- return EPLostStopTotal, EPLostBadTotal, EPOverTotal, EPLostPerformTotal, EPIdealTotal - RemoveOverEP
- # 计算实测功率曲线函数
- def calculate_measured_power_curve(PVDot, VRated, PRated):
- XBinNumber = np.ones(50)
- PCurve = np.zeros((50, 2))
- PCurve[:, 0] = np.arange(0.5, 25.5, 0.5)
- XBinSum = np.zeros((50, 2))
- for i in range(len(PVDot)):
- nWhichBin = 0
- for b in range(50):
- if (b * 0.5 - 0.25) < PVDot.iloc[i][fieldWindSpeed] <= (b * 0.5 + 0.25):
- nWhichBin = b
- break
- if nWhichBin > 0:
- XBinSum[nWhichBin, 0] += PVDot.iloc[i][fieldWindSpeed]
- XBinSum[nWhichBin, 1] += PVDot.iloc[i][fieldActivePower]
- XBinNumber[nWhichBin] += 1
- XBinNumber -= 1
- for b in range(50):
- if XBinNumber[b] > 0:
- PCurve[b, 0] = XBinSum[b, 0] / XBinNumber[b]
- PCurve[b, 1] = XBinSum[b, 1] / XBinNumber[b]
- VRatedNum = int(VRated / 0.5)
- for m in range(VRatedNum, 50):
- if PCurve[m, 1] == 0:
- PCurve[m, 1] = PRated
- return PCurve
- # 计算标准正则功率曲线函数
- def calculate_normalized_power_curve(IdealCurve, VRated, PRated):
- PCurveNorm = np.zeros((50, 2))
- VRatedNum = int(VRated / 0.5)
- # 15m/s以上为额定功率
- high_wind_speeds = np.arange(15, 25.5, 0.5)
- PCurveNorm[VRatedNum:VRatedNum+len(high_wind_speeds), 0] = high_wind_speeds
- PCurveNorm[VRatedNum:VRatedNum+len(high_wind_speeds), 1] = PRated
- # 15m/s以下正则功率曲线
- VSpeed = np.arange(0.5, 15.5, 0.5)
- CurveData = IdealCurve[IdealCurve[fieldWindSpeed]
- <= 15].to_numpy() # 提取风速<=15的数据
- for i, v in enumerate(VSpeed):
- if i < len(CurveData) - 1:
- # 插值计算
- x0, y0 = CurveData[i]
- x1, y1 = CurveData[i + 1]
- PCurveNorm[i, 0] = v
- PCurveNorm[i, 1] = y0 + (v - x0) * (y1 - y0) / (x1 - x0)
- else:
- PCurveNorm[i, 0] = v
- PCurveNorm[i, 1] = PRated # 防止超出范围的情况
- return PCurveNorm
- # 保存结果函数
- def save_results(PCurve, PCurveNorm, EPKW, EPPer, outputDir='./output/A01'):
- if not os.path.exists(outputDir):
- os.makedirs(outputDir)
- pd.DataFrame(PCurve, columns=[fieldWindSpeed, fieldActivePower]).to_csv(
- os.path.join(outputDir, 'PCurve.csv'), index=False)
- pd.DataFrame(PCurveNorm, columns=[fieldWindSpeed, fieldActivePower]).to_csv(
- os.path.join(outputDir, 'PCurveNorm.csv'), index=False)
- pd.DataFrame([EPKW], columns=['EPIdealTotal', 'EPActualTotal', 'EPLostStopTotal', 'EPLostBadLimitTotal', 'EPLostPerformTotal', 'EPLostBadTotal',
- 'EPLostLimitTotal', 'EPOverTotal', 'WindSpeedAvr', 'TurbineRunRate']).to_csv(os.path.join(outputDir, 'EPKW.csv'), index=False)
- pd.DataFrame([EPPer], columns=['Percent1', 'Percent2', 'Percent3', 'Percent4', 'Percent5', 'Percent6', 'Percent7',
- 'Percent8', 'WindSpeedAvr', 'TurbineRunRate']).to_csv(os.path.join(outputDir, 'EPPer.csv'), index=False)
- # 绘制结果函数,使用 Plotly
- def plot_results(PVBad, PVLimit, PVDot, PCurve, IdealCurve):
- fig = go.Figure()
- # 添加坏点数据
- if not PVBad.empty:
- fig.add_trace(go.Scatter(x=PVBad[fieldWindSpeed], y=PVBad[fieldActivePower],
- mode='markers', name='坏点', marker=dict(color='red')))
- # 添加限电点数据
- if not PVLimit.empty:
- fig.add_trace(go.Scatter(x=PVLimit[fieldWindSpeed], y=PVLimit[fieldActivePower],
- mode='markers', name='限功率', marker=dict(color='blue')))
- # 添加正常点数据
- if not PVDot.empty:
- fig.add_trace(go.Scatter(x=PVDot[fieldWindSpeed], y=PVDot[fieldActivePower],
- mode='markers', name='好点', marker=dict(color='black')))
- # 添加实测功率曲线
- if PCurve.shape[0] > 0:
- fig.add_trace(go.Scatter(
- x=PCurve[:, 0], y=PCurve[:, 1], mode='lines+markers', name='实测功率曲线', line=dict(color='green')))
- # 添加设计功率曲线
- if IdealCurve.shape[0] > 0:
- fig.add_trace(go.Scatter(x=IdealCurve[:, 0], y=IdealCurve[:, 1],
- mode='lines+markers', name='合同功率曲线', line=dict(color='yellow')))
- # 更新布局
- fig.update_layout(
- title={'text':'风力机功率散点数据分类标识','x':0.5},
- xaxis_title=fieldWindSpeed,
- yaxis_title=fieldActivePower,
- legend=dict(x=0.01, y=0.99),
- template='plotly_white'
- )
- # fig.show()
- outputDir = './output/A01'
- filePath = os.path.join(outputDir, f'power_scatter.html')
- fig.write_html(filePath)
- # 主函数
- def main():
- # 读取数据
- March809, IdealCurve = read_data()
- # 计算统计信息
- AA, BB, PNum, EPActualTotal, WindSpeedAvr, TurbineRunRate, EPIdealTotalAAA = calculate_statistics(
- March809, IdealCurve)
- # 分类数据
- DzMarch809, XBoxNumber = classify_data(March809, PNum, VNum)
- # 计算百分比
- PBoxPercent, VBoxPercent = compute_percentages(XBoxNumber, PNum, VNum)
- # 查找主带
- DotDense, DotDenseLeftRight, PowerLimit, WidthAverage, WidthVar, PowerBandWidth, PBoxMaxIndex = find_main_band(
- PBoxPercent, PNum, VNum, XBoxNumber)
- # 标记坏点
- DzMarch809Sel = mark_bad_points(
- DzMarch809, DotDenseLeftRight, PBoxMaxIndex, PowerLimit, PNum, VNum, XBoxNumber, PBoxPercent)
- # 标识限负荷数据点
- PVLimitArray, DzMarch809Sel = identify_limit_load_data(
- DzMarch809, DzMarch809Sel, PRated)
- # 初始化存储好点和坏点的 DataFrame
- PVBad = pd.DataFrame(columns=DzMarch809.columns)
- PVLimit = pd.DataFrame(columns=DzMarch809.columns)
- PVDot = pd.DataFrame(columns=DzMarch809.columns)
- # 存储好点和坏点
- for i in range(len(DzMarch809)):
- if DzMarch809Sel[i] in [1, 2, 3]:
- if not DzMarch809.iloc[[i]].isna().all().all():
- PVBad = pd.concat(
- [PVBad, DzMarch809.iloc[[i]]], ignore_index=True)
- elif DzMarch809Sel[i] == 4:
- if not DzMarch809.iloc[[i]].isna().all().all():
- PVLimit = pd.concat(
- [PVLimit, DzMarch809.iloc[[i]]], ignore_index=True)
- else:
- if not DzMarch809.iloc[[i]].isna().all().all():
- PVDot = pd.concat(
- [PVDot, DzMarch809.iloc[[i]]], ignore_index=True)
- # 计算能量损失
- EPLostStopTotal, EPLostBadTotal, EPOverTotal, EPLostPerformTotal, EPIdealTotal = calculate_energy_loss(
- DzMarch809Sel, DzMarch809, IdealCurve, PNum, BB, PRated, EPIdealTotalAAA, EPActualTotal)
- EPKW = [EPIdealTotal, EPActualTotal, EPLostStopTotal, EPLostBadTotal,
- EPLostPerformTotal, EPLostBadTotal, 0, EPOverTotal, WindSpeedAvr, TurbineRunRate]
- EPPer = [100, EPActualTotal / EPIdealTotal * 100, EPLostStopTotal / EPIdealTotal * 100, EPLostBadTotal / EPIdealTotal * 100,
- EPLostPerformTotal / EPIdealTotal * 100, EPLostBadTotal / EPIdealTotal * 100, 0, EPOverTotal / EPActualTotal * 100, WindSpeedAvr, TurbineRunRate]
- # 计算实测功率曲线
- PCurve = calculate_measured_power_curve(PVDot, VRated, PRated)
- # 计算标准正则功率曲线
- PCurveNorm = calculate_normalized_power_curve(IdealCurve, VRated, PRated)
- # 保存结果
- save_results(PCurve, PCurveNorm, EPKW, EPPer)
- # 绘制结果
- plot_results(PVBad, PVLimit, PVDot, PCurve, IdealCurve.to_numpy())
- if __name__ == "__main__":
- main()
|