Расчёт установившегося режима со случайным изменением параметров

Материал из Wiki Power System
Перейти к: навигация, поиск

Данная статья посвящена описанию примера реализации метода Монте-Карло для оценки статистических законов изменения параметров установившегося режима, при случайной вариации исходных данных, по заранее заданному закону.

Описание расчётов

Значения перетоков активной мощности используются для соблюдения требований к устойчивости энергосистем при планировании и управлении электроэнергетическим режимом. Величины перетоков активной мощности зависят как от мощностей нагрузок в узлах сети, так и от параметров электрической сети. При создании математической модели исследуемой сети используется ряд допущений, например, мощность нагрузки задается конкретным числом на отдельных интервалах времени, хотя в действительности она не является постоянной величиной и может быть представлена некоторым интервалом, внутри которого находятся фактические значения. Параметры сети так же изменяются во времени под влиянием различных факторов, и их задание в виде постоянных величин вводит погрешность в оценку параметров режима работы исследуемой сети. В частности, величина предельного перетока обратно пропорциональна сопротивлению рассматриваемой линии электропередач.

Оценка влияния колебаний нагрузок и изменения сопротивлений линий на изменение величины предельного перетока, а также оценка интервала его возможных значений может представлять интерес для дальнейшего использования величины предельного перетока в инженерных расчётах.

В настоящей статье предлагается применить на практике метод Монте Карло (метод статических испытаний). Это численный метод, основанный на моделировании случайных величин и построении статистических оценок для искомых величин. Изменения нагрузки энергосистемы представляют собой суммирование изменений нагрузок отдельных электроприемников. Основное допущение состоит в том, что все одиночные элетроприемники подключаются и отключаются независимо друг от друга. Как известно, независимое сложение достаточно большого числа случайных слагаемых приводит к нормальному распределению вероятности их суммы [1].

Описание мощностей нагрузок нормальным распределением даёт возможность решения задач достоверизации и обобщения результатов косвенной статистической оценки вероятностных характеристик колебаний небаланса мощности энергосистем и обменной мощности их объединения.

Предлагаемый метод основан на проведении серий расчётов установившихся режимов при случайном изменении величин мощностей нагрузок, при случайном изменении величин сопротивлений линий или при сочетании этих изменений. С помощью численного метода моделируется вероятностный характер заданных величин. В этом случае случайная величина (мощность нагрузки каждого узла, сопротивление линии или их сочетание) задается законом распределения, а именно возможные значения случайной величины сопоставляются с вероятностями их появления. В качестве диапазона вариации параметров можно ориентироваться на величины приведённые здесь.

Расчёты установившихся режимов проводятся с использованием библиотеки pandapower на языке Python.

Импорт необходимых для расчёта библиотек

import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import copy

import pandas as pd
import pandapower as pp
import pandapower.networks as pn

#Настройки отрисовки графиков
sns.set(style="whitegrid")
plt.rcParams['figure.figsize'] = (14, 6)

Функция для случайного изменения одного параметра

def calcRandomSingleParam_Line(stbNet,rndNet,sigma,nameParam):
    #Функция, которая вносит случайные возмущения в параметры ЛЭП, по нормальному закону
    calcTableParam(stbNet.line,rndNet.line,sigma,nameParam)
def calcRandomSingleParam_Load(stbNet,rndNet,sigma,nameParam):
    #Функция, которая вносит случайные возмущения в параметры нагрузки, по нормальному закону
    calcTableParam(stbNet.load,rndNet.load,sigma,nameParam)
def calcRandomSingleParam_extGrid(stbNet,rndNet,sigma,nameParam):
    #Функция, которая вносит случайные возмущения в параметры внешней сети, по нормальному закону
    calcTableParam(stbNet.ext_grid,rndNet.ext_grid,sigma,nameParam)
def calcTableParam(stbTable,rndTable,sigma,nameParam):
    j=0
    for r in stbTable[nameParam]:        
        #Генерируем случайный, нормально распределённый параметр
        k=np.random.normal(r, sigma*r)
        rndTable[nameParam].iloc[j]=k
        j=j+1

Функция для расчёта единичного установившегося режима

def calcSingleStadyState(inNet,copyNet,lineCases,loadCases,extGridCases):
    #Функция выполняет расчёт одного режима со случайными парамтерами        
    #В переменной lineCases содердится информация о случайно изменяемых параметрах
    
    #Расчёт случайных изменений парметров схемы
    for sigma, nameParam in lineCases:
        calcRandomSingleParam_Line(inNet,copyNet,sigma,nameParam)
    for sigma, nameParam in loadCases:
        calcRandomSingleParam_Load(inNet,copyNet,sigma,nameParam)
    for sigma, nameParam in extGridCases:
        calcRandomSingleParam_extGrid(inNet,copyNet,sigma,nameParam)
    #Расчёт режима
    calcRgm = 1
    try:
        pp.runpp(net=copyNet, max_iteration=100)
    except Exception:
        calcRgm=0
    return calcRgm,copyNet

Функция расчёта множества режимов со случайными параметрами

def calcManyStadyState(inNet,nameinNet,lineCases,loadCases,extGridCases,runs):
    #Результаты расчётов по узлам
    col = ['numRun','numBus','vm_pu', 'va_degree', 'p_kw', 'q_kvar']
    resBus = pd.DataFrame(columns=col)
    col = ['numRun','numLine','p_from_kw', 'q_from_kvar', 'p_to_kw', 'q_to_kvar', 'pl_kw', 'ql_kvar',
           'i_from_ka', 'i_to_ka', 'i_ka', 'loading_percent']
    resLine = pd.DataFrame(columns=col) 

    #Массив номеров узлов
    numBuses= [i for i in range(0,inNet.bus.iloc[:,0].size)]
    #Массив номеров линий
    numLines= [i for i in range(0,inNet.line.iloc[:,0].size)]
    
    #Количество успешных расчётов режима
    rgm = 0
    #Копирование исходной сети в новую область памяти, для того чтобы не изменять исходную сеть.
    #В исходной сети будут храниться мат. ожидания параметров
    copyInNet = copy.deepcopy(inNet)
    for i in range(runs):
        calcRgm, copyInNet = calcSingleStadyState(inNet,copyInNet,lineCases,loadCases,extGridCases)
        if calcRgm==1:
            rgm += 1
            #Сохранение результатов расчётов
            #По узлам
            copyInNet.res_bus['numRun']=i
            copyInNet.res_bus['numBus']=numBuses
            resBus= pd.merge(resBus, copyInNet.res_bus,  how = 'outer')
            #По линиям
            copyInNet.res_line['numRun']=i
            copyInNet.res_line['numLine']=numLines
            resLine= pd.merge(resLine, copyInNet.res_line,  how = 'outer')
    #Отчёт о проделанной работе
    print('Отчёт по результатам расчётов сети ',nameNet,'.')
    print ('   - Режимов успешно рассчитано -',str(rgm), 'из',str(runs),'расчётов.')
    return resBus, resLine

Скрипт для инициализации и запуска расчётов

#Рассматриваемые сети
netCases = [
    (pn.case14(),'14 IEEE'),
    (pn.case33bw(),'33bw IEEE'),
    (pn.case118(),'118 IEEE'),
]

#Случайно изменяемые параметры в таблице ветви
#(дисперсия задаётся как доля от мат. ожидания параметра (при 1 - дисперсия = 100%), названием параметра)
lineCases = [
        (0.05,'r_ohm_per_km'),
        (0.05,'x_ohm_per_km'),        
    ]
#Изменения в узлах нагрузки
loadCases = [
        (0.05,'const_i_percent'),
        (0.05,'const_z_percent'),        
    ]
#Колебания внешней сети
extGridCases = [
        (0.01,'vm_pu'),        
    ]
#Количество расчётов
runs = 10

#Создание шаблона таблицы для хранения результатов
allResBus = pd.DataFrame(columns=['Сеть','numRun','numBus',
                               'vm_pu', 'va_degree', 'p_kw', 'q_kvar'])
allResLine = pd.DataFrame(columns=['Сеть','numRun','numLine',
                                'p_from_kw', 'q_from_kvar', 'p_to_kw', 'q_to_kvar', 'pl_kw', 'ql_kvar',
                                'i_from_ka', 'i_to_ka', 'i_ka', 'loading_percent'])
#Расчёты режимов
for net, nameNet in netCases:
    resBus, resLine = calcManyStadyState(net,nameNet,lineCases,loadCases,extGridCases,runs)
    resBus['Сеть']=nameNet
    resLine['Сеть']=nameNet
    allResBus = pd.merge(allResBus, resBus,  how = 'outer')
    allResLine = pd.merge(allResLine, resLine,  how = 'outer')

Результаты расчётов

Были получены результаты перетоков активной и реактивной мощности линии для каждого успешно рассчитанного установившегося режима (без нарушения статической устойчивости). По полученным значениям мощностей были построены их распределения и определены диапазоны изменения.

Для тестовой схемы 14 IEEE, для случая 1 000 расчётов установившегося режима, результаты представлены на графиках.

  • Результаты расчёта напряжений при случайном изменении параметров схемы. Показаны законы распределения 10 и 11 узла. 14 IEEE.
  • Результаты расчёта напряжений при случайном изменении параметров схемы. Все узлы схемы 14 IEEE.
  • Результаты расчёта напряжений при случайном изменении параметров схемы. Показаны законы распределения 10 и 11 узла. 33bw IEEE.
  • Результаты расчёта напряжений при случайном изменении параметров схемы. Все узлы схемы 33bw IEEE.

Графики распределений напряжений для 10 и 11 узла построены с помощью следующего скрипта.

sns.set(style="white", palette="muted", color_codes=True)
# Настройки построения графиков
f, axes = plt.subplots(1, 2, figsize=(7, 4), sharex=True)
sns.despine(left=True)

sns.distplot(allResBus[(allResBus['Сеть']=='14 IEEE')&(allResBus['numBus']==10)][['vm_pu']], color="b", ax=axes[ 0])
sns.distplot(allResBus[(allResBus['Сеть']=='14 IEEE')&(allResBus['numBus']==11)][['vm_pu']], color="r", ax=axes[ 1])

#Подписи по осям
plt.setp(axes[0], xlabel='Узел 10 - V,о.е.')
plt.setp(axes[1], xlabel='Узел 11 - V,о.е.')

plt.setp(axes, title="14 IEEE")
plt.setp(axes, yticks=[])
plt.tight_layout()
path = "" #Путь для сохранения файлов
f.savefig(path + 'dist.jpg', dpi=300)

Чтобы посмотреть результаты расчёта напряжений во всех узлах тестовой 14 IEEE схемы можно воспользоваться графиком "ящик с усами"

plt.rcParams['figure.figsize'] = (14, 6)
g=sns.boxplot(x="numBus", y="vm_pu",
            palette=["m", "g"],
            data=allResBus[allResBus['Сеть']=='14 IEEE'])
#Подписи по осям         
plt.xlabel("Номер узла")
plt.ylabel("V, о.е.")
#Отображение сетки на графике
plt.grid(b=True, which='major')
plt.grid(b=True, which='minor')
#Сохранение графика
path = "" #Путь для сохранения файлов
plt.savefig(path + 'boxPlot.jpg', dpi=400)

Файлы для скачивания

Файл со скриптом для Jupiter NoteBook.

Использованные источники

  1. Kolmogorov A.N. Sur la loi forte des grands nombres / A.N. Kolmogorov // C. R. Acad. Sci. Paris. - 1930. - Vol. 191. - P.910-912.