Source code for Topology_BusBranch

"""
This module contains definitions of classes to represent an object oriented structure of a Matpower case file.

.. note::This module is based mostly on the the started but never finished OpenSource project Cim2BusBranch https://www.versioneye.com/Python/Cim2BusBranch/0.1

.. seealso::  :py:mod:`Topology_NodeBreaker` and :py:mod:`CIM2Matpower`

:Date: 2016-05-10
:Authors: Konstantin Gerasimov
:e-mail: kkgerasimov@gmail.com
:Credits: This function is created for KU-Leuven as part of the GARPUR project http://www.garpur-project.eu
"""


import collections
from itertools import count

import logging
logger = logging.getLogger(__name__)
# logger = logging.getLogger('CIM2Matpower')

bus_type = collections.namedtuple('BusType', 'PQ, PV, REF, ISOLATED')(
    PQ=1,
    PV=2,
    REF=3,
    ISOLATED=4,
)
"""Defines the Matpower BUS_TYPE constants"""

Point = collections.namedtuple('Point', 'x, y')


[docs]class Bus(object): """ A *Bus* represents a node in a bus/branch network graph. """ id_generator = count(1) def __init__(self, name='', btype=bus_type.PQ, pd=0., qd=0., base_kv=100., vm=1., va=0., vm_max=1.1, vm_min=0.9, gs=0., bs=0., area=1, zone=1,): # cim_classes=None, pos=None): self.id = next(self.id_generator) """Autogenerated ID (subsequent numbering, starting from 1)""" self.name = name if name else str(id(self)) """Bus name""" self.btype = btype """Bus type (One of these attributes of :obj:`bus_type`: *PQ*, *PV*, *REF*, *ISOLATED*)""" self.pd = pd """Active power demand (MW)""" self.qd = qd """Reactive power demand (MVAr)""" self.base_kv = base_kv """Base voltage (kV)""" self.vm = vm """Voltage magnitude (p.u.)""" self.va = va """Voltage angle (degrees)""" self.vm_max = vm_max """maximum voltage magnitude (p.u.)""" self.vm_min = vm_min """minimum voltage magnitude (p.u.)""" self.gs = gs """Shunt conductance (MVAr injected at V = 1. p.u.)""" self.bs = bs """Shunt susceptance (MVAr injected at V = 1. p.u.)""" self.area = area """Area number (positive integer)""" self.zone = zone """Loss zone (positive integer)""" # self.cim_classes = cim_classes if cim_classes else [] # """Classes of CIM components that were connected to this bus.""" # # self.pos = pos # """Set of position points (x, y) of that bus.""" self._pd_orig = pd self._qd_orig = qd @property def pd_orig(self): """The value of ``self.pd`` when the instance was created.""" return self._pd_orig @property def qd_orig(self): """The value of ``self.qd`` when the instance was created.""" return self._qd_orig
[docs] def reset(self): """ Resets ``self.pd`` and ``self.qd`` to their original values (that they had when the instance was created). """ self.pd = self._pd_orig self.qd = self._qd_orig
def __repr__(self): return ('%s(name=%-6s, btype=%d, pd=%7.3f, qd=%7.3f, ' 'vm=%.3f, va=%+.3f)') % ( type(self).__name__, "'%s'" % self.name, self.btype, self.pd, self.qd, self.vm, self.va, )
[docs]class Generator(object): """ A *Generator* can be attached to a :class:`Bus`. """ def __init__(self, bus, name='', pg=0., qg=0., pg_min=0., pg_max=0., qg_min=0., qg_max=0., vg=1., base_mva=0., online=True, pc1=0., pc2=0., qc1_min=0., qc1_max=0., qc2_min=0., qc2_max=0., ramp_agc=0., ramp_10=0., ramp_30=0., ramp_q=0., apf=0.,): self.bus = bus """Reference to the bus the generator is connected to""" self.name = name if name else str(id(self)) """Generator name""" self.pg = pg """Real power output (MW)""" self.qg = qg """Reactive power output (MVAr)""" self.pg_min = pg_min """Minimum real power output (MW)""" # Only raise an error if pg_max was provided by the user if 0.0 < pg_max < pg: raise ValueError('Generator %s: pg_max must be >= pg, but %f < %f' % (self.name,pg_max, pg)) self.pg_max = max(pg_max, pg) # enforce pg_max >= pg """Maximum real power output (MW)""" self.qg_min = qg_min """Minimum reactive power output at Pc1 (MVAr)""" # Only raise an error if qg_max was provided by the user if 0.0 < qg_max < qg: # raise ValueError('Generator %s: qg_max must be >= qg, but %f < %f' % # (self.name,qg_max, qg)) logger.warn('Generator %s: qg_max must be >= qg, but %f < %f', self.name, qg_max, qg) self.qg_max = max(qg_max, qg) # enforce qg_max >= qg """Maximum reactive power output at Pc1 (MVAr)""" self.vg = vg """Voltage magnitude setpoint (p.u.)""" self.base_mva = base_mva """Total MVA base of this machine, defaults to baseMVA""" self.online = online """Status (machine in service (True) or out of service (False))""" self.pc1 = pc1 """Lower real power output of PQ capability curve (MW)""" self.pc2 = pc2 """Upper real power output of PQ capability curve (MW)""" self.qc1_min = qc1_min """Minimum reactive power output at Pc1 (MVAr)""" self.qc1_max = qc1_max """Maximum reactive power output at Pc1 (MVAr)""" self.qc2_min = qc2_min """Minimum reactive power output at Pc2 (MVAr)""" self.qc2_max = qc2_max """Maximum reactive power output at Pc2 (MVAr)""" self.ramp_agc = ramp_agc """Ramp rate for load following/AGC (MW/min)""" self.ramp_10 = ramp_10 """Ramp rate for 10 minute reserves (MW)""" self.ramp_30 = ramp_30 """Ramp rate for 30 minute reserves (MW)""" self.ramp_q = ramp_q """Ramp rate for reactive power (2 sec timescale) (MVAr/min)""" self.apf = apf """APF, area participation factor""" self._pg_orig = pg self._qg_orig = qg @property def pg_orig(self): """The value of ``self.pg`` when the instance was created.""" return self._pg_orig @property def qg_orig(self): """The value of ``self.qg`` when the instance was created.""" return self._qg_orig
[docs] def reset(self): """ Resets ``self.pg`` and ``self.qg`` to their original values (that they had when the instance was created). """ self.pg = self._pg_orig self.qg = self._qg_orig
def __repr__(self): return ('%s(name=%-6s, bus=%-6s, pg=%7.3f, qg=%7.3f)') % ( type(self).__name__, "'%s'" % self.name, "'%s'" % self.bus.name, self.pg, self.qg, )
[docs]class Branch(object): """ A *Branch* represents a vertex in a Bus/Branch network. """ def __init__(self, from_bus, to_bus, name='', r=0., x=0., b=0., rate_a=0., rate_b=0., rate_c=0., ratio=0., angle=0., angle_min=-360., angle_max=360., online=True, p_from=0., q_from=0., p_to=0., q_to=0.): self.from_bus = from_bus """Reference to the bus on the from-side""" self.to_bus = to_bus """Reference to the bus on the to-side""" self.name = name if name else str(id(self)) """Branch name""" self.r = r """Resistance (p.u.)""" self.x = x """Reactance (p.u.)""" self.b = b """Total line charging susceptance (p.u.)""" self.rate_a = rate_a """MVA rating A (long term rating)""" self.rate_b = rate_b """MVA rating B (short term rating)""" self.rate_c = rate_c """MVA rating C (emergency rating)""" self.ratio = ratio """Transformer off nominal turns ratio (=0 for lines)""" self.angle = angle """Transformer phase shift angle (degree, positive => delay)""" self.angle_min = angle_min """Minimum angle difference between both ends (from_bus.va - to_bus.va) (degree)""" self.angle_max = angle_max """Maximum angle difference between both ends (from_bus.va - to_bus.va) (degree)""" self.online = online """Initial branch status (in service (True) or out of service (False))""" self.p_from = p_from """Real power injected at "from" bus end (MW)""" self.q_from = q_from """Reactive power injected at "from" bus end (MVAr)""" self.p_to = p_to """Real power injected at "to" bus end (MW)""" self.q_to = q_to """Reactive power injected at "to" bus end (MVAr)""" def __repr__(self): return ('%s(name=%-6s, from_bus=%-6s, to_bus=%-6s, p_from=%+8.3f, ' 'q_from=%+8.3f, p_to=%+8.3f, q_to=%+8.3f)') % ( type(self).__name__, "'%s'" % self.name, "'%s'" % self.from_bus.name, "'%s'" % self.to_bus.name, self.p_from, self.q_from, self.p_to, self.q_to, )
[docs]class Case(object): """ *Case* is a container for :class:`Bus`es, :class:`Generator`s and :class:`Branch`es. It also holds the global *base_mva* attribute for the case. """ def __init__(self, base_mva, buses, generators, branches): self.base_mva = base_mva self.buses = buses self.generators = generators self.branches = branches self.bus_ids = {} # Gets updated whenever a case is created
[docs] def reset(self): """ Resets all buses and generators. """ for item in self.buses + self.generators: item.reset()
def __repr__(self): lists = [self.buses, self.generators, self.branches] reprs = ['\n'.join([' %s,' % i for i in li]) for li in lists] reprs = ['[\n%s\n]' % rep for rep in reprs] return ('<%s object \n base_mva=%s,\n buses=%s,\n generators=%s,\n' ' branches=%s>') % ( type(self).__name__, self.base_mva, reprs[0], reprs[1], reprs[2], )