Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Учебники / 0841558_16EA1_federico_milano_power_system_modelling_and_scripting

.pdf
Скачиваний:
82
Добавлен:
08.06.2015
Размер:
6.72 Mб
Скачать

9.2 Devices as Classes

239

system.DAE.nx

+= 1

for item in self. algebs:

self. dict [item][var] = system.DAE.ny system.DAE.ny += 1

Where the scalars system.DAE.ny and system.DAE.nx indicate the number of total algebraic and state variables, respectively. A slightly di erent approach is required for assigning the indexes of ac voltage magnitudes vh

and phases θh as well as of equations gp,hi and gq,hi. The indexes of these variables and equations are assigned by the device system.Bus. Thus, a

device connected to a certain bus has to retrieve the indexes of vh and phases θh from system.Bus itself. This is easily obtained by providing to the device the indexes of the buses to which are connected.

def bus index(self):

for index in self. bus.keys():

for item in self. dict [index]:

if not system.Bus.int(has key(idx): self.message(’Bus index <%s> does not exist’,

data tuple=item, level=self.ERROR)

else:

idx = system.Bus.int[item]

self. dict [self. bus[index][0]].append(system.Bus.a[idx]) self. dict [self. bus[index][1]].append(system.Bus.v[idx])

For example, if self. bus = {’bus’:[’a’, ’v’]}, the previous code assigns to the attributes self.a and self.v the elements self.bus of system.Bus.a and system.Bus.v, respectively. Another post-parsing operation consists in converting to CVXOPT arrays all parameters of the list param, as follows:

def list2matrix(self):

for item in self. params:

self. dict [item] = matrix(self. dict [item])

5.Deletion of an element from the device instance. Removing an element can be useful for internal operations. For example, after solving the power flow analysis and before running a time domain simulation, it could be necessary to remove static generators at the buses where there are synchronous machines (i.e., synchronous machines substitute static generators in dynamic analysis). The code below accepts as input the index of the element that has to be removed. If the index is not defined, then the procedure exits. Otherwise, all parameter arrays and lists are processed and the element at the position item is popped out.

def remove(self, idx=None):

if idx != None:

if not self.int.has key(idx):

240

9 Device Generalities

item = self.idx.index[idx] key = idx

else:

print ’The item <%s> does not exist.’ % idx return None

else:

# nothing to remove return None

convert = False

if isinstance(self. dict [self. params[0]], matrix): self. matrix2list()

convert = True

self.n -= 1 self.int.pop(key, ’’) self.idx.pop(item)

for x, y in self.int.iteritems():

if y > item: self.int[x] = y - 1

for param in self. data:

self. dict [param].pop(item)

for param in self. service:

if len(self. dict [param]) == (self.n + 1): if isinstance(self. dict [param], list):

self. dict [param].pop(item)

elif isinstance(self. dict [param], matrix): service = list(self. dict [param]) service.pop(item)

self. dict [param] = matrix(service)

for x in self. states:

if len(self. dict [x]): self. dict [x].pop(item)

for y in self. algebs:

if self. dict [y]: self. dict [y].pop(item)

for key, param in self. bus.iteritems(): if isinstance(param, list):

for subparam in param:

if len(self. dict [subparam]): self. dict [subparam].pop(item)

else:

self. dict [param].pop(item)

for key, param in self. node.iteritems(): self. dict [param].pop(item)

self.name.pop(item)

if convert and self.n: self. list2matrix()

9.2 Devices as Classes

241

The built-in method pop is available for both lists and dictionaries and is used in the script for removing the assigned element. Since CVXOPT array dimension cannot be modified and do not provide the method pop, CVXOPT arrays are firstly converted into lists through the method matrix2list which implements the opposite conversion than the method list2matrix.

6.Handling windup and anti-windup limiters. A description of windup and anti-windup limiters as well as the implementation of the correspondent meta-methods is provided in Appendix C and Script C.1, respectively.

9.2.3Specific Device Methods

A description and an example of specific device methods conclude this chapter. Specific methods define the mathematical model of the device, as follows.

1.Initialization of state and algebraic variables (for devices initialized after the power flow analysis).

2.Algebraic equations g.

3.Di erential equations f .

4.Jacobian matrix gy .

5.Jacobian matrices gx, f y and f x.7

6.Objective function, inequality constraints and Hessian matrix (for devices that are included in the OPF problem).

7.Windup and anti-windup limiters (for devices with variables than can saturate).

Script 9.3 Methods of the Synchronous Machine Two-Axis

Model

The following methods implement the algebraic di erential equations and Jacobian matrices as well as the initialization function for the two-axis model of the synchronous machine. It is assumed that the constructor method is the one presented in the previous Script 9.2 and that all parameters are vectors initialized by the function matrix of the module CVXOPT. For all functions, the following header is assumed:

import system

from cvxopt.base import spmatrix, matrix

from cvxopt.base import mul, div, exp, log, sin, cos

1.Initialization of algebraic and state variables. Since the synchronous machine model is not used in power flow analysis, this device has to be initialized after the solution of the power flow problem. The equations

7The Jacobian matrix gy is taken apart from other Jacobian matrices to allows implementing e ciently explicit numerical methods (see Chapter 8).

242

9 Device Generalities

used for initializing the synchronous machine two-axis model are given in Example 9.2.

def xinit(self, dae):

p0 = mul(mul(self.u, system.Bus.pg[self.a]), self.gammap)

q0 = mul(mul(self.u, system.Bus.qg[self.a]), self.gammaq)

v0 = mul(self.u, dae.y[self.v]) theta0 = dae.y[self.a]

V = mul(v0 + 0j, exp(theta0*1j))

S = p0 - q0*1j

I = div(S, V.H.T)

E = V + mul(self.ra + self.xq*1j, I) delta = log(div(E, abs(E) + 0j))

dae.x[self.delta] = mul(self.u, delta.imag()) dae.x[self.omega] = matrix(1.0, (self.n, 1), ’d’)

# d- and q-axis voltages and currents jpi2 = 1.5707963267948966j

vdq = mul(self.u + 0j, mul(V, exp(jpi2 - delta))) idq = mul(self.u + 0j, mul(I, exp(jpi2 - delta)))

vd = dae.y[self.vd] = vdq.real() vq = dae.y[self.vq] = vdq.imag() Id = dae.y[self.Id] = idq.real() Iq = dae.y[self.Iq] = idq.imag()

self.tm0 = mul(vq + mul(self.ra, Iq), Iq) + \ mul(vd + mul(self.ra, Id), Id)

dae.y[self.tm] = self.tm0

dae.x[self.e1q] = vq + mul(self.ra, Iq) + mul(self.xd1, Id) dae.x[self.e1d] = vd + mul(self.ra, Id) - mul(self.xq1, Iq) self.vf0 = dae.x[self.e1q] + mul(self.xd - self.xd1, Id) dae.y[self.vf] = self.vf0

system.Device.remove gen(self.gen)

In this example, it is assumed that generated active and reactive powers are stored in the vectors system.Bus.Pg and system.Bus.Qg, while the bus voltage is contained in the variable dae (which is passed as an argument of the method and coincides with system.DAE). In the last line, the interface class system.Device is called for removing, if required, the static generator connected at the synchronous machine buses. A possible implementation of the method system.Device.remove gen is as follows:

def remove gen(self, idx):

for item, stagen in zip(self.devices, self.stagen): if stagen:

indexes = system. dict [item].int.keys() for key in idx:

9.2 Devices as Classes

243

if key in indexes:

system. dict [item].remove(key)

where self.devices is is the list of devices currently in use and the attribute self.stagen is a list of the same length as self.devices and whose elements are True if the correspondent device is a static generator, False otherwise.

2. Algebraic equations.

def gcall(self, dae):

delta = dae.x[delta] e1d = dae.x[e1d]

e1q = dae.x[e1q]

v = mul(self.u, dae.y[self.v]) theta = dae.y[self.a]

tm = dae.y[self.tm] vf = dae.y[self.vf] te = dae.y[self.te]

vq = mul(self.u, dae.y[self.vq]) vd = mul(self.u, dae.y[self.vd]) Iq = mul(self.u, dae.y[self.Iq]) Id = mul(self.u, dae.y[self.Id])

# internal algebraic equations

dae.g[self.te] = mul(vq + mul(self.ra, Iq), Iq) + \ mul(vd + mul(self.ra, Id), Id) - te

dae.g[self.Id] = vq + mul(self.ra, Iq) - e1q + mul(self.xd1, Id) dae.g[self.Iq] = vd + mul(self.ra, Id) - e1d - mul(self.xq1, Iq) dae.g[self.vd] = mul(v, sin(delta - theta)) - vd

dae.g[self.vq] = mul(v, cos(delta - theta)) - vq dae.g[self.tm] = mul(self.u, self.tm0) - tm dae.g[self.vf] = mul(self.u, self.vf0) - vf

#network interface equations

#active power

dae.g[self.a] += spmatrix(mul(vd, Id) + mul(vq, Iq), \ self.a, [0]*self.n, (dae.ny, 1), ’d’)

# reactive power

dae.g[self.v] += spmatrix(mul(vq, Id) - mul(vd, Iq), \ self.v, [0]*self.n, (dae.ny, 1), ’d’)

There is a conceptual di erence between internal algebraic equations gˆi and gp,hi and gq,hi. Equations gˆi are specific of the synchronous machine model and are not shared with other devices. Thus a direct indexation works fine. On the other hand, gp,hi and gq,hi are the synchronous machine contribution to the power balance at the bus h. Since other devices (e.g., transmission lines) sum their power injections to the same bus, the function

244

9 Device Generalities

gcall has to update and not to overwrite the current value of gp,hi and

gq,hi.

The status vector self.u systematically multiplies parameters and/or variables to impose gˆi = 0, gp,hi = 0 and gq,hi = 0 in case some synchronous machine element is o -line.

3. Di erential equations.

def fcall(self, dae):

# differential equations

omega = dae.x[self.omega] tm = dae.y[self.tm]

te = dae.y[self.te] vf = dae.y[self.vf]

iTd = div(self.u,

self.Td10)

 

xTd = mul(iTd, self.xd - self.xd1)

iTq = div(self.u,

self.Tq10)

 

xTq = mul(iTq, self.xq - self.xq1)

dae.f[self.delta]

= system.Settings.rad * \

 

 

mul(self.u,

omega - 1)

dae.f[self.omega]

= mul(self.u,

div(tm - te - \

 

 

mul(self.D,

omega - 1), 2*self.H))

dae.f[self.e1q]

=

mul(iTd, vf)

- mul(xTd, dae.y[self.Id]) - \

 

 

mul(iTd, dae.x[self.e1q])

dae.f[self.e1d]

=

mul(xTq, dae.y[self.Iq]) - mul(iTq, dae.x[self.e1d])

The variable system.Settings.rad contains the base synchronous speed in rad/s. The status vector self.u imposes f i = 0 if some synchronous machine element is switched o .

4. Jacobian matrices.

def gycall(self, dae):

delta = dae.x[delta]

v = mul(self.u, dae.y[self.v]) theta = dae.y[self.a]

vq = mul(self.u, dae.y[self.vq]) vd = mul(self.u, dae.y[self.vd]) Iq = mul(self.u, dae.y[self.Iq]) Id = mul(self.u, dae.y[self.Id]) ny x ny = (dae.ny, dae.ny)

# internal Jacobians

dae.Gy -= spmatrix(1, self.te, self.te, ny x ny, ’d’) dae.Gy += spmatrix(Iq, self.te, self.vq, ny x ny, ’d’) dae.Gy += spmatrix(Id, self.te, self.vd, ny x ny, ’d’)

9.2 Devices as Classes

245

dae.Gy += spmatrix(vq + 2*mul(self.ra, Iq), self.te, \ self.Iq, ny x ny, ’d’)

dae.Gy += spmatrix(vd + 2*mul(self.ra, Id), self.te, \ self.Id, ny x ny, ’d’)

dae.Gy += spmatrix(self.u, self.Id, self.vq, ny x ny, ’d’) dae.Gy += spmatrix(mul(self.u, self.ra), self.Id, self.Iq, \

ny x ny, ’d’)

dae.Gy += spmatrix(self.xd1, self.Id, self.Id, ny x ny, ’d’)

dae.Gy += spmatrix(self.u, self.Iq, self.vd, ny x ny, ’d’) dae.Gy += spmatrix(mul(self.u, self.ra), self.Iq, self.Id, \

ny x ny, ’d’)

dae.Gy -= spmatrix(self.xq1, self.Iq, self.Iq, ny x ny, ’d’)

dae.Gy -= spmatrix(1, self.vd, self.vd, ny x ny, ’d’) dae.Gy += spmatrix(mul(self.u, sin(delta - theta)), \

self.vd, self.v, ny x ny, ’d’) dae.Gy -= spmatrix(mul(v, cos(delta - theta)), \ self.vd, self.a, ny x ny, ’d’)

dae.Gy -= spmatrix(1, self.vq, self.vq, ny x ny, ’d’) dae.Gy += spmatrix(mul(self.u, cos(delta - theta)), \

self.vq, self.v, ny x ny, ’d’) dae.Gy += spmatrix(mul(v, sin(delta - theta)), \ self.vq, self.a, ny x ny, ’d’)

dae.Gy -= spmatrix(1, self.tm, self.tm, ny x ny, ’d’) dae.Gy -= spmatrix(1, self.vf, self.vf, ny x ny, ’d’)

# network interface Jacobians

dae.Gy += spmatrix(Id, self.a, self.vd, ny x ny, ’d’) dae.Gy += spmatrix(vd, self.a, self.Id, ny x ny, ’d’) dae.Gy += spmatrix(Iq, self.a, self.vq, ny x ny, ’d’) dae.Gy += spmatrix(vq, self.a, self.Iq, ny x ny, ’d’)

dae.Gy += spmatrix(Id, self.v, self.vq, ny x ny, ’d’) dae.Gy += spmatrix(vq, self.v, self.Id, ny x ny, ’d’) dae.Gy -= spmatrix(Iq, self.v, self.vd, ny x ny, ’d’) dae.Gy -= spmatrix(vd, self.v, self.Iq, ny x ny, ’d’)

def fxcall(self, dae):

delta = dae.x[delta] theta = dae.y[self.a]

v = mul(self.u, dae.y[self.v])

# Jacobian matrix g x

dae.Gx += spmatrix(mul(v, cos(delta - theta)), self.vd, \ self.delta, (dae.ny, dae.nx), ’d’)

dae.Gx -= spmatrix(mul(v, sin(delta - theta)), self.vq, \ self.delta, (dae.ny, dae.nx), ’d’)

246 9 Device Generalities

dae.Gx -= spmatrix(self.u, self.Id, self.e1q, (dae.ny, dae.nx), ’d’) dae.Gx -= spmatrix(self.u, self.Iq, self.e1d, (dae.ny, dae.nx), ’d’)

# Jacobian matrix f y

dae.Fy += spmatrix(div(self.u, 2*self.H), self.omega, \ self.tm, (dae.nx, dae.ny), ’d’)

dae.Fy -= spmatrix(div(self.u, 2*self.H), self.omega, \ self.te, (dae.nx, dae.ny), ’d’)

dae.Fy += spmatrix(div(self.u, self.Td10), self.e1q, \ self.vf, (dae.nx, dae.ny), ’d’)

dae.Fy -= spmatrix(div(mul(self.u, self.xd - self.xd1), self.Td10), \ self.e1q, self.Id, (dae.nx, dae.ny), ’d’)

dae.Fy += spmatrix(div(mul(self.u, self.xq - self.xq1), self.Tq10), \ self.e1d, self.Iq, (dae.nx, dae.ny), ’d’)

# Jacobian matrix f x

dae.Fx += spmatrix(1 - self.u, self.delta, \ self.delta, (dae.nx, dae.nx), ’d’)

dae.Fx += spmatrix(system.Settings.rad*self.u, self.delta, \ self.omega, (dae.nx, dae.nx), ’d’)

dae.Fx -= spmatrix(div(self.D, 2*self.H), self.omega, \ self.omega, (dae.nx, dae.nx), ’d’)

dae.Fx -= spmatrix(div(1.0, self.Td10), self.e1q, \ self.e1q, (dae.nx, dae.nx), ’d’) dae.Fx -= spmatrix(div(1.0, self.Tq10), self.e1d, \ self.e1d, (dae.nx, dae.nx), ’d’)

In case an element is o -line, all Jacobians matrices are null except for the diagonal elements, which cannot be zero to avoid singularities.

Chapter 10

Power Flow Devices

This chapter describes topological elements as well as standard shunt (i.e., connected to a single bus) devices for power flow analysis. The most important topological element is the bus, while standard devices are constant generators, PV generators, PQ generators, PQ loads and constant and switched shunt admittances.

10.1Topological Elements

This section describes the main topological elements used in power system analysis, namely buses, zones, areas, regions and systems. The main data and constraints associated with topological elements are also discussed.

10.1.1Bus

The minimal network unit is the bus. Shunt devices are connected to buses, while series devices connect two or more buses together.

The name bus is an abbreviation of the Latin word omnibus, which means for anybody. This is actually what a bus should be: a connection point for any device. Unfortunately, the original meaning of the word bus is often misused in power flow analysis. Several software packages and books refer to “PV bus”, “slack bus” or “PQ bus”. These expressions mix together two di erent concept: (i) the concept of bus, which provides a topological information and (ii) the concept of generators or loads, which are devices connected to a bus. Actually, there is no real matter in connecting a PQ load and a PV generator at the same bus. Thus, strictly speaking, expressions as “PV bus” or “PQ bus” are misleading and should be avoided.

In power flow analysis, buses are intended as topological nodes, not as physical connections. This may prevent modelling physical bus-bars. For example it can be necessary to model buses composed by di erent bars or divided into

F. Milano: Power System Modelling and Scripting, Power Systems, pp. 247–261. springerlink.com c Springer-Verlag Berlin Heidelberg 2010

248

10 Power Flow Devices

sections. These features can be easily modelled by means of coupling devices (see Section 11.1.5 of Chapter 11).

Typical bus parameters are depicted in Table 10.1. Bus numbers (or names) are used by all other devices for identifying the bus to which are connected. Thus, each bus has to be identified by a unique number, code or name. In some old formats (e.g., IEEE common data format [350]), bus identification codes are integers. This was mainly due to old-style system programming languages such as FORTRAN that hardly handle hash types (e.g., lists of key-value pairs). Modern script languages provide a simple manner to handle hashes. The Python implementation of the hash is the built-in type dictionary. A key can be any number or string, the only requisite is that keys have to be unique. Then, the value associated to each key is the bus index h. In general, hashes provide a great programming flexibility and should be preferred to other indexing methods.

Table 10.1 Bus parameters

Variable

Description

Unit

 

 

 

 

 

 

-

Bus code

-

-

Bus name

-

-

Area code

-

-

Zone code

-

-

Region code

-

-

System code

-

v(0)

Voltage amplitude initial guess

pu

Vb

Voltage rating

kV

vmax

Maximum admissible voltage

pu

vmin

Minimum admissible voltage

pu

θ(0)

Voltage phase initial guess

rad

The voltage rating Vb is generally used in power flow analysis for fixing the voltage bases for per unit analysis. Voltage magnitudes v(0) and phases θ(0) can be optionally set if the power flow solution is known or if a custom initial guess is needed. If voltages are not specified, a flat start is used (e.g., v(0) = 1 at all buses except for buses where a PV or slack generator is present, and θ(0) = 0). Finally, area, zone, region and system codes are generally used for evaluating inter-area power flows or simply to assign an owner to the bus.

Since buses contain only topological information, no equation is required for defining the bus model. The only issue that can arise is in case a bus is islanded. In that case, the system admittance matrix may become singular if it is not properly conditioned. The easiest solution is to find islanded buses and to impose that the power balance at those buses is zero. This also implies that, the Jacobian matrix has to be properly conditioned, as follows. If a given bus h is islanded, the columns associated with the derivatives with respect to