1 #------------------------------------------------------------------------------
2 # Copyright (C) 2007-2010 Richard Lincoln
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 #------------------------------------------------------------------------------
17 """ Defines classes for dynamic simulation.
19 Based on MatDyn by Stijn Cole, developed at Katholieke Universiteit Leuven.
20 See U{http://www.esat.kuleuven.be/electa/teaching/matdyn/} for more info.
23 #------------------------------------------------------------------------------
25 #------------------------------------------------------------------------------
33 array
, zeros
, ones
, exp
, conj
, pi
, angle
, abs, sin
, cos
, c_
, r_
, \
36 from scipy
.sparse
.linalg
import spsolve
, splu
38 from pylon
import NewtonPF
40 from util
import _Named
, _Serializable
42 #------------------------------------------------------------------------------
44 #------------------------------------------------------------------------------
46 logger
= logging
.getLogger(__name__
)
48 #------------------------------------------------------------------------------
50 #------------------------------------------------------------------------------
52 EPS
= finfo(float).eps
54 CLASSICAL
= "classical"
55 FOURTH_ORDER
= "fourth_order"
57 CONST_EXCITATION
= "constant excitation"
58 IEEE_DC1A
= "IEEE DC1A"
60 CONST_POWER
= "constant power"
61 GENERAL_IEEE
= "IEEE general speed-governing system"
63 BUS_CHANGE
= "bus change"
64 BRANCH_CHANGE
= "branch change"
66 #------------------------------------------------------------------------------
67 # "DynamicCase" class:
68 #------------------------------------------------------------------------------
70 class DynamicCase(_Named
, _Serializable
):
71 """ Defines a dynamic simulation case.
74 def __init__(self
, case
, frequency
=50.0, stepsize
=0.01, stoptime
=1):
75 """ Constructs a new DynamicCase instance.
80 #: Dynamic generators.
81 self
.dyn_generators
= []
83 #: Generator exciters.
86 #: Generator governors.
89 #: Network frequency (Hz).
92 #: Stepsize of the integration algorithm (s).
93 self
.stepsize
= stepsize
95 #: Stoptime of the simulation (s).
96 self
.stoptime
= stoptime
99 def getAugYbus(self
, U0
, gbus
):
100 """ Based on AugYbus.m from MatDyn by Stijn Cole, developed at
101 Katholieke Universiteit Leuven. See U{http://www.esat.kuleuven.be/
102 electa/teaching/matdyn/} for more information.
105 @return: The augmented bus admittance matrix.
108 buses
= self
.case
.connected_buses
111 Ybus
, _
, _
= self
.case
.getYbus()
113 # Steady-state bus voltages.
115 # Calculate equivalent load admittance
116 Sd
= array([self
.case
.s_demand(bus
) for bus
in buses
])
117 Yd
= conj(Sd
) / abs(U0
)**2
119 xd_tr
= array([g
.xd_tr
for g
in self
.dyn_generators
])
121 # Calculate equivalent generator admittance.
123 Yg
[gbus
] = 1 / (j
* xd_tr
)
125 # Add equivalent load and generator admittance to Ybus matrix
127 Ybus
[i
, i
] = Ybus
[i
, i
] + Yg
[i
] + Yd
[i
]
132 def generatorInit(self
, U0
):
133 """ Based on GeneratorInit.m from MatDyn by Stijn Cole, developed at
134 Katholieke Universiteit Leuven. See U{http://www.esat.kuleuven.be/
135 electa/teaching/matdyn/} for more information.
138 @return: Initial generator conditions.
141 generators
= self
.dyn_generators
143 Efd0
= zeros(len(generators
))
144 Xgen0
= zeros((len(generators
), 4))
146 typ1
= [g
._i
for g
in generators
if g
.model
== CLASSICAL
]
147 typ2
= [g
._i
for g
in generators
if g
.model
== FOURTH_ORDER
]
149 # Generator type 1: classical model
150 x_tr
= array([g
.x_tr
for g
in generators
])
152 omega0
= ones(len(typ1
)) * 2 * pi
* self
.freq
154 # Initial machine armature currents.
155 Sg
= array([g
.p
+ j
* g
.q
for g
in generators
])
156 Ia0
= conj(Sg
[typ1
]) / conj(U0
) / self
.base_mva
158 # Initial Steady-state internal EMF.
159 Eq_tr0
= U0
[typ1
] + j
* x_tr
* Ia0
160 delta0
= angle(Eq_tr0
)
163 Xgen0
[typ1
, :] = c_
[delta0
, omega0
, Eq_tr0
]
165 # Generator type 2: 4th order model
166 xd
= array([g
.xd
for g
in generators
])
167 xq
= array([g
.xq
for g
in generators
])
168 xd_tr
= array([g
.xd_tr
for g
in generators
])
169 xq_tr
= array([g
.xq_tr
for g
in generators
])
171 omega0
= ones(len(typ2
)) * 2 * pi
* self
.freq
173 # Initial machine armature currents.
174 Ia0
= conj(Sg
[typ2
]) / conj(U0
[typ2
]) / self
.base_mva
177 # Initial Steady-state internal EMF.
178 Eq0
= U0
[typ2
] + j
* xq
* Ia0
181 # Machine currents in dq frame.
182 Id0
= -abs(Ia0
) * sin(delta0
- phi0
)
183 Iq0
= abs(Ia0
) * cos(delta0
- phi0
)
186 Efd0
[typ2
] = abs(Eq0
) - (xd
- xq
) * Id0
188 # Initial Transient internal EMF.
189 Eq_tr0
= Efd0
[typ2
] + (xd
- xd_tr
) * Id0
190 Ed_tr0
= -(xq
- xq_tr
) * Iq0
192 Xgen0
[typ2
, :] = c_
[delta0
, omega0
, Eq_tr0
, Ed_tr0
]
201 def exciterInit(self
, Xexc
, Vexc
):
202 """ Based on ExciterInit.m from MatDyn by Stijn Cole, developed at
203 Katholieke Universiteit Leuven. See U{http://www.esat.kuleuven.be/
204 electa/teaching/matdyn/} for more information.
207 @return: Exciter initial conditions.
209 exciters
= self
.exciters
211 Xexc0
= zeros(Xexc
.shape
)
212 Pexc0
= zeros(len(exciters
))
214 typ1
= [e
.generator
._i
for e
in exciters
if e
.model
==CONST_EXCITATION
]
215 typ2
= [e
.generator
._i
for e
in exciters
if e
.model
== IEEE_DC1A
]
217 # Exciter type 1: constant excitation
219 Xexc0
[typ1
, 0] = Efd0
221 # Exciter type 2: IEEE DC1A
223 Ka
= array([e
.ka
for e
in exciters
])
224 Ta
= array([e
.ta
for e
in exciters
])
225 Ke
= array([e
.ke
for e
in exciters
])
226 Te
= array([e
.te
for e
in exciters
])
227 Kf
= array([e
.kf
for e
in exciters
])
228 Tf
= array([e
.tf
for e
in exciters
])
229 Aex
= array([e
.aex
for e
in exciters
])
230 Bex
= array([e
.bex
for e
in exciters
])
231 Ur_min
= array([e
.ur_min
for e
in exciters
])
232 Ur_max
= array([e
.ur_max
for e
in exciters
])
236 Uf
= zeros(len(typ2
))
237 Ux
= Aex
* exp(Bex
* Efd0
)
239 Uref2
= U
+ (Ux
+ Ke
* Efd0
) / Ka
- U
242 Xexc0
[typ2
, :] = c_
[Efd0
, Uf
, Ur
]
243 Pexc0
[typ2
, :] = c_
[Ka
, Ta
, Ke
, Te
, Kf
, Tf
, Aex
, Bex
,
244 Ur_min
, Ur_max
, Uref
, Uref2
]
253 def governorInit(self
, Xgov
, Vgov
):
254 """ Based on GovernorInit.m from MatDyn by Stijn Cole, developed at
255 Katholieke Universiteit Leuven. See U{http://www.esat.kuleuven.be/
256 electa/teaching/matdyn/} for more information.
259 @return: Initial governor conditions.
261 governors
= self
.governors
263 Xgov0
= zeros(Xgov
.shape
)
264 Pgov0
= zeros(len(governors
))
266 typ1
= [g
.generator
._i
for g
in governors
if g
.model
== CONST_POWER
]
267 typ2
= [g
.generator
._i
for g
in governors
if g
.model
== GENERAL_IEEE
]
269 # Governor type 1: constant power
273 # Governor type 2: IEEE general speed-governing system
276 K
= array([g
.k
for g
in governors
])
277 T1
= array([g
.t1
for g
in governors
])
278 T2
= array([g
.t2
for g
in governors
])
279 T3
= array([g
.t3
for g
in governors
])
280 Pup
= array([g
.p_up
for g
in governors
])
281 Pdown
= array([g
.p_down
for g
in governors
])
282 Pmax
= array([g
.p_max
for g
in governors
])
283 Pmin
= array([g
.p_min
for g
in governors
])
285 omega0
= Vgov
[typ2
, 0]
290 P0
= K
* (2 * pi
* self
.freq
- omega0
)
291 xx0
= T1
* (1 - T2
/ T1
) * (2 * pi
* self
.freq
- omega0
)
293 Xgov0
[typ2
, :] = c_
[Pm0
, P0
, xx0
, zz0
]
294 Pgov0
[typ2
, :] = c_
[K
, T1
, T2
, T3
, Pup
, Pdown
, Pmax
, Pmin
, PP0
]
303 def machineCurrents(self
, Xg
, U
):
304 """ Based on MachineCurrents.m from MatDyn by Stijn Cole, developed at
305 Katholieke Universiteit Leuven. See U{http://www.esat.kuleuven.be/
306 electa/teaching/matdyn/} for more information.
308 @param Xg: Generator state variables.
309 @param U: Generator voltages.
312 @return: Currents and electric power of generators.
314 generators
= self
.dyn_generators
322 typ1
= [g
._i
for g
in generators
if g
.model
== CLASSICAL
]
323 typ2
= [g
._i
for g
in generators
if g
.model
== FOURTH_ORDER
]
325 # Generator type 1: classical model
329 xd
= array([g
.xd
for g
in generators
])
332 1 / xd
* abs(U
[typ1
]) * abs(Eq_tr
) * sin(delta
- angle(U
[typ1
]))
334 # Generator type 2: 4th order model
339 xd_tr
= array([g
.xd_tr
for g
in generators
])
340 xq_tr
= array([g
.xq_tr
for g
in generators
])
344 # Transform U to rotor frame of reference.
345 vd
= -abs(U
[typ2
]) * sin(delta
- theta
[typ2
])
346 vq
= abs(U
[typ2
]) * cos(delta
- theta
[typ2
])
348 Id
[typ2
] = (vq
- Eq_tr
) / xd_tr
349 Iq
[typ2
] = -(vd
- Ed_tr
) / xq_tr
352 Eq_tr
* Iq
[typ2
] + Ed_tr
* Id
[typ2
] + \
353 (xd_tr
- xq_tr
) * Id
[typ2
] * Iq
[typ2
]
358 def solveNetwork(self
, Xgen
, augYbus_solver
, gbus
):
359 """ Based on SolveNetwork.m from MatDyn by Stijn Cole, developed at
360 Katholieke Universiteit Leuven. See U{http://www.esat.kuleuven.be/
361 electa/teaching/matdyn/} for more information.
364 @return: Bus voltages.
366 generators
= self
.dyn_generators
372 s
= len(augYbus_solver
)
375 # Define generator types.
376 typ1
= [g
._i
for g
in generators
if g
.model
== CLASSICAL
]
377 typ2
= [g
._i
for g
in generators
if g
.model
== FOURTH_ORDER
]
379 # Generator type 1: classical model
380 delta
= Xgen
[typ1
, 0]
381 Eq_tr
= Xgen
[typ1
, 2]
383 xd_tr
= array([g
.xd_tr
for g
in generators
])[typ1
]
385 # Calculate generator currents
386 Igen
[typ1
] = (Eq_tr
* exp(j
* delta
)) / (j
* xd_tr
)
388 # Generator type 2: 4th order model
389 delta
= Xgen
[typ2
, 0]
390 Eq_tr
= Xgen
[typ2
, 2]
391 Ed_tr
= Xgen
[typ2
, 3]
393 xd_tr
= array([g
.xd_tr
for g
in generators
])[typ2
] # Pgen(type2,8)
395 # Calculate generator currents. (Padiyar, p.417.)
396 Igen
[typ2
] = (Eq_tr
+ j
* Ed_tr
) * exp(j
* delta
) / (j
* xd_tr
)
398 # Calculations --------------------------------------------------------
403 # Calculate network voltages: U = Y/Ig
404 U
= augYbus_solver
.solve(Ig
)
409 def exciter(self
, Xexc
, Pexc
, Vexc
):
412 Based on Exciter.m from MatDyn by Stijn Cole, developed at Katholieke
413 Universiteit Leuven. See U{http://www.esat.kuleuven.be/electa/teaching/
414 matdyn/} for more information.
416 exciters
= self
.exciters
418 F
= zeros(Xexc
.shape
)
420 typ1
= [e
.generator
._i
for e
in exciters
if e
.model
==CONST_EXCITATION
]
421 typ2
= [e
.generator
._i
for e
in exciters
if e
.model
== IEEE_DC1A
]
423 # Exciter type 1: constant excitation
426 # Exciter type 2: IEEE DC1A
439 Ur_min
= Pexc
[typ2
, 8]
440 Ur_max
= Pexc
[typ2
, 9]
441 Uref
= Pexc
[typ2
, 10]
442 Uref2
= Pexc
[typ2
, 11]
446 Ux
= Aex
* exp(Bex
* Efd
)
447 dUr
= 1 / Ta
* (Ka
* (Uref
- U
+ Uref2
- Uf
) - Ur
)
448 dUf
= 1 / Tf
* (Kf
/ Te
* (Ur
- Ux
- Ke
* Efd
) - Uf
)
450 if sum(flatnonzero(Ur
> Ur_max
)) >= 1:
452 elif sum(flatnonzero(Ur
< Ur_max
)) >= 1:
457 dEfd
= 1 / Te
* (Ur2
- Ux
- Ke
* Efd
)
458 F
[typ2
, :] = c_
[dEfd
, dUf
, dUr
]
467 def governor(self
, Xgov
, Pgov
, Vgov
):
470 Based on Governor.m from MatDyn by Stijn Cole, developed at Katholieke
471 Universiteit Leuven. See U{http://www.esat.kuleuven.be/electa/teaching/
472 matdyn/} for more information.
474 governors
= self
.governors
475 omegas
= 2 * pi
* self
.freq
477 F
= zeros(Xgov
.shape
)
479 typ1
= [g
.generator
._i
for g
in governors
if g
.model
== CONST_POWER
]
480 typ2
= [g
.generator
._i
for g
in governors
if g
.model
== GENERAL_IEEE
]
482 # Governor type 1: constant power
485 # Governor type 2: IEEE general speed-governing system
496 Pdown
= Pgov
[typ2
, 5]
501 omega
= Vgov
[typ2
, 0]
503 dx
= K
* (-1 / T1
* x
+ (1 - T2
/ T1
) * (omega
- omegas
))
504 dP
= 1 / T1
* x
+ T2
/ T1
* (omega
- omegas
)
506 y
= 1 / T3
* (P0
- P
- Pm
)
510 if sum(flatnonzero(y
> Pup
)) >= 1:
511 y2
= (1 - flatnonzero(y
> Pup
)) * y2
+ flatnonzero(y
> Pup
) * Pup
512 if sum(flatnonzero(y
< Pdown
)) >= 1:
513 y2
= (1 - flatnonzero(y
<Pdown
)) * y2
+ flatnonzero(y
<Pdown
) * Pdown
519 if sum(flatnonzero(z
> Pmax
)) >= 1:
520 dPm
= (1 - flatnonzero(z
> Pmax
)) * dPm
+ flatnonzero(z
> Pmax
) * 0
521 if sum(flatnonzero(z
< Pmin
)) >= 1:
522 dPm
= (1 - flatnonzero(z
< Pmin
)) * dPm
+ flatnonzero(z
< Pmin
) * 0
524 F
[typ2
, :] = c_
[dPm
, dP
, dx
, dz
]
533 def generator(self
, Xgen
, Xexc
, Xgov
, Vgen
):
536 Based on Generator.m from MatDyn by Stijn Cole, developed at Katholieke
537 Universiteit Leuven. See U{http://www.esat.kuleuven.be/electa/teaching/
538 matdyn/} for more information.
540 generators
= self
.dyn_generators
541 omegas
= 2 * pi
* self
.freq
543 F
= zeros(Xgen
.shape
)
545 typ1
= [g
._i
for g
in generators
if g
.model
== CLASSICAL
]
546 typ2
= [g
._i
for g
in generators
if g
.model
== FOURTH_ORDER
]
548 # Generator type 1: classical model
549 omega
= Xgen
[typ1
, 1]
552 H
= array([g
.h
for g
in generators
])[typ1
]
553 D
= array([g
.d
for g
in generators
])[typ1
]
557 ddelta
= omega
= omegas
558 domega
= pi
* self
.freq
/ H
* (-D
* (omega
- omegas
) + Pm0
- Pe
)
559 dEq
= zeros(len(typ1
))
561 F
[typ1
, :] = c_
[ddelta
, domega
, dEq
]
563 # Generator type 2: 4th order model
564 omega
= Xgen
[typ2
, 1]
565 Eq_tr
= Xgen
[typ2
, 2]
566 Ed_tr
= Xgen
[typ2
, 3]
568 H
= array([g
.h
for g
in generators
])
569 D
= array([g
.d
for g
in generators
])
570 xd
= array([g
.xd
for g
in generators
])
571 xq
= array([g
.xq
for g
in generators
])
572 xd_tr
= array([g
.xd_tr
for g
in generators
])
573 xq_tr
= array([g
.xq_tr
for g
in generators
])
574 Td0_tr
= array([g
.td
for g
in generators
])
575 Tq0_tr
= array([g
.tq
for g
in generators
])
584 ddelta
= omega
- omegas
585 domega
= pi
* self
.freq
/ H
* (-D
* (omega
- omegas
) + Pm
- Pe
)
586 dEq
= 1 / Td0_tr
* (Efd
- Eq_tr
+ (xd
- xd_tr
) * Id
)
587 dEd
= 1 / Tq0_tr
* (-Ed_tr
- (xq
- xq_tr
) * Iq
)
589 F
[typ2
, :] = c_
[ddelta
, domega
, dEq
, dEd
]
597 #------------------------------------------------------------------------------
598 # "DynamicSolver" class:
599 #------------------------------------------------------------------------------
601 class DynamicSolver(object):
602 """ Defines a solver for dynamic simulation.
604 The adaptive step size methods start with minimal step size. It is of
605 interest to increase minimum step size as it speeds up the calculations.
606 Generally, tolerance must be increased as well, or the integration will
609 Based on rundyn.m from MatDyn by Stijn Cole, developed at Katholieke
610 Universiteit Leuven. See U{http://www.esat.kuleuven.be/electa/teaching/
611 matdyn/} for more information.
614 def __init__(self
, dyn_case
, method
=None, tol
=1e-04,
615 minstep
=1e-03, maxstep
=1e02
, verbose
=True, plot
=True):
618 self
.dyn_case
= dyn_case
620 #: Integration method.
621 self
.method
= ModifiedEuler() if method
is None else method
623 #: Specify the tolerance of the error. This argument is only used for
624 #: the Runge-Kutta Fehlberg and Higham and Hall methods.
627 #: Sets the minimum step size. Only used by the adaptive step size
628 #: algorithms: Runge-Kutta Fehlberg and Higham and Hall methods.
629 self
.minstep
= minstep
631 #: Sets the maximal step size. Only used by the adaptive step size
632 #: algorithms: Runge-Kutta Fehlberg and Higham and Hall methods.
633 self
.maxstep
= maxstep
635 #: Print progress output?
636 self
.verbose
= verbose
643 """ Runs dynamic simulation.
646 @return: Solution dictionary with the following keys:
647 - C{angles} - generator angles
648 - C{speeds} - generator speeds
649 - C{eq_tr} - q component of transient voltage behind
651 - C{ed_tr} - d component of transient voltage behind
653 - C{efd} - Excitation voltage
654 - C{pm} - mechanical power
655 - C{voltages} - bus voltages
656 - C{stepsize} - step size integration method
657 - C{errest} - estimation of integration error
658 - C{failed} - failed steps
659 - C{time} - time points
662 buses
= self
.dyn_case
.buses
664 solution
= NewtonPF(self
.case
).solve()
666 if not solution
["converged"]:
667 logger
.error("Power flow did not converge. Exiting...")
670 logger
.info("Power flow converged.")
672 # Construct augmented Ybus.
674 logger
.info("Constructing augmented admittance matrix...")
676 gbus
= [g
.bus
._i
for g
in self
.dyn_generators
]
679 Um
= array([bus
.v_magnitude
for bus
in buses
])
680 Ua
= array([bus
.v_angle
* (pi
/ 180.0) for bus
in buses
])
681 U0
= Um
* exp(1j
* Ua
)
684 augYbus
= self
.dyn_case
.getAugYbus(U0
, gbus
)
685 augYbus_solver
= splu(augYbus
)
687 # Calculate initial machine state.
689 logger
.info("Calculating initial state...")
691 Efd0
, Xgen0
= self
.dyn_case
.generatorInit(U0
)
694 Id0
, Iq0
, Pe0
= self
.dyn_case
.machineCurrents(Xgen0
, U0
)
695 Vgen0
= r_
[Id0
, Iq0
, Pe0
]
697 # Exciter initial conditions.
698 Vexc0
= abs(U0
[gbus
])
699 Xexc0
, Pexc0
= self
.dyn_case
.exciterInit(Efd0
, Vexc0
)
701 # Governor initial conditions.
703 Xgov0
, Pgov0
= self
.dyn_case
.governorInit(Pm0
, omega0
)
706 # Check steady-state.
707 Fexc0
= self
.dyn_case
.exciter(Xexc0
, Pexc0
, Vexc0
)
708 Fgov0
= self
.dyn_case
.governor(Xgov0
, Pgov0
, Vgov0
)
709 Fgen0
= self
.dyn_case
.generator(Xgen0
, Xexc0
, Xgov0
, Vgen0
)
711 # Check Generator Steady-state
712 if sum(abs(Fgen0
)) > 1e-06:
713 logger
.error("Generator not in steady-state. Exiting...")
715 # Check Exciter Steady-state
716 if sum(abs(Fexc0
)) > 1e-06:
717 logger
.error("Exciter not in steady-state. Exiting...")
719 # Check Governor Steady-state
720 if sum(abs(Fgov0
)) > 1e-06:
721 logger
.error("Governor not in steady-state. Exiting...")
725 logger
.info("System in steady-state.")
727 # Initialization of main stability loop.
728 t
= -0.02 # simulate 0.02s without applying events
733 stoptime
= self
.dyn_case
.stoptime
735 if (isinstance(self
.method
, RungeKuttaFehlberg
) or
736 isinstance(self
.method
, RungeKuttaHighamHall
)):
737 stepsize
= self
.minstep
739 stepsize
= self
.dyn_case
.stepsize
742 eventhappened
= False
745 # Allocate memory for variables.
747 logger
.info("Allocating memory...")
752 errest
= zeros(chunk
)
754 stepsizes
= zeros(chunk
)
755 stepsizes
[0, :] = stepsize
758 voltages
= zeros(chunk
)
759 voltages
[0, :] = U0
.H
762 angles
= zeros((chunk
, ng
))
763 angles
[0, :] = Xgen0
[:, 0] * 180.0 / pi
764 speeds
= zeros((chunk
, ng
))
765 speeds
[0, :] = Xgen0
[:, 0] / 2 * pi
* self
.dyn_case
.freq
766 Eq_tr
= zeros((chunk
, ng
))
767 Eq_tr
[0, :] = Xgen0
[:, 2]
768 Ed_tr
= zeros((chunk
, ng
))
769 Ed_tr
[0, :] = Xgen0
[:, 3]
771 # Exciter and governor
772 Efd
= zeros((chunk
, ng
))
773 Efd
[0, :] = Efd0
[:, 0]
774 PM
= zeros((chunk
, ng
))
777 # Main stability loop.
778 while t
< stoptime
+ stepsize
:
780 if i
% 45 == 0 and self
.verbose
:
781 logger
.info("%6.2f%% completed." % t
/ stoptime
* 100)
784 Xgen0
, self
.Pgen0
, Vgen0
, Xexc0
, Pexc0
, Vexc0
, Xgov0
, Pgov0
, \
785 Vgov0
, U0
, t
, newstepsize
= self
.method
.solve(t
, Xgen0
,
786 self
.Pgen0
, Vgen0
, Xexc0
, Pexc0
, Vexc0
, Xgov0
, Pgov0
,
787 Vgov0
, augYbus_solver
, gbus
, stepsize
)
789 # if self.method == MODIFIED_EULER:
790 # solver = ModifiedEuler(t, Xgen0, self.Pgen0, Vgen0, Xexc0,
791 # Pexc0, Vexc0, Xgov0, Pgov0, Vgov0,
792 # augYbus_solver, gbus, stepsize)
794 # Xgen0, self.Pgen0, Vgen0, Xexc0, Pexc0, Vexc0, Xgov0, Pgov0,
795 # Vgov0, U0, t, newstepsize = solver.solve()
796 # elif self.method == RUNGE_KUTTA:
798 # elif self.method == RUNGE_KUTTA_FEHLBERG:
800 # elif self.method == HIGHAM_HALL:
802 # elif self.method == MODIFIED_EULER2:
808 logger
.info("No solution found. Exiting... ")
814 # End exactly at stop time.
815 if t
+ newstepsize
> stoptime
:
816 newstepsize
= stoptime
- t
817 elif stepsize
< self
.minstep
:
818 logger
.info("No solution found with minimum step size. Exiting... ")
821 # Allocate new memory chunk if matrices are full.
822 if i
> time
.shape
[0]:
824 errest
= zeros(chunk
)
825 stepsize
= zeros(chunk
)
826 voltages
= zeros(chunk
)
827 angles
= zeros((chunk
, ng
))
828 speeds
= zeros((chunk
, ng
))
829 Eq_tr
= zeros((chunk
, ng
))
830 Ed_tr
= zeros((chunk
, ng
))
831 Efd
= zeros((chunk
, ng
))
832 PM
= zeros((chunk
, ng
))
835 stepsizes
[i
, :] = stepsize
842 Efd
[i
, :] = Xexc0
[:, 0]
843 # TODO: Set Efd to zero when using classical generator model.
846 PM
[i
, :] = Xgov0
[:, 0]
849 angles
[i
, :] = Xgen0
[:, 0] * 180.0 / pi
850 speeds
[i
, :] = Xgen0
[:, 1] * (2 * pi
* self
.dyn_case
.freq
)
851 Eq_tr
[i
, :] = Xgen0
[:, 2]
852 Ed_tr
[i
, :] = Xgen0
[:, 3]
854 # Adapt step size if event will occur in next step.
855 if (len(self
.events
) > 0 and ev
<= len(self
.events
) and
856 isinstance(self
.method
, RungeKuttaFehlberg
) and
857 isinstance(self
.method
, RungeKutta
)):
859 if t
+ newstepsize
>= self
.events
[ev
].t
:
860 if self
.events
[ev
] - t
< newstepsize
:
861 newstepsize
= self
.events
[ev
].t
- t
864 if len(self
.events
) > 0 and ev
<= len(self
.events
):
865 for event
in self
.events
:
866 if (abs(t
- self
.events
[ev
].t
) > 10 * EPS
or
867 ev
> len(self
.events
)):
872 event
.obj
.set_attr(event
.param
, event
.newval
)
878 self
.dyn_case
.getAugYbus(U00
, gbus
)
879 U0
= self
.dyn_case
.solveNetwork(Xgen0
, self
.Pgen0
,
880 augYbus_solver
, gbus
)
882 Id0
, Iq0
, Pe0
= self
.dyn_case
.machineCurrents(Xgen0
,
885 Vgen0
= r_
[Id0
, Iq0
, Pe0
]
886 Vexc0
= abs(U0
[gbus
])
888 # Decrease stepsize after event occured.
889 if (isinstance(self
.method
, RungeKuttaFehlberg
) or
890 isinstance(self
.method
, RungeKuttaHighamHall
)):
892 newstepsize
= self
.minstepsize
894 # If event occurs, save values at t- and t+.
898 stepsize
[i
, :] = stepsize
.T
899 errest
[i
, :] = erst
.T
902 voltages
[i
, :] = U0
.T
905 # Set Efd to zero when using classical generator model.
906 # Efd[i, :] = Xexc0[:, 1] * (flatnonzero(genmodel > 1))
909 PM
[i
, :] = Xgov0
[:, 1]
912 angles
[i
, :] = Xgen0
[:, 0] * 180.0 / pi
913 speeds
[i
, :] = Xgen0
[:, 1] / (2.0 * pi
* self
.freq
)
914 Eq_tr
[i
, :] = Xgen0
[:, 2]
915 Ed_tr
[i
, :] = Xgen0
[:, 3]
917 eventhappened
= False
920 stepsize
= newstepsize
923 # End of main stability loop ------------------------------------------
925 # Output --------------------------------------------------------------
928 logger
.info("100%% completed")
929 elapsed
= time() - t0
930 logger
.info("Simulation completed in %5.2f seconds." % elapsed
)
932 # Save only the first i elements.
933 angles
= angles
[0:i
, :]
934 speeds
= speeds
[0:i
, :]
935 Eq_tr
= Eq_tr
[0:i
, :]
936 Ed_tr
= Ed_tr
[0:i
, :]
941 voltages
= voltages
[0:i
, :]
943 stepsize
= stepsize
[0:i
, :]
944 errest
= errest
[0:i
, :]
948 raise NotImplementedError
952 #------------------------------------------------------------------------------
953 # "ModifiedEuler" class:
954 #------------------------------------------------------------------------------
956 class ModifiedEuler(object):
957 """ Modified Euler ODE solver.
959 Based on ModifiedEuler.m from MatDyn by Stijn Cole, developed at Katholieke
960 Universiteit Leuven. See U{http://www.esat.kuleuven.be/electa/teaching/
961 matdyn/} for more information.
964 def solve(self
, t
, Xgen0
, Pgen
, Vgen0
, Xexc0
, Pexc
, Vexc0
, Xgov0
, Pgov
,
965 Vgov0
, augYbus_solver
, gbus
, stepsize
):
968 # First Euler step ----------------------------------------------------
971 dFexc0
= case
.exciter(Xexc0
, Pexc
, Vexc0
)
972 Xexc1
= Xexc0
+ case
.stepsize
* dFexc0
975 dFgov0
= case
.governor(Xgov0
, Pgov
, Vgov0
)
976 Xgov1
= Xgov0
+ case
.stepsize
* dFgov0
979 dFgen0
= case
.generator(Xgen0
, Xexc1
, Xgov1
, Pgen
,Vgen0
)
980 Xgen1
= Xgen0
+ case
.stepsize
* dFgen0
982 # Calculate system voltages.
983 U1
= case
.solveNetwork(Xgen1
, Pgen
, augYbus_solver
,gbus
)
985 # Calculate machine currents and power.
986 Id1
, Iq1
, Pe1
= case
.machineCurrents(Xgen1
, Pgen
, U1
[gbus
])
988 # Update variables that have changed.
989 Vexc1
= abs(U1
[gbus
])
990 Vgen1
= r_
[Id1
, Iq1
, Pe1
]
993 # Second Euler step ---------------------------------------------------
996 dFexc1
= case
.exciter(Xexc1
, Pexc
, Vexc1
)
997 Xexc2
= Xexc0
+ case
.stepsize
/ 2 * (dFexc0
+ dFexc1
)
1000 dFgov1
= case
.governor(Xgov1
, Pgov
, Vgov1
)
1001 Xgov2
= Xgov0
+ case
.stepsize
/ 2 * (dFgov0
+ dFgov1
)
1004 dFgen1
= case
.generator(Xgen1
, Xexc2
, Xgov2
, Pgen
, Vgen1
)
1005 Xgen2
= Xgen0
+ case
.stepsize
/ 2 * (dFgen0
+ dFgen1
)
1007 # Calculate system voltages.
1008 U2
= case
.solveNetwork(Xgen2
, Pgen
, augYbus_solver
, gbus
)
1010 # Calculate machine currents and power.
1011 Id2
, Iq2
, Pe2
= case
.machineCurrents(Xgen2
, Pgen
, U2
[gbus
])
1013 # Update variables that have changed.
1014 Vgen2
= r_
[Id2
, Iq2
, Pe2
]
1015 Vexc2
= abs(U2
[gbus
])
1018 return Xgen2
, Pgen
, Vgen2
, Xexc2
, Pexc
, Vexc2
, \
1019 Xgov2
, Pgov
, Vgov2
, U2
, t
, stepsize
1021 #------------------------------------------------------------------------------
1022 # "RungeKutta" class:
1023 #------------------------------------------------------------------------------
1025 class RungeKutta(object):
1026 """ Standard 4th order Runge-Kutta ODE solver.
1028 Based on RundeKutta.m from MatDyn by Stijn Cole, developed at Katholieke
1029 Universiteit Leuven. See U{http://www.esat.kuleuven.be/electa/teaching/
1030 matdyn/} for more information.
1034 #: Runge-Kutta coefficients.
1035 self
._a
= array([0.0, 0.0, 0.0, 0.0, 1.0/2.0, 0.0, 0.0, 0.0, 0.0,
1036 1.0/2.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0])
1037 #: Runge-Kutta coefficients.
1038 self
._b
= array([1.0/6.0, 2.0/6.0, 2.0/6.0, 1.0/6.0])
1039 # self._c = array([0.0, 1.0/2.0, 1.0/2.0, 1.0]) # not used
1042 def solve(self
, t
, Xgen0
, Pgen
, Vgen0
, Xexc0
, Pexc
, Vexc0
, Xgov0
, Pgov
,
1043 Vgov0
, augYbus_solver
, gbus
, stepsize
):
1045 Xexc1
, Vexc1
, Kexc1
, Xgov1
, Vgov1
, Kgov1
, Xgen1
, Vgen1
, Kgen1
, _
= \
1048 Xexc2
, Vexc2
, Kexc2
, Xgov2
, Vgov2
, Kgov2
, Xgen2
, Vgen2
, Kgen2
, _
= \
1049 self
._k
2(Xexc1
, Vexc1
, Kexc1
, Xgov1
, Vgov1
, Kgov1
, Xgen1
, Vgen1
,
1052 Xexc3
, Vexc3
, Kexc3
, Xgov3
, Vgov3
, Kgov3
, Xgen3
, Vgen3
, Kgen3
, _
= \
1053 self
._k
3(Xexc2
, Vexc2
, Kexc2
, Kexc1
, Xgov2
, Vgov2
, Kgov2
, Kgov1
,
1054 Xgen2
, Vgen2
, Kgen2
, Kgen1
)
1056 Xexc4
, Vexc4
, _
, Xgov4
, Vgov4
, _
, Xgen4
, Vgen4
, _
, U4
= \
1057 self
._k
4(Xexc3
, Vexc3
, Kexc3
, Kexc2
, Kexc1
, Xgov3
, Vgov3
, Kgov3
,
1058 Kgov2
, Kgov1
, Xgen3
, Vgen3
, Kgen3
, Kgen2
, Kgen1
)
1060 return Xgen4
, Vgen4
, Xexc4
, Vexc4
, Xgov4
, Vgov4
, U4
1063 def _k1(self
, Xexc0
, Pexc
, Vexc0
, Xgov0
, Pgov
, Vgov0
, Xgen0
, Pgen
, Vgen0
,
1064 augYbus_solver
, gbus
):
1065 case
= self
.dyn_case
1069 Kexc1
= case
.exciter(Xexc0
, Pexc
, Vexc0
)
1070 Xexc1
= Xexc0
+ case
.stepsize
* a
[1, 0] * Kexc1
1073 Kgov1
= case
.governor(Xgov0
, Pgov
, Vgov0
)
1074 Xgov1
= Xgov0
+ case
.stepsize
* a
[1, 0] * Kgov1
1077 Kgen1
= case
.generator(Xgen0
, Xexc1
, Xgov1
, Pgen
, Vgen0
)
1078 Xgen1
= Xgen0
+ case
.stepsize
* a
[1, 0] * Kgen1
1080 # Calculate system voltages.
1081 U1
= case
.solveNetwork(Xgen1
, Pgen
, augYbus_solver
, gbus
)
1083 # Calculate machine currents and power.
1084 Id1
, Iq1
, Pe1
= case
.machineCurrents(Xgen1
, Pgen
, U1
[gbus
])
1086 # Update variables that have changed
1087 Vexc1
= abs(U1
[gbus
])
1088 Vgen1
= r_
[Id1
, Iq1
, Pe1
];
1091 return Xexc1
, Vexc1
, Kexc1
, Xgov1
, Vgov1
, Kgov1
, Xgen1
, Vgen1
, Kgen1
,U1
1094 def _k2(self
, Xexc1
, Xexc0
, Pexc
, Vexc1
, Kexc1
, Xgov1
, Xgov0
, Pgov
, Vgov1
,
1095 Kgov1
, Xgen1
, Xgen0
, Pgen
, Vgen1
, Kgen1
, augYbus_solver
, gbus
):
1096 case
= self
.dyn_case
1100 Kexc2
= case
.exciter(Xexc1
, Pexc
, Vexc1
)
1101 Xexc2
= Xexc0
+ case
.stepsize
* (a
[2, 0] * Kexc1
+ a
[2, 1] *Kexc2
)
1104 Kgov2
= case
.governor(Xgov1
, Pgov
, Vgov1
)
1105 Xgov2
= Xgov0
+ case
.stepsize
* (a
[2, 0] * Kgov1
+ a
[2, 1] *Kgov2
)
1108 Kgen2
= case
.generator(Xgen1
, Xexc2
, Xgov2
, Pgen
, Vgen1
)
1109 Xgen2
= Xgen0
+ case
.stepsize
* (a
[2, 0] * Kgen1
+ a
[2, 1] *Kgen2
)
1111 # Calculate system voltages.
1112 U2
= case
.solveNetwork(Xgen2
, Pgen
, augYbus_solver
,gbus
)
1114 # Calculate machine currents and power.
1115 Id2
, Iq2
, Pe2
= case
.machineCurrents(Xgen2
, Pgen
, U2
[gbus
])
1117 # Update variables that have changed
1118 Vexc2
= abs(U2
[gbus
])
1119 Vgen2
= r_
[Id2
, Iq2
, Pe2
]
1122 return Xexc2
, Vexc2
, Kexc2
, Xgov2
, Vgov2
, Kgov2
, Xgen2
, Vgen2
, Kgen2
,U2
1125 def _k3(self
, Xexc2
, Xexc0
, Pexc
, Vexc2
, Kexc2
, Kexc1
, Xgov2
, Xgov0
, Pgov
,
1126 Vgov2
, Kgov2
, Kgov1
, Xgen2
, Xgen0
, Pgen
, Vgen2
, Kgen2
, Kgen1
,
1127 augYbus_solver
, gbus
):
1128 case
= self
.dyn_case
1132 Kexc3
= case
.exciter(Xexc2
, Pexc
, Vexc2
)
1133 Xexc3
= Xexc0
+ case
.stepsize
* \
1134 (a
[3, 0] * Kexc1
+ a
[3, 1] * Kexc2
+ a
[3, 2] * Kexc3
)
1137 Kgov3
= case
.governor(Xgov2
, Pgov
, Vgov2
)
1138 Xgov3
= Xgov0
+ case
.stepsize
* \
1139 (a
[3,0] * Kgov1
+ a
[3, 1] * Kgov2
+ a
[3, 2] * Kgov3
)
1142 Kgen3
= case
.generator(Xgen2
, Xexc3
, Xgov3
, Pgen
, Vgen2
)
1143 Xgen3
= Xgen0
+ case
.stepsize
* \
1144 (a
[3, 0] * Kgen1
+ a
[3, 1] * Kgen2
+ a
[3, 2] * Kgen3
)
1146 # Calculate system voltages.
1147 U3
= case
.solveNetwork(Xgen3
, Pgen
, augYbus_solver
,gbus
)
1149 # Calculate machine currents and power
1150 Id3
, Iq3
, Pe3
= case
.machineCurrents(Xgen3
, Pgen
, U3
[gbus
])
1152 # Update variables that have changed.
1153 Vexc3
= abs(U3
[gbus
])
1154 Vgen3
= r_
[Id3
, Iq3
, Pe3
]
1157 return Xexc3
, Vexc3
, Kexc3
, Xgov3
, Vgov3
, Kgov3
, Xgen3
, Vgen3
, Kgen3
,U3
1160 def _k4(self
, Xexc3
, Xexc0
, Pexc
, Vexc3
, Kexc3
, Kexc2
, Kexc1
, Xgov3
, Xgov0
,
1161 Pgov
, Vgov3
, Kgov3
, Kgov2
, Kgov1
, Xgen3
, Xgen0
, Pgen
, Vgen3
, Kgen3
,
1162 Kgen2
, Kgen1
, augYbus_solver
, gbus
):
1163 case
= self
.dyn_case
1167 Kexc4
= case
.exciter(Xexc3
, Pexc
, Vexc3
)
1168 Xexc4
= Xexc0
+ case
.stepsize
* \
1169 (b
[0] * Kexc1
+ b
[1] * Kexc2
+ b
[2] * Kexc3
+ b
[3] * Kexc4
)
1172 Kgov4
= case
.governor(Xgov3
, Pgov
, Vgov3
)
1173 Xgov4
= Xgov0
+ case
.stepsize
* \
1174 (b
[0] * Kgov1
+ b
[1] * Kgov2
+ b
[2] * Kgov3
+ b
[3] * Kgov4
)
1177 Kgen4
= case
.generator(Xgen3
, Xexc4
, Xgov4
, Pgen
, Vgen3
)
1178 Xgen4
= Xgen0
+ case
.stepsize
* \
1179 (b
[0] * Kgen1
+ b
[1] * Kgen2
+ b
[2] * Kgen3
+ b
[3] * Kgen4
)
1181 # Calculate system voltages.
1182 U4
= case
.solveNetwork(Xgen4
, Pgen
, augYbus_solver
,gbus
)
1184 # Calculate machine currents and power.
1185 Id4
, Iq4
, Pe4
= case
.machineCurrents(Xgen4
, Pgen
, U4
[gbus
])
1187 # Update variables that have changed
1188 Vexc4
= abs(U4
[gbus
])
1189 Vgen4
= r_
[Id4
, Iq4
, Pe4
]
1192 return Xexc4
, Vexc4
, Kexc4
, Xgov4
, Vgov4
, Kgov4
, Xgen4
, Vgen4
, Kgen4
,U4
1194 #------------------------------------------------------------------------------
1195 # "RungeKuttaFehlberg" class:
1196 #------------------------------------------------------------------------------
1198 class RungeKuttaFehlberg(RungeKutta
):
1199 """ Runge-Kutta Fehlberg ODE solver.
1201 Based on RungeKuttaFehlberg.m from MatDyn by Stijn Cole, developed at
1202 Katholieke Universiteit Leuven. See U{http://www.esat.kuleuven.be/electa/
1203 teaching/matdyn/} for more information.
1206 def __init__(self
, t
, Xgen0
, Pgen
, Vgen0
, Xexc0
, Pexc
, Vexc0
, Xgov0
, Pgov
,
1207 Vgov0
, augYbus_solver
, gbus
, stepsize
):
1209 super(self
, RungeKuttaFehlberg
).__init
__(t
, Xgen0
, Pgen
, Vgen0
, Xexc0
,
1210 Pexc
, Vexc0
, Xgov0
, Pgov
, Vgov0
, augYbus_solver
, gbus
, stepsize
)
1212 #: Runge-Kutta coefficients
1213 self
._a
= array([0.0, 0.0, 0.0, 0.0, 0.0, 1.0/4.0, 0.0, 0.0, 0.0, 0.0,
1214 3.0/32.0, 9.0/32.0, 0.0, 0.0, 0.0, 1932.0/2197.0,
1215 -7200.0/2197.0, 7296.0/2197.0, 0.0, 0.0, 439.0/216.0,
1216 -8.0, 3680.0/513.0, -845.0/4104.0, 0.0, -8.0/27.0,
1217 2.0, -3544.0/2565.0, 1859.0/4104.0, -11.0/40.0])
1218 #: Runge-Kutta coefficients.
1219 self
._b
1 = array([25.0/216.0, 0.0, 1408.0/2565.0, 2197.0/4104.0,
1221 #: Runge-Kutta coefficients.
1222 self
._b
2 = array([16.0/135.0, 0.0, 6656.0/12825.0, 28561.0/56430.0,
1223 -9.0/50.0, 2.0/55.0,])
1224 # c = array([0.0, 1.0/4.0, 3.0/8.0, 12.0/13.0, 1.0, 1.0/2.0,])#not used
1228 case
= self
.dyn_case
1236 while accept
== False:
1239 Xexc1
, Vexc1
, Kexc1
, Xgov1
, Vgov1
, Kgov1
, Xgen1
, Vgen1
, Kgen1
, _
=\
1242 Xexc2
, Vexc2
, Kexc2
, Xgov2
, Vgov2
, Kgov2
, Xgen2
, Vgen2
, Kgen2
, _
=\
1243 self
._k
2(Xexc1
, Vexc1
, Kexc1
, Xgov1
, Vgov1
, Kgov1
, Xgen1
,
1246 Xexc3
, Vexc3
, Kexc3
, Xgov3
, Vgov3
, Kgov3
, Xgen3
, Vgen3
, Kgen3
, _
=\
1247 self
._k
3(Xexc2
, Vexc2
, Kexc2
, Kexc1
, Xgov2
, Vgov2
, Kgov2
,
1248 Kgov1
, Xgen2
, Vgen2
, Kgen2
, Kgen1
)
1250 Xexc4
, Vexc4
, Kexc4
, Xgov4
, Vgov4
, Kgov4
, Xgen4
, Vgen4
, Kgen4
, _
=\
1251 self
._k
4(Xexc3
, Vexc3
, Kexc3
, Kexc2
, Kexc1
, Xgov3
, Vgov3
,
1252 Kgov3
, Kgov2
, Kgov1
, Xgen3
, Vgen3
, Kgen3
, Kgen2
, Kgen1
)
1254 Xexc5
, Vexc5
, Kexc5
, Xgov5
, Vgov5
, Kgov5
, Xgen5
, Vgen5
, Kgen5
, _
=\
1255 self
._k
5(Xexc4
, Vexc4
, Kexc4
, Kexc3
, Kexc2
, Kexc1
, Xgov4
,
1256 Vgov4
, Kgov4
, Kgov3
, Kgov2
, Kgov1
, Xgen4
, Vgen4
, Kgen4
,
1257 Kgen3
, Kgen2
, Kgen1
)
1259 Xexc6
, Vexc6
, Kexc6
, Xgov6
, Vgov6
, Kgov6
, Xgen6
, Vgen6
, Kgen6
, U6
=\
1260 self
._k
6(Xexc5
, Vexc5
, Kexc5
, Kexc4
, Kexc3
, Kexc2
, Kexc1
,
1261 Xgov5
, Vgov5
, Kgov5
, Kgov4
, Kgov3
, Kgov2
, Kgov1
, Xgen5
,
1262 Vgen5
, Kgen5
, Kgen4
, Kgen3
, Kgen2
, Kgen1
)
1264 # Second, higher order solution.
1265 Xexc62
= self
.Xexc0
+ case
.stepsize
* \
1266 (b2
[0] * Kexc1
+ b2
[1] * Kexc2
+ b2
[2] * Kexc3
+ \
1267 b2
[3] * Kexc4
+ b2
[4] * Kexc5
+ b2
[5] * Kexc6
)
1268 Xgov62
= self
.Xgov0
+ case
.stepsize
* \
1269 (b2
[0] * Kgov1
+ b2
[1] * Kgov2
+ b2
[2] * Kgov3
+ \
1270 b2
[3] * Kgov4
+ b2
[4] * Kgov5
+ b2
[5] * Kgov6
)
1271 Xgen62
= self
.Xgen0
+ case
.stepsize
* \
1272 (b2
[0] * Kgen1
+ b2
[1] * Kgen2
+ b2
[2] * Kgen3
+ \
1273 b2
[3] * Kgen4
+ b2
[4] * Kgen5
+ b2
[5] * Kgen6
)
1276 Xexc
= abs((Xexc62
- Xexc6
).T
)
1277 Xgov
= abs((Xgov62
- Xgov6
).T
)
1278 Xgen
= abs((Xgen62
- Xgen6
).T
)
1279 errest
= max( r_
[max(max(Xexc
)), max(max(Xgov
)), max(max(Xgen
))] )
1284 q
= 0.84 * (self
.tol
/ errest
)^
(1.0/4.0)
1286 if errest
< self
.tol
:
1311 stepsize
= min(max(q
, 0.1), facmax
) * case
.stepsize
1314 stepsize
= min(max(q
, 0.1), facmax
) * case
.stepsize
1316 if stepsize
> self
.maxstepsize
:
1317 stepsize
= self
.maxstepsize
1319 return Xgen0
, Pgen0
, Vgen0
, Xexc0
, Pexc0
, Vexc0
, Xgov0
, Pgov0
, Vgov0
, \
1320 U0
, errest
, failed
, self
.t
, stepsize
1323 def _k4(self
, Xexc3
, Vexc3
, Kexc3
, Kexc2
, Kexc1
, Xgov3
, Vgov3
, Kgov3
,
1324 Kgov2
, Kgov1
, Xgen3
, Vgen3
, Kgen3
, Kgen2
, Kgen1
):
1325 # Overrides the standard Runge-Kutta K4 method.
1326 case
= self
.dyn_case
1330 Kexc4
= case
.exciter(Xexc3
, self
.Pexc
, Vexc3
)
1331 Xexc4
= self
.Xexc0
+ case
.stepsize
* \
1332 (a
[4, 0] * Kexc1
+ a
[4, 1] * Kexc2
+ a
[4, 2] * Kexc3
+
1336 Kgov4
= case
.governor(Xgov3
, self
.Pgov
, Vgov3
)
1337 Xgov4
= self
.Xgov0
+ case
.stepsize
* \
1338 (a
[4, 0] * Kgov1
+ a
[4, 1] * Kgov2
+ a
[4, 2] * Kgov3
+
1342 Kgen4
= case
.generator(Xgen3
, Xexc4
, Xgov4
, self
.Pgen
, Vgen3
)
1343 Xgen4
= self
.Xgen0
+ case
.stepsize
* \
1344 (a
[4, 0] * Kgen1
+ a
[4, 1] * Kgen2
+ a
[4, 2] * Kgen3
+
1347 # Calculate system voltages.
1348 U4
= case
.solveNetwork(Xgen4
, self
.Pgen
, self
.augYbus_solver
,self
.gbus
)
1350 # Calculate machine currents and power.
1351 Id4
, Iq4
, Pe4
= case
.machineCurrents(Xgen4
, self
.Pgen
, U4
[self
.gbus
])
1353 # Update variables that have changed
1354 Vexc4
= abs(U4
[self
.gbus
])
1355 Vgen4
= r_
[Id4
, Iq4
, Pe4
]
1358 return Xexc4
, Vexc4
, Kexc4
, Xgov4
, Vgov4
, Kgov4
, Xgen4
, Vgen4
, Kgen4
,U4
1361 def _k5(self
, Xexc4
, Vexc4
, Kexc4
, Kexc3
, Kexc2
, Kexc1
, Xgov4
, Vgov4
,Kgov4
,
1362 Kgov3
, Kgov2
, Kgov1
, Xgen4
, Vgen4
, Kgen4
, Kgen3
, Kgen2
, Kgen1
):
1363 case
= self
.dyn_case
1367 Kexc5
= case
.exciter(Xexc4
, self
.Pexc
, Vexc4
)
1368 Xexc5
= self
.Xexc0
+ case
.stepsize
* \
1369 (a
[5, 0] * Kexc1
+ a
[5, 1] * Kexc2
+ a
[5, 2] * Kexc3
+ \
1370 a
[5, 3] * Kexc4
+ a
[5, 4] * Kexc5
)
1373 Kgov5
= case
.governor(Xgov4
, self
.Pgov
, Vgov4
)
1374 Xgov5
= self
.Xgov0
+ case
.stepsize
* \
1375 (a
[5, 0] * Kgov1
+ a
[5, 1] * Kgov2
+ a
[5, 2] * Kgov3
+ \
1376 a
[5, 3] * Kgov4
+ a
[5, 4] * Kgov5
)
1379 Kgen5
= case
.generator(Xgen4
, Xexc5
, Xgov5
, self
.Pgen
, Vgen4
)
1380 Xgen5
= self
.Xgen0
+ case
.stepsize
* \
1381 (a
[5, 0] * Kgen1
+ a
[5, 1] * Kgen2
+ a
[5, 2] * Kgen3
+ \
1382 a
[5, 3] * Kgen4
+ a
[5, 4] * Kgen5
)
1384 # Calculate system voltages.
1385 U5
= case
.solveNetwork(Xgen5
, self
.Pgen
, self
.augYbus_solver
,
1388 # Calculate machine currents and power.
1389 Id5
, Iq5
, Pe5
= case
.machineCurrents(Xgen5
, self
.Pgen
,
1392 # Update variables that have changed.
1393 Vexc5
= abs(U5
[self
.gbus
])
1394 Vgen5
= r_
[Id5
, Iq5
, Pe5
]
1397 return Xexc5
, Vexc5
, Kexc5
, Xgov5
, Vgov5
, Kgov5
, Xgen5
, Vgen5
, Kgen5
,U5
1400 def _k6(self
, Xexc5
, Vexc5
, Kexc5
, Kexc4
, Kexc3
, Kexc2
, Kexc1
, Xgov5
,
1401 Vgov5
, Kgov5
, Kgov4
, Kgov3
, Kgov2
, Kgov1
, Xgen5
, Vgen5
, Kgen5
,
1402 Kgen4
, Kgen3
, Kgen2
, Kgen1
):
1403 case
= self
.dyn_case
1407 Kexc6
= case
.exciter(Xexc5
, self
.Pexc
, Vexc5
)
1408 Xexc6
= self
.Xexc0
+ case
.stepsize
* \
1409 (b1
[0] * Kexc1
+ b1
[1] * Kexc2
+ b1
[2] * Kexc3
+ \
1410 b1
[3] * Kexc4
+ b1
[4] * Kexc5
+ b1
[5] * Kexc6
)
1413 Kgov6
= case
.governor(Xgov5
, self
.Pgov
, Vgov5
)
1414 Xgov6
= self
.Xgov0
+ case
.stepsize
* \
1415 (b1
[0] * Kgov1
+ b1
[1] * Kgov2
+ b1
[2] * Kgov3
+ \
1416 b1
[3] * Kgov4
+ b1
[4] * Kgov5
+ b1
[5] * Kgov6
)
1419 Kgen6
= case
.generator(Xgen5
, Xexc6
, Xgov6
, self
.Pgen
, Vgen5
)
1420 Xgen6
= self
.Xgen0
+ case
.stepsize
* \
1421 (b1
[0] * Kgen1
+ b1
[1] * Kgen2
+ b1
[2] * Kgen3
+ \
1422 b1
[3] * Kgen4
+ b1
[4] * Kgen5
+ b1
[5] * Kgen6
)
1424 # Calculate system voltages.
1425 U6
= case
.solveNetwork(Xgen6
, self
.Pgen
, self
.augYbus_solver
,
1428 # Calculate machine currents and power.
1429 Id6
, Iq6
, Pe6
= case
.machineCurrents(Xgen6
, self
.Pgen
,
1432 # Update variables that have changed.
1433 Vexc6
= abs(U6
[self
.gbus
])
1434 Vgen6
= r_
[Id6
, Iq6
, Pe6
]
1437 return Xexc6
, Vexc6
, Kexc6
, Xgov6
, Vgov6
, Kgov6
, Xgen6
, Vgen6
, Kgen6
,U6
1439 # # K1 --------------------------------------------------------------
1442 # Kexc1 = case.exciter(self.Xexc0, self.Pexc, self.Vexc0)
1443 # Xexc1 = self.Xexc0 + case.stepsize * a[1, 0] * Kexc1
1446 # Kgov1 = case.governor(self.Xgov0, self.Pgov, self.Vgov0)
1447 # Xgov1 = self.Xgov0 + case.stepsize * a[1, 0] * Kgov1
1450 # Kgen1 = case.generator(self.Xgen0, Xexc1, Xgov1, self.Pgen,
1452 # Xgen1 = self.Xgen0 + case.stepsize * a[1, 0] * Kgen1
1454 # # Calculate system voltages.
1455 # U1 = case.solveNetwork(Xgen1, self.Pgen, self.augYbus_solver,
1458 # # Calculate machine currents and power.
1459 # Id1, Iq1, Pe1 = case.machineCurrents(Xgen1, self.Pgen,
1462 # # Update variables that have changed.
1463 # Vexc1 = abs(U1[self.gbus])
1464 # Vgen1 = r_[Id1, Iq1, Pe1]
1465 # Vgov1 = Xgen1[:, 1]
1467 # # K2 --------------------------------------------------------------
1470 # Kexc2 = case.exciter(Xexc1, self.Pexc, Vexc1)
1471 # Xexc2 = self.Xexc0 + case.stepsize * \
1472 # (a[2, 0] * Kexc1 + a[2, 1] * Kexc2)
1475 # Kgov2 = case.governor(Xgov1, self.Pgov, Vgov1)
1476 # Xgov2 = self.Xgov0 + case.stepsize * \
1477 # (a[2, 0] * Kgov1 + a[2, 1] * Kgov2)
1480 # Kgen2 = case.generator(Xgen1, Xexc2, Xgov2, self.Pgen, Vgen1)
1481 # Xgen2 = self.Xgen0 + case.stepsize * \
1482 # (a[2, 0] * Kgen1 + a[2, 1] * Kgen2)
1484 # # Calculate system voltages.
1485 # U2 = case.solveNetwork(Xgen2, self.Pgen, self.augYbus_solver,
1488 # # Calculate machine currents and power.
1489 # Id2, Iq2, Pe2 = case.machineCurrents(Xgen2, self.Pgen,
1492 # # Update variables that have changed
1493 # Vexc2 = abs(U2[self.gbus])
1494 # Vgen2 = r_[Id2, Iq2, Pe2]
1495 # Vgov2 = Xgen2[:, 1]
1497 # # K3 --------------------------------------------------------------
1500 # Kexc3 = case.exciter(Xexc2, self.Pexc, Vexc2)
1501 # Xexc3 = self.Xexc0 + case.stepsize * \
1502 # (a[3, 0] * Kexc1 + a[3, 1] * Kexc2 + a[3, 2] * Kexc3)
1505 # Kgov3 = case.governor(Xgov2, self.Pgov, Vgov2)
1506 # Xgov3 = self.Xgov0 + case.stepsize * \
1507 # (a[3, 0] * Kgov1 + a[3, 1] * Kgov2 + a[3, 2] * Kgov3)
1510 # Kgen3 = case.generator(Xgen2, Xexc3, Xgov3, self.Pgen, Vgen2)
1511 # Xgen3 = self.Xgen0 + case.stepsize * \
1512 # (a[3, 0] * Kgen1 + a[3, 1] * Kgen2 + a[3, 2] * Kgen3)
1514 # # Calculate system voltages
1515 # U3 = case.solveNetwork(Xgen3, self.Pgen, self.augYbus_solver,
1518 # # Calculate machine currents and power.
1519 # Id3, Iq3, Pe3 = case.machineCurrents(Xgen3, self.Pgen,
1522 # # Update variables that have changed
1523 # Vexc3 = abs(U3[self.gbus])
1524 # Vgen3 = r_[Id3, Iq3, Pe3]
1525 # Vgov3 = Xgen3[:, 1]
1527 # # K4 --------------------------------------------------------------
1530 # Kexc4 = case.exciter(Xexc3, self.Pexc, Vexc3)
1531 # Xexc4 = self.Xexc0 + case.stepsize * \
1532 # (a[4, 0] * Kexc1 + a[4, 1] * Kexc2 + a[4, 2] * Kexc3 +
1536 # Kgov4 = case.governor(Xgov3, self.Pgov, Vgov3)
1537 # Xgov4 = self.Xgov0 + case.stepsize * \
1538 # (a[4, 0] * Kgov1 + a[4, 1] * Kgov2 + a[4, 2] * Kgov3 +
1542 # Kgen4 = case.generator(Xgen3, Xexc4, Xgov4, self.Pgen, Vgen3)
1543 # Xgen4 = self.Xgen0 + case.stepsize * \
1544 # (a[4, 0] * Kgen1 + a[4, 1] * Kgen2 + a[4, 2] * Kgen3 +
1547 # # Calculate system voltages.
1548 # U4 = case.solveNetwork(Xgen4, self.Pgen, self.augYbus_solver,
1551 # # Calculate machine currents and power.
1552 # Id4, Iq4, Pe4 = case.machineCurrents(Xgen4, self.Pgen,
1555 # # Update variables that have changed.
1556 # Vexc4 = abs(U4[self.gbus])
1557 # Vgen4 = r_[Id4, Iq4, Pe4]
1558 # Vgov4 = Xgen4[:, 1]
1560 #------------------------------------------------------------------------------
1561 # "RungeKuttaHighamHall" class:
1562 #------------------------------------------------------------------------------
1564 class RungeKuttaHighamHall(RungeKuttaFehlberg
):
1565 """ Runge-Kutta Higham and Hall ODE solver.
1567 Based on RungeKuttaHighamHall.m from MatDyn by Stijn Cole, developed at
1568 Katholieke Universiteit Leuven. See U{http://www.esat.kuleuven.be/electa/
1569 teaching/matdyn/} for more information.
1572 def __init__(self
, t
, Xgen0
, Pgen
, Vgen0
, Xexc0
, Pexc
, Vexc0
, Xgov0
, Pgov
,
1573 Vgov0
, augYbus_solver
, gbus
, stepsize
):
1575 super(self
, RungeKuttaHighamHall
).__init
__(t
, Xgen0
, Pgen
, Vgen0
,Xexc0
,
1576 Pexc
, Vexc0
, Xgov0
, Pgov
, Vgov0
, augYbus_solver
, gbus
, stepsize
)
1578 #: Runge-Kutta coefficients.
1579 self
._a
= array([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 2.0/9.0, 0.0, 0.0, 0.0,
1580 0.0, 0.0, 1.0/12.0, 1.0/4.0, 0.0, 0.0, 0.0, 0.0,
1581 1.0/8.0, 0.0, 3.0/8.0, 0.0, 0.0, 0.0, 91.0/500.0,
1582 -27.0/100.0, 78.0/125.0, 8.0/125.0, 0.0, 0.0,
1583 -11.0/20.0, 27.0/20.0, 12.0/5.0, -36.0/5.0, 5.0, 0.0,
1584 1.0/12.0, 0.0, 27.0/32.0, -4.0/3.0, 125.0/96.0,
1586 #: Runge-Kutta coefficients.
1587 self
._b
1 = array([1.0/12.0, 0.0, 27.0/32.0, -4.0/3.0, 125.0/96.0,
1589 #: Runge-Kutta coefficients.
1590 self
._b
2 = array([2.0/15.0, 0.0, 27.0/80.0, -2.0/15.0, 25.0/48.0,
1591 1.0/24.0, 1.0/10.0,])
1592 # c = array([0.0, 2.0/9.0, 1.0/3.0, 1.0/2.0, 3.0/5.0, 1.0, 1.0,])
1596 case
= self
.dyn_case
1604 while accept
== False:
1607 Xexc1
, Vexc1
, Kexc1
, Xgov1
, Vgov1
, Kgov1
, Xgen1
, Vgen1
, Kgen1
, _
=\
1610 Xexc2
, Vexc2
, Kexc2
, Xgov2
, Vgov2
, Kgov2
, Xgen2
, Vgen2
, Kgen2
, _
=\
1611 self
._k
2(Xexc1
, Vexc1
, Kexc1
, Xgov1
, Vgov1
, Kgov1
, Xgen1
,
1614 Xexc3
, Vexc3
, Kexc3
, Xgov3
, Vgov3
, Kgov3
, Xgen3
, Vgen3
, Kgen3
, _
=\
1615 self
._k
3(Xexc2
, Vexc2
, Kexc2
, Kexc1
, Xgov2
, Vgov2
, Kgov2
,
1616 Kgov1
, Xgen2
, Vgen2
, Kgen2
, Kgen1
)
1618 Xexc4
, Vexc4
, Kexc4
, Xgov4
, Vgov4
, Kgov4
, Xgen4
, Vgen4
, Kgen4
, _
=\
1619 self
._k
4(Xexc3
, Vexc3
, Kexc3
, Kexc2
, Kexc1
, Xgov3
, Vgov3
,
1620 Kgov3
, Kgov2
, Kgov1
, Xgen3
, Vgen3
, Kgen3
, Kgen2
, Kgen1
)
1622 Xexc5
, Vexc5
, Kexc5
, Xgov5
, Vgov5
, Kgov5
, Xgen5
, Vgen5
, Kgen5
, _
=\
1623 self
._k
5(Xexc4
, Vexc4
, Kexc4
, Kexc3
, Kexc2
, Kexc1
, Xgov4
,
1624 Vgov4
, Kgov4
, Kgov3
, Kgov2
, Kgov1
, Xgen4
, Vgen4
, Kgen4
,
1625 Kgen3
, Kgen2
, Kgen1
)
1627 Xexc6
, Vexc6
, Kexc6
, Xgov6
, Vgov6
, Kgov6
, Xgen6
, Vgen6
, Kgen6
, _
=\
1628 self
._k
6(Xexc5
, Vexc5
, Kexc5
, Kexc4
, Kexc3
, Kexc2
, Kexc1
,
1629 Xgov5
, Vgov5
, Kgov5
, Kgov4
, Kgov3
, Kgov2
, Kgov1
, Xgen5
,
1630 Vgen5
, Kgen5
, Kgen4
, Kgen3
, Kgen2
, Kgen1
)
1632 Xexc7
, _
, Kexc7
, Xgov7
, _
, Kgov7
, Xgen7
, _
, Kgen7
, U7
= \
1633 self
._k
6(Xexc6
, Vexc6
, Kexc6
, Kexc5
, Kexc4
, Kexc3
, Kexc2
,
1634 Kexc1
, Xgov6
, Vgov6
, Kgov6
, Kgov5
, Kgov4
, Kgov3
, Kgov2
,
1635 Kgov1
, Xgen6
, Vgen6
, Kgen6
, Kgen5
, Kgen4
, Kgen3
, Kgen2
,
1638 # Second, higher order solution.
1639 Xexc72
= self
.Xexc0
+ case
.stepsize
* \
1640 (b2
[0] * Kexc1
+ b2
[1] * Kexc2
+ b2
[2] * Kexc3
+
1641 b2
[3] * Kexc4
+ b2
[4] * Kexc5
+ b2
[5] * Kexc6
+ b2
[6] * Kexc7
)
1642 Xgov72
= self
.Xgov0
+ case
.stepsize
* \
1643 (b2
[0] * Kgov1
+ b2
[1] * Kgov2
+ b2
[2] * Kgov3
+
1644 b2
[3] * Kgov4
+ b2
[4] * Kgov5
+ b2
[5] * Kgov6
+ b2
[6] * Kgov7
)
1645 Xgen72
= self
.Xgen0
+ case
.stepsize
* \
1646 (b2
[0] * Kgen1
+ b2
[1] * Kgen2
+ b2
[2] * Kgen3
+
1647 b2
[3] * Kgen4
+ b2
[4] * Kgen5
+ b2
[5] * Kgen6
+ b2
[6] * Kgen7
)
1650 Xexc
= abs((Xexc72
- Xexc7
).T
)
1651 Xgov
= abs((Xgov72
- Xgov7
).T
)
1652 Xgen
= abs((Xgen72
- Xgen7
).T
)
1653 errest
= max( r_
[max(max(Xexc
)), max(max(Xgov
)), max(max(Xgen
))] )
1658 q
= 0.84 * (self
.tol
/ errest
)^
(1.0 / 4.0)
1660 if errest
< self
.tol
:
1685 stepsize
= min(max(q
, 0.1), facmax
) * case
.stepsize
1688 stepsize
= min(max(q
, 0.1), facmax
) * case
.stepsize
1690 if stepsize
> self
.maxstepsize
:
1691 stepsize
= self
.maxstepsize
1693 return Xgen0
, Pgen0
, Vgen0
, Xexc0
, Pexc0
, Vexc0
, Xgov0
, Pgov0
, Vgov0
, \
1694 U0
, errest
, failed
, self
.t
, stepsize
1697 def _k6(self
, Xexc5
, Vexc5
, Kexc5
, Kexc4
, Kexc3
, Kexc2
, Kexc1
, Xgov5
,
1698 Vgov5
, Kgov5
, Kgov4
, Kgov3
, Kgov2
, Kgov1
, Xgen5
, Vgen5
, Kgen5
,
1699 Kgen4
, Kgen3
, Kgen2
, Kgen1
):
1700 # Overrides the K6 method from the Runge-Kutta Fehlberg solver.
1701 case
= self
.dyn_case
1705 Kexc6
= case
.exciter(Xexc5
, self
.Pexc
, Vexc5
)
1706 Xexc6
= self
.Xexc0
+ case
.stepsize
* \
1707 (a
[6, 0] * Kexc1
+ a
[6, 1] * Kexc2
+ a
[6, 2] * Kexc3
+ \
1708 a
[6, 3] * Kexc4
+ a
[6, 4] * Kexc5
+ a
[6, 5] * Kexc6
)
1711 Kgov6
= case
.governor(Xgov5
, self
.Pgov
, Vgov5
)
1712 Xgov6
= self
.Xgov0
+ case
.stepsize
* \
1713 (a
[6, 0] * Kgov1
+ a
[6, 1] * Kgov2
+ a
[6, 2] * Kgov3
+ \
1714 a
[6, 3] * Kgov4
+ a
[6, 4] * Kgov5
+ a
[6, 5] * Kgov6
)
1717 Kgen6
= case
.generator(Xgen5
, Xexc6
, Xgov6
, self
.Pgen
, Vgen5
)
1718 Xgen6
= self
.Xgen0
+ case
.stepsize
* \
1719 (a
[6, 0] * Kgen1
+ a
[6, 1] * Kgen2
+ a
[6, 2] * Kgen3
+ \
1720 a
[6, 3] * Kgen4
+ a
[6, 4] * Kgen5
+ a
[6, 5] * Kgen6
)
1722 # Calculate system voltages.
1723 U6
= case
.solveNetwork(Xgen6
, self
.Pgen
, self
.augYbus_solver
,
1726 # Calculate machine currents and power.
1727 Id6
, Iq6
, Pe6
= case
.machineCurrents(Xgen6
, self
.Pgen
,
1730 # Update variables that have changed.
1731 Vexc6
= abs(U6
[self
.gbus
])
1732 Vgen6
= r_
[Id6
, Iq6
, Pe6
]
1735 return Xexc6
, Vexc6
, Kexc6
, Xgov6
, Vgov6
, Kgov6
, Xgen6
, Vgen6
, Kgen6
,U6
1738 def _k7(self
, Xexc6
, Vexc6
, Kexc6
, Kexc5
, Kexc4
, Kexc3
, Kexc2
, Kexc1
,
1739 Xgov6
, Vgov6
, Kgov6
, Kgov5
, Kgov4
, Kgov3
, Kgov2
, Kgov1
, Xgen6
,
1740 Vgen6
, Kgen6
, Kgen5
, Kgen4
, Kgen3
, Kgen2
, Kgen1
):
1741 case
= self
.dyn_case
1745 Kexc7
= case
.exciter(Xexc6
, self
.Pexc
, Vexc6
)
1746 Xexc7
= self
.Xexc0
+ case
.stepsize
* \
1747 (b1
[0] * Kexc1
+ b1
[1] * Kexc2
+ b1
[2] * Kexc3
+ b1
[3] * Kexc4
+
1748 b1
[4] * Kexc5
+ b1
[5] * Kexc6
+ b1
[6] * Kexc7
)
1751 Kgov7
= case
.governor(Xgov6
, self
.Pgov
, Vgov6
)
1752 Xgov7
= self
.Xgov0
+ case
.stepsize
* \
1753 (b1
[0]* Kgov1
+ b1
[1] * Kgov2
+ b1
[2] * Kgov3
+ b1
[3] * Kgov4
+
1754 b1
[4] * Kgov5
+ b1
[5] * Kgov6
+ b1
[6] * Kgov7
)
1757 Kgen7
= case
.generator(Xgen6
, Xexc7
, Xgov7
, self
.Pgen
, Vgen6
)
1758 Xgen7
= self
.Xgen0
+ case
.stepsize
* \
1759 (b1
[0] * Kgen1
+ b1
[1] * Kgen2
+ b1
[2] * Kgen3
+ b1
[3] * Kgen4
+
1760 b1
[4] * Kgen5
+ b1
[5] * Kgen6
+ b1
[6] * Kgen7
)
1762 # Calculate system voltages.
1763 U7
= case
.solveNetwork(Xgen7
, self
.Pgen
, self
.augYbus_solver
,
1766 # Calculate machine currents and power.
1767 Id7
, Iq7
, Pe7
= case
.machineCurrents(Xgen7
, self
.Pgen
, U7
[self
.gbus
])
1769 # Update variables that have changed
1770 Vexc7
= abs(U7
[self
.gbus
])
1771 Vgen7
= r_
[Id7
, Iq7
, Pe7
]
1774 return Xexc7
, Vexc7
, Kexc7
, Xgov7
, Vgov7
, Kgov7
, Xgen7
, Vgen7
, Kgen7
,U7
1776 #------------------------------------------------------------------------------
1777 # "ModifiedEuler2" class:
1778 #------------------------------------------------------------------------------
1780 class ModifiedEuler2(object):
1781 """ Modified Euler ODE solver with check on interface errors.
1783 Based on ModifiedEuler2.m from MatDyn by Stijn Cole, developed at
1784 Katholieke Universiteit Leuven. See U{http://www.esat.kuleuven.be/electa/
1785 teaching/matdyn/} for more information.
1788 def __init__(self
, t
, Xgen0
, Pgen
, Vgen0
, Xexc0
, Pexc
, Vexc0
, Xgov0
, Pgov
,
1789 Vgov0
, augYbus_solver
, gbus
, stepsize
):
1791 self
.dyn_case
= None
1809 case
= self
.dyn_case
1813 # First Prediction Step -----------------------------------------------
1816 dFexc0
= case
.exciter(self
.Xexc0
, self
.Pexc
, self
.Vexc0
)
1817 Xexc_new
= self
.Xexc0
+ case
.stepsize
* dFexc0
1820 dFgov0
= case
.governor(self
.Xgov0
, self
.Pgov
, self
.Vgov0
)
1821 Xgov_new
= self
.Xgov0
+ case
.stepsize
* dFgov0
1824 dFgen0
= case
.generator(self
.Xgen0
, Xexc_new
, Xgov_new
, self
.Pgen
,
1826 Xgen_new
= self
.Xgen0
+ case
.stepsize
* dFgen0
1828 Vexc_new
= self
.Vexc0
1829 Vgov_new
= self
.Vgov0
1830 Vgen_new
= self
.Vgen0
1832 for i
in range(self
.maxit
):
1841 # Calculate system voltages
1842 U_new
= case
.solveNetwork(Xgen_new
, self
.Pgen
, self
.augYbus_solver
,
1845 # Calculate machine currents and power.
1846 Id_new
, Iq_new
, Pe_new
= case
.machineCurrents(Xgen_new
, self
.Pgen
,
1849 # Update variables that have changed.
1850 Vgen_new
= r_
[Id_new
, Iq_new
, Pe_new
]
1851 Vexc_new
= abs(U_new
[self
.gbus
])
1852 Vgov_new
= Xgen_new
[:,1]
1854 # Correct the prediction, and find new values of x ----------------
1857 dFexc1
= case
.exciter(Xexc_old
, self
.Pexc
, Vexc_new
)
1858 Xexc_new
= self
.Xexc0
+ case
.stepsize
/ 2.0 * (dFexc0
+ dFexc1
)
1861 dFgov1
= case
.governor(Xgov_old
, self
.Pgov
, Vgov_new
)
1862 Xgov_new
= self
.Xgov0
+ case
.stepsize
/ 2.0 * (dFgov0
+ dFgov1
)
1865 dFgen1
= case
.generator(Xgen_old
, Xexc_new
, Xgov_new
, self
.Pgen
,
1867 Xgen_new
= self
.Xgen0
+ case
.stepsize
/ 2.0 * (dFgen0
+ dFgen1
)
1870 Xexc_d
= abs((Xexc_new
- Xexc_old
).T
)
1871 Xgov_d
= abs((Xgov_new
- Xgov_old
).T
)
1872 Xgen_d
= abs((Xgen_new
- Xgen_old
).T
)
1874 Vexc_d
= abs((Vexc_new
- Vexc_old
).T
)
1875 Vgov_d
= abs((Vgov_new
- Vgov_old
).T
)
1876 Vgen_d
= abs((Vgen_new
- Vgen_old
).T
)
1878 errest
= max( r_
[max(max(Vexc_d
)), max(max(Vgov_d
)), max(max(Vgen_d
)), max(max(Xexc_d
)), max(max(Xgov_d
)), max(max(Xgen_d
))] )
1880 if errest
< self
.tol
:
1881 break # solution found
1895 return Xgen0
, Pgen0
, Vgen0
, Xexc0
, Pexc0
, Vexc0
, Xgov0
, \
1896 Pgov0
, Vgov0
, U0
, self
.t
, eulerfailed
, case
.stepsize
1912 return Xgen0
, Pgen0
, Vgen0
, Xexc0
, Pexc0
, Vexc0
, Xgov0
, Pgov0
, Vgov0
, \
1913 U0
, self
.t
, eulerfailed
, case
.stepsize
1915 #------------------------------------------------------------------------------
1916 # "DynamicGenerator" class:
1917 #------------------------------------------------------------------------------
1919 class DynamicGenerator(object):
1920 """ Defines a classical generator model and a fourth order generator model
1921 for dynamic simulation.
1924 def __init__(self
, generator
, exciter
, governor
, model
=CLASSICAL
, h
=1.0,
1925 d
=0.01, x
=1.0, x_tr
=1.0, xd
=1.0, xq
=1.0, xd_tr
=1.0, xq_tr
=1.0,
1927 #: Power flow generator.
1928 self
.generator
= generator
1931 self
.exciter
= exciter
1934 self
.governor
= governor
1936 #: Classical or 4th order model.
1939 #: Inertia constant.
1942 #: Damping constant.
1945 # Classical model -----------------------------------------------------
1947 #: Reactance (p.u.).
1950 #: Transient reactance (p.u.).
1953 # 4th order model -----------------------------------------------------
1955 #: d-axis reactance (p.u.).
1958 #: q-axis reactance (p.u.).
1961 #: d-axis transient reactance (p.u.).
1964 #: q-axis transient reactance (p.u.).
1967 #: d-axis time constant (s).
1970 #: q-axis time constant (s).
1973 #------------------------------------------------------------------------------
1975 #------------------------------------------------------------------------------
1977 class Exciter(object):
1978 """ Defines an IEEE DC1A excitation system.
1981 def __init__(self
, generator
, model
=CONST_EXCITATION
, ka
=0.0, ta
=0.0,
1982 ke
=0.0, te
=0.0, kf
=0.0, tf
=0.0, aex
=0.0, bex
=0.0,
1983 ur_min
=-1.5, ur_max
=1.5):
1985 #: Power flow generator.
1986 self
.generator
= generator
1994 #: Amplifier time constant.
2000 #: Exciter time constant.
2006 #: Stabiliser time constant.
2009 #: Parameter saturation function.
2012 #: Parameter saturation function.
2015 #: Lower voltage limit.
2016 self
.ur_min
= ur_min
2018 #: Upper voltage limit.
2019 self
.ur_max
= ur_max
2021 #------------------------------------------------------------------------------
2023 #------------------------------------------------------------------------------
2025 class Governor(object):
2026 """ Defines an IEEE model of a general speed-governing system for steam
2027 turbines. It can represent a mechanical-hydraulic or electro-hydraulic
2028 system by the appropriate selection of parameters.
2031 def __init__(self
, generator
, model
=CONST_POWER
, k
=0.0, t1
=0.0, t2
=0.0,
2032 t3
=0.0, p_up
=0.0, p_down
=0.0, p_max
=0.0, p_min
=0.0):
2034 #: Power flow generator.
2035 self
.generator
= generator
2049 #: Servo motor time constant.
2052 #: Upper ramp limit.
2055 #: Lower ramp limit.
2056 self
.p_down
= p_down
2058 #: Maxmimum turbine output.
2061 #: Minimum turbine output.
2064 #------------------------------------------------------------------------------
2066 #------------------------------------------------------------------------------
2068 class Event(object):
2069 """ Defines an event.
2072 def __init__(self
, time
, type):
2073 """ Constructs a new Event instance.
2075 #: Instant of change (s).
2078 #: Bus or branch event.
2081 #------------------------------------------------------------------------------
2082 # "BusChange" class:
2083 #------------------------------------------------------------------------------
2085 class BusChange(object):
2086 """ Defines a bus parameter change event.
2088 Three-phase bus faults can be simulated by changing the shunt
2089 susceptance of the bus in a bus change event.
2092 def __init__(self
, bus
, time
, param
, newval
):
2094 #: Bus with new parameter value.
2097 #: Instant of change (s).
2100 #: Bus parameter to change.
2103 #: New parameter value.
2104 self
.newval
= newval
2106 #------------------------------------------------------------------------------
2107 # "BranchChange" class:
2108 #------------------------------------------------------------------------------
2110 class BranchChange(object):
2111 """ Defines a branch parameter change event.
2114 def __init__(self
, branch
, time
, param
, newval
):
2116 #: Branch with new parameter value.
2117 self
.branch
= branch
2119 #: Instant of change (s).
2122 #: Bus parameter to change.
2125 #: New parameter value.
2126 self
.newval
= newval
2128 # EOF -------------------------------------------------------------------------