Продолжаем цикл изучения открытых python-библиотек с разбора pandapower - с надстроенными над pandas функциями расчета электрических сетей.

Даже если вы раньше ни разу не имели дела с расчетом электрических сетей, с библиотекой pandapowerвы можете быстро войти-в-айти-начала-прошлого-века... Подробные комментарии прилагаются.

В предыдущей статье большой обзор открытых электрических библиотек на питоне.

Библиотека pandapowerсоздана группой специалистов в Университете Касселя, Германия, распространяется под лицензией BSD

pandapower предоставляет возможность расчетов и оптимизации потоков мощности,  оценку состояния сети и расчёт токов коротких замыканий согласно стандарту IEC 60909. Есть возможность моделирования постоянных токовых нагрузок, сетей с несколькими балансирующими узлами и проверки связности сети. С этой библиотекой вы быстро погрузитесь в мир расчета электрических сетей. Зачем? А почему бы и нет?

Библиотека pandapower на гитхабе

Документация библиотеки

Сайт проекта

Установка

Установим pandapowerс помощью pip install pandapower

Кроме того, должны быть установлены:

  • numpy

  • scipy

  • numba

  • matplotlib

1. Расчет простейшей сети

Рассмотрим простейшую сеть (пример на гитхабе), где есть:

  • Grid Connection - присоединение к внешней сети

  • Voltage Level 20 kV - шина напряжением 20 киловольт (шина - это проводник с низким сопротивлением, к которому могут присоединяться отдельные электрические цепи или устройства - подстанции, распредустройства и т.д.)

  • Transformer - Трансформатор 20 киловольт по верхней стороне / 0,4 кВ по нижней стороне, мощностью 400 кВА (киловольт-ампер, в этих величинах выражается полная мощность, в то время как ее часть, активная мощность, производящая работу, выражается в киловаттах, кВт)

  • Voltage Level 400 V - шина напряжением 0,4 киловольт

  • Line - линия длиной 100 метров, максимальный ток 142 А

  • Voltage Level 400 V - шина напряжением 0,4 киловольт

  • Load - нагрузка (потребитель электрической энергии) с активной мощностью 100 кВт (активная мощность выполняет полезную работу) и реактивной 50 кВАр (киловольт-ампер реактивной мощности, не выполняющей полезную работу).

Простейший участок сети с 3 шинами, потребителем, линией, трансформатором, присоединением к внешней сети
Простейший участок сети с 3 шинами, потребителем, линией, трансформатором, присоединением к внешней сети

Заметим, что на рисунке приведена так называемая однолинейная схема - то есть физические 3 фазных провода и нулевой провод обозначены одной линией. А шины, то есть места соединений, обозначены перпендикулярной утолщенной линией. Трансформатор, имеющий высоковольтную и низковольтную обмотки, обозначен, как вы поняли, двумя пересекающимися окружностями.

Вот код, создающий модель такой простейшей сети с комментариями:

import pandapower as pp

# Создадим пустую сеть, частотой 50 Гц (по умолчанию f_hz=50.0)
# и с заданием мощности в мегавольтамперах (по умолчанию sn_mva=1):
net = pp.create_empty_network()

# Создадим 3 шины, в сети net, где vn_kv - номминальное напряжение в кВ,
# c именем name="Bus 1" ... "Bus 3"
bus1 = pp.create_bus(net, vn_kv=20., name="Bus 1")
bus2 = pp.create_bus(net, vn_kv=0.4, name="Bus 2")
bus3 = pp.create_bus(net, vn_kv=0.4, name="Bus 3")

# Создадим присоединение к внешней сети, в сети net, 
# с именем name = "Grid Connection"
# присоединенную к шине 1 (bus=bus1)
# с коэффициентом напряжения в балансирующем узле 1,02
pp.create_ext_grid(net, bus=bus1, vm_pu=1.02, name="Grid Connection")

# Создадим нагрузку в сети net, с именем name="Load"
# с активной мощностью p_mw= 100 кВт, реактивной q_mvar 50 кВА
# (по умолчанию при создании сети sn_mva=1, 
# т.е. все в мегаваттах, мегавольтамперах, поэтому  p_mw=0.100)
pp.create_load(net, bus=bus3, p_mw=0.100, q_mvar=0.05, name="Load")

# Создадим трансформатор в сети net, с именем name="Trafo"
# стандартного типа (0,4 МВА мощностью 20/0,4 кВ) 
# std_type="0.4 MVA 20/0.4 kV" - данные подтягиваются из библиотеки,
# присоединенный верхней стороной к шине bus1 (hv_bus=bus1)
# и нижней стороной к шине bus2 (lv_bus=bus2)
trafo = pp.create_transformer(net, hv_bus=bus1, lv_bus=bus2, 
                              std_type="0.4 MVA 20/0.4 kV", name="Trafo")

# И, наконец, создадим линию в сети net, с именем name="Line"
# длиной 0,1 км (length_km=0.1)
# стандартного типа std_type="NAYY 4x50 SE" - это кабель из 4 алюминиевых
# жил сечением 50 мм2 каждая
# std_type="0.4 MVA 20/0.4 kV" - данные подтягиваются из библиотеки,
# присоединенной по направлению перетока мощности от шины bus2 (from_bus=bus2)
# к шине bus3 (to_bus=bus3)
line = pp.create_line(net, from_bus=bus2, to_bus=bus3, 
                      length_km=0.1, std_type="NAYY 4x50 SE", name="Line")

Созданная структура данных сети хранится в нескольких датафреймах pandas, их можно вызвать по имени сети net и соответствующему атрибуту:

# Датафрейм с данными всех шин сети net:
net.bus
Датафрейм с данными всех шин
Датафрейм с данными всех шин

Датафрейм шины содержит столбцы:

  • name -имя, которое мы задали;

  • vn_kv -номинальное напряжение в киловольтах;

  • type - тип шины. По умолчанию b, - busbar, что означает металлическая шина, шинопровод. (Есть еще “n” - node, узел, “m” - muff, муфта);

  • zone - регион/район сети для группировки шин, в данном случае мы его не задавали;

  • in_service - находится в рабочем состоянии (True) или в нерабочем (False).

Теперь выведем данные всех линий:

# Датафрейм с данными всех линий сети net:
net.line
Датафрейм с данными всех линий
Датафрейм с данными всех линий

Здесь мы видим одну линию с заданными нами:

  • именем (name),

  • стандартным типом проводника из встроенной библиотеки данных std_type = NAYY 4x50 SE, он выглядит вот так,

  • ссылками на индексы шин, откуда и куда идет линия (от 1 к 2, то есть от Bus 2 к Bus 3),

  • длиной length_km.

Остальные данные подтянула powerpandas исходя из заданного стандартного типа кабеля (std_type = NAYY 4x50 SE) - в противном случае нам пришлось бы задать их вручную, путем метода create_line_from_parameters() . Это:

Параметры линии:
  • r_ohm_per_km (float) - сопротивление линии в Ом на км;

  • x_ohm_per_km (float) - реактивное сопротивление линии в Ом на км;

  • c_nf_per_km (float) - емкость линии (линия-земля) в нанофарадах на км;

  • max_i_ka (float) - максимальный ток (ток термической стойкости, при превышении которого металл проводников начнет плавиться) в килоамперах;

  • type (str) — тип линии («ol» для воздушной линии или «cs» для кабельной линии);

  • parallel (integer) — количество параллельных линий;

  • in_service - находится в рабочем состоянии (True).

Датафрейм с данными всех трансформаторов:

# Датафрейм с данными всех трансформаторов сети net:
net.trafo

Как видим, у трансформатора намного больше параметров. Мы создавали один двухобмоточный трансформатор, поэтому и показаны данные одного трансформатора двухобмоточного.

Если трансформатор трёхобмоточный, то используем create_transformer3w() для создания и net.trafo3w (описание) для отображения датафрейма данных.

Итак, данные которые мы задали:

  • name - имя "Trafo"

  • std_type - "0.4 MVA 20/0.4 kV" - заданный стандартный тип, примерно такой

  • hv_bus = 0 -  шина, к которой подключена высокая сторона трансформатора

  • lv_bus = 1 - шина, к которой подключена низкая сторона трансформатора

Данные, которые были вычислены по заданному стандартному типу:

Рассмотрим остальные данные, если мы задаем параметры через create_transformer_from_parameters()

Обязательные данные:

  • sn_mva - номинальная мощность трансформатора, МВА (мегавольт-амперы);

  • vn_hv_kv - номинальное напряжение ВН (обмотки высокого напряжения);

  • vn_lv_kv - номинальное напряжение НН (обмотки низкого напряжения);

  • vk_percent - напряжение короткого замыкания,%

  • vkr_percent - активная составляющая напряжения короткого замыкания,%

  • pfe_kw - потери в железе (в проводящих частях), кВт,

  • i0_percent - ток холостого хода трансформатора, %,

    Необязательные данные:

  • shift_degree - угловой сдвиг трансформатора (необходим для расчёта потокораспределения)

  • tap_side (int) - расположение РПН трансформатора (это устройство для регулирования напряжения путем изменения количества витков обмотки под нагрузкой). «hv» — на ВН (на стороне высокого напряжения), «lv» — на НН (на стороне низкого напряжения);

  • tap_neutral (int) - положение РПН, при котором коэффициент трансформации равен отношению номинальных напряжений

  • tap_min (int) - минимально допустимое положение РПН

  • tap_max (int) - максимально допустимое положение РПН

  • tap_step_percent (float) - величина шага изменения для величины напряжения в процентах

  • tap_step_degree (float) - величина шага изменения для угла напряжения в градусах

  • tap_pos (int) - текущее положение РПН трансформатора. По умолчанию среднее положение (tap_neutral)

  • tap_phase_shifter (bool) - если True, РПН моделирует работу идеального фазорегулятора, то есть при каждом переключении ответвления изменяется фаза на постоянную величину шага

  • parallel - количество параллельных трансформаторов;

  • df (float) - коэффициент снижения: максимальный ток трансформатора по отношению к номинальному току трансформатора (от 0 до 1)

  • in_service - находится в рабочем состоянии (True).

Датафрейм с данными всех нагрузок / потребителей сети net:

# Выведем данные всех нагрузок
net.load

Здесь мы видим имя (load), индекс 2-й шины, к которой подключена нагрузка (bus), p_mv - активную мощность в мегаваттах и q_mvar - реактивную мощность в мегавольт-ампер реактивных,

а также еще несколько параметров:
  • const_z_percent - (float, по умолчанию 0) — доля нагрузки, характеризующаяся постоянным сопротивлением;

  • const_i_percent - (float, по умолчанию 0) — доля нагрузки, потребляющая постоянный ток;

  • sn_mva - (float, по умолчанию None) — номинальная мощность нагрузки, МВА;

  • scaling - (float, по умолчанию 1) — коэффициент масштабирования;

  • in_service - находится в рабочем состоянии (True).

  • type - тип нагрузки, 'wye'/'delta'

2. Расчет установившегося режима сети

Задав данные нашей сети и изучив их, запустим, наконец, её расчёт! Просто передадим в функцию runpp нашу сеть net.

# Запустим расчет без параметров

pp.runpp(net)

Установившийся режим — это режим работы энергосистемы, при котором параметры режи­ма могут приниматься неизменными.

Результаты расчета установившегося режима сети также будут в датафреймах и для каждого вида элементов сети они будут иметь префикс res_ :

Датафрейм результатов расчета для шин:

# Выведем результаты расчета для шин

net.res_bus

Мы видим рассчитанные p_mv - активную мощность в мегаваттах и q_mvar - реактивную мощность в мегавольт-амперах:

  • на шине Bus 1 (с индексом 0) мощность со знаком '-' - она входит из внешней сети

  • на шине Bus 2 (с индексом 1) (шина между трансформатором и линией) - здесь мощность не потребляется, входящая на шину равна исходящей

  • на шине Bus 3 (с индексом 2) мощность равна мощности нагрузки, она "выходит" из шины на нагрузку, поэтому с противоположным шине 1 знаком

И еще 2 параметра для шин:
  • vm_pu - напряжение на шине, в относительных единицах (заметно, что от внешней сети оно постепенно снижается, поскольку трансформатор и сеть потребляют мощность на потери)

  • va_degree - угол напряжения в градусах

Датафрейм результатов расчета для линий:

# Выведем результаты расчета для линий

net.res_line
Что мы видим в результатах расчета линии:
  • p_from_mw и p_to_mw - активная мощность, входящая в линию и исходящая (на нагрузку), МВт

  • q_from_mvar и q_to_mvar - реактивная мощность, входящая в линию и исходящая (на нагрузку), МВАр

  • pl_mw и ql_mvar - активные и реактивные потери мощности на нагрузке

  • i_from_ka, i_ka и i_to_ka - ток в килоамперах на входе в линию, в линии и на выходе на нагрузку

  • vm_from_pu и vm_to_pu - относительное напряжение (относительно номинального) на входе в линию и на выходе на нагрузку

  • loading_percent - процент загрузки линии

Датафрейм результатов расчета для трансформаторов:

# Выведем результаты расчета для трансформаторов

net.res_trafo
Что мы видим в результатах расчета трансформатора:
  • p_hv_mw и q_hv_mvar - активная и реактивная мощность на высокой стороне

  • p_lv_mw и q_lv_mvar - активная и реактивная мощность на низкой стороне

  • pl_mw и ql_mvar - потери мощности в трансформаторе

  • i_hv_ka и i_lv_ka - ток на высокой и низкой стороне в килоамперах

  • vm_hv_pu и vm_lv_pu - относительное (относительно номинального) напряжение на высокой и низкой стороне

  • loading_percent - процент загрузки линии

Датафрейм результатов расчета для нагрузки и присоединения к внешней сети:

# Выведем результаты расчета для нагрузки

net.res_load
# Выведем результаты расчета для присоединения к внешней сети

net.res_ext_grid

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

Изменяем положение переключателя обмоток трансформатора

В трансформаторе есть возможность изменять количество витков, чтобы поддерживать нормальный уровень напряжения у потребителей.

# Теперь мы изменяем положение переключателя обмоток с положения 0 на -1 
net.trafo.tap_pos.at[trafo] = -1

# и запускаем заново расчет сети:
pp.runpp(net)

Посмотрим на результаты, и увидим, что напряжение шины на стороне низкого напряжения трансформатора увеличилось с 0,964 до 0,992 номинального:

net.res_bus

А вот потребляемая из внешней сети мощность немного уменьшилась:

net.res_ext_grid

Вау! Мы только что оптимизировали работу нашей сети!

3. Поработаем рубильником!

Наверное, у вас уже возникал вопрос: а где же здесь рубильник? Давайте его создадим, причем в разомкнутом состоянии, отключив нашу нагрузку!

# Создадим переключатель, указав нашу сеть, место (до шины нагрузки)
# closed=False означает, что переключатель разомкнул сеть (у включенного True)
# element=line - индекс элемента, в данном случае - линии;
# et: «l» — выключатель между шиной и линией, 
#     «t» — выключатель между шиной и трансформатором, 
#     «t3» — выключатель между шиной и 3-х обмоточным трансформатором, 
#     «b» — выключатель между двумя шинами

pp.create_switch(net, bus=bus3, element=line, et="l", closed=False)
Так теперь выглядит схема нашей сети. Квадратиком обозначен выключатель. Поскольку он разомкнут, то всё, что после него (шина 3 и нагрузка) обозначены серым.
Так теперь выглядит схема нашей сети. Квадратиком обозначен выключатель. Поскольку он разомкнут, то всё, что после него (шина 3 и нагрузка) обозначены серым.

Разомкнутый выключатель отключает шину нагрузки от источника питания. Это можно проверить, запустив расчет потоков мощности и проверив результаты. Напряжение на шине с индексом 2 показано как NaN, то есть отсутствует:

pp.runpp(net) 
net.res_bus

Нагрузка/мощность не подается на потребителя:

net.res_load
Нагрузка отключена и не потребляет мощность
Нагрузка отключена и не потребляет мощность

И линия находится в разомкнутом режиме:

net.res_line
Мощность и ток в линии отсутствуют, но линия остаётся под напряжением (vm_from_pu 1.046 от номинального!)
Мощность и ток в линии отсутствуют, но линия остаётся под напряжением (vm_from_pu 1.046 от номинального!)

4. Топологический анализ сети

Структуру сети можно анализировать с помощью пакета топологии. Он использует интерфейс библиотеки NetworkX для поиска по графу. Есть несколько предустановленных алгоритмов поиска, например, поиск не нагруженных шин:

# Загрузим пакет топологии
import pandapower.topology as top

# Выведем множество шин, которые не под нагрузкой:
top.unsupplied_buses(net)
{2}

В данном случае это шина с индексом 2 (Bus 3) - мы же её отключили!

Когда мы замыкаем переключатель, не запитанных шин больше нет:

# Когда мы замыкаем переключатель, не запитанных шин больше нет:
net.switch.closed.at[0] = True

top.unsupplied_buses(net)
set()

Помимо предопределенных функций поиска, также можно преобразовать сеть pandapower в граф NetworkX и выполнять поиск непосредственно на этом графе.

Предположим, мы хотим найти все шины с тем же уровнем напряжения, что и шина нагрузки. Мы переводим сеть в граф, но исключая трансформаторы:

# Переводим сеть в граф, но исключая трансформаторы
mg = top.create_nxgraph(net, include_trafos=False)

print(mg)
MultiGraph with 3 nodes and 1 edges
# Найдем все шины, подключенные к шине нагрузки, на этом графе:
list(top.connected_component(mg, 2))
[2, 1]

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

Кроме того, с помощью пакета топологии можно найти:

calc_distance_to_bus(net, bus, respect_switches=True, nogobuses=None, notravbuses=None)- вычисляет кратчайшее расстояние между исходной шиной и всеми подключенными к ней шинами

connected_component(mg, bus, notravbuses=[]) - находит все шины в графе NetworkX, которые подключены к определенной шине

connected_components(mg, notravbuses={}) - кластеризирует все шины в графе NetworkX, которые соединены друг с другом

Подробнее здесь

5. Расчет короткого замыкания

Pandapower включает модуль короткого замыкания, соответствующий стандарту IEC 60909. Чтобы выполнить анализ короткого замыкания, нам необходимо определить параметры короткого замыкания для внешней сети:

# Определим максимальную мощность короткого замыкания [МВА]
net.ext_grid["s_sc_max_mva"] = 100

# Максимальное отношение R/X (активного сопротивления к реактивному) 
# импеданса короткого замыкания
# Импеданс является всеобъемлющим выражением всех видов сопротивлений 
# потоку электронов (включая активное и реактивное сопротивления) 
net.ext_grid["rx_max"] = 0.1

Теперь мы можем рассчитать короткие замыкания. Здесь мы рассчитываем максимальный (case="max") ток при замыкании трех фаз (по умолчанию аргумент fault='3ph'). Возможно также “2ph” - замыкание фазы на фазу и “1ph” - замыкание фазы на землю.

Сопротивление короткого замыкания в расчете - 2 Ом (r_fault_ohm=2.).

Еслиip=True, то выполняется расчёт апериодической составляющей тока короткого замыкания (она затухает по экспоненциальному закону). Есть еще периодическая составляющая тока КЗ.

# Импортируем специальную библиотеку для расчета КЗ:
import pandapower.shortcircuit as sc

sc.calc_sc(net, case="max", ip=True, r_fault_ohm=2.)
# Выведем начальные и пиковые токи короткого замыкания 
# для повреждений на всех шинах:

net.res_bus_sc
Какие параметры мы видим при расчете КЗ:
  • ikss_ka — расчётный ток короткого замыкания в килоамперах (кА)

  • skss_mw - максимальная мощность короткого замыкания в мегавольтамперах (МВА)

  • ip_ka - расчётное значение апериодической составляющей в килоамперах (кА)

  • rk_ohm, xk_ohm - активное и реактивное сопротивление КЗ

Кроме net.res_bus_sc можно вызвать net.res_line_sc — таблицу с результатами расчёта по линиям электропередачи и net.res_trafo_sc — таблицу с результатами расчёта по трансформаторам.

6. Встроенные примеры сетей

pandapower содержит несколько встроенных примеров сети - вам даже не нужно задавать все элементы вручную:

# Импортируем модуль с экземлярами предустановленных сетей:
import pandapower.networks

# Выберем простейшую сеть, содержащую генератор:
net = pandapower.networks.example_simple()
pandapower.networks.example_simple() загружает нам вот такую сеть с внешним подключением, генератором и нагрузкой.
pandapower.networks.example_simple() загружает нам вот такую сеть с внешним подключением, генератором и нагрузкой.

Здесь можно посмотреть код создания данной сети вручную со всеми параметрами.

А код net = pandapower.networks.example_multivoltage() создаст нам сложную сеть с ветрогенератором, балансирующей его газовой турбиной, подключением ко внешней сети, с 4 уровнями напряжения (110 кВ, 20 кВ, 10 кВ и 0,4 кВ) и с массой нагрузок... Вот ссылка на пример.

7. Дополнительные возможности

Мы рассмотрели несколько элементов сети, но в pandapowerих существенно больше - вот весь список.

Более того, некоторые типовые элементы (линии, трансформаторы) можно брать из библиотеки, либо создать свою библиотеку типовых элементов сети.

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

pandapower плотно интегрирован как с pandas, что позволяет использовать ее богатые возможности для обработки наших данных, так и с NetworkX - библиотекой для построения и анализа графов. Поэтому сеть можно представить в виде графа и использовать богатые возможности NetworkX.

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

Также есть возможность рассчитать целую временную серию параметров (изменение параметров сети во времени). Подробнее о временных сериях.

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

Комментарии (7)