Blocks#

Background#

The block library contains commonly used blocks (such as transfer functions and nonlinear functions). Variables and equations are pre-defined for blocks to be used as "lego pieces" for scripting DAE models. The base class for blocks is andes.core.block.Block.

The supported blocks include Lag, LeadLag, Washout, LeadLagLimit, PIController. In addition, the base class for piece-wise nonlinear functions, PieceWise is provided. PieceWise is used for implementing the quadratic saturation function MagneticQuadSat and exponential saturation function MagneticExpSat.

All variables in a block must be defined as attributes in the constructor, just like variable definition in models. The difference is that the variables are "exported" from a block to the capturing model. All exported variables need to placed in a dictionary, self.vars at the end of the block constructor.

Blocks can be nested as advanced usage. See the following API documentation for more details.

class andes.core.block.Block(name: Optional[str] = None, tex_name: Optional[str] = None, info: Optional[str] = None, namespace: str = 'local')[source]

Base class for control blocks.

Blocks are meant to be instantiated as Model attributes to provide pre-defined equation sets. Subclasses must overload the __init__ method to take custom inputs. Subclasses of Block must overload the define method to provide initialization and equation strings. Exported variables, services and blocks must be constructed into a dictionary self.vars at the end of the constructor.

Blocks can be nested. A block can have blocks but itself as attributes and therefore reuse equations. When a block has sub-blocks, the outer block must be constructed with a``name``.

Nested block works in the following way: the parent block modifies the sub-block's name attribute by prepending the parent block's name at the construction phase. The parent block then exports the sub-block as a whole. When the parent Model class picks up the block, it will recursively import the variables in the block and the sub-blocks correctly. See the example section for details.

Parameters
namestr, optional

Block name

tex_namestr, optional

Block LaTeX name

infostr, optional

Block description.

namespacestr, local or parent

Namespace of the exported elements. If 'local', the block name will be prepended by the parent. If 'parent', the original element name will be used when exporting.

Warning

It is a good practice to avoid more than one level of nesting, to avoid multi-underscore variable names.

Examples

Example for two-level nested blocks. Suppose we have the following hierarchy

SomeModel  instance M
   |
LeadLag A  exports (x, y)
   |
Lag B      exports (x, y)

SomeModel instance M contains an instance of LeadLag block named A, which contains an instance of a Lag block named B. Both A and B exports two variables x and y.

In the code of Model, the following code is used to instantiate LeadLag

class SomeModel:
    def __init__(...)
        ...
        self.A = LeadLag(name='A',
                         u=self.foo1,
                         T1=self.foo2,
                         T2=self.foo3)

To use Lag in the LeadLag code, the following lines are found in the constructor of LeadLag

class LeadLag:
    def __init__(name, ...)
        ...
        self.B = Lag(u=self.y, K=self.K, T=self.T)
        self.vars = {..., 'A': self.A}

The __setattr__ magic of LeadLag takes over the construction and assigns A_B to B.name, given A's name provided at run time. self.A is exported with the internal name A at the end.

Again, the LeadLag instance name (A in this example) MUST be provided in SomeModel's constructor for the name prepending to work correctly. If there is more than one level of nesting, other than the leaf-level block, all parent blocks' names must be provided at instantiation.

When A is picked up by SomeModel.__setattr__, B is captured from A's exports. Recursively, B's variables are exported, Recall that B.name is now A_B, following the naming rule (parent block's name + variable name), B's internal variables become A_B_x and A_B_y.

In this way, B's define() needs no modification since the naming rule is the same. For example, B's internal y is always {self.name}_y, although B has gotten a new name A_B.

Transfer Functions#

The following transfer function blocks have been implemented. They can be imported to build new models.

Algebraic#

class andes.core.block.Gain(u, K, name=None, tex_name=None, info=None)[source]

Gain block.

     ┌───┐
u -> │ K │ -> y
     └───┘

Exports an algebraic output y.

define()[source]

Implemented equation and the initial condition are

\[\begin{split}y = K u \\ y^{(0)} = K u^{(0)}\end{split}\]

First Order#

class andes.core.block.Integrator(u, T, K, y0, check_init=True, name=None, tex_name=None, info=None)[source]

Integrator block.

     ┌──────┐
u -> │ K/sT │ -> y
     └──────┘

Exports a differential variable y.

The initial output needs to be specified through y0.

define()[source]

Implemented equation and the initial condition are

\[\begin{split}\dot{y} = K u \\ y^{(0)} = 0\end{split}\]
class andes.core.block.IntegratorAntiWindup(u, T, K, y0, lower, upper, name=None, tex_name=None, info=None, no_warn=False)[source]

Integrator block with anti-windup limiter.

           upper
          /¯¯¯¯¯
     ┌──────┐
u -> │ K/sT │ -> y
     └──────┘
   _____/
   lower

Exports a differential variable y and an AntiWindup lim. The initial output must be specified through y0.

define()[source]

Implemented equation and the initial condition are

\[\begin{split}\dot{y} = K u \\ y^{(0)} = 0\end{split}\]
class andes.core.block.Lag(u, T, K, D=1, name=None, tex_name=None, info=None)[source]

Lag (low pass filter) transfer function.

     ┌────────┐
     │    K   │
u -> │ ────── │ -> y
     │ D + sT │
     └────────┘

Exports one state variable y as the output.

Parameters
K

Gain

T

Time constant

D

Constant

u

Input variable

define()[source]

Notes

Equations and initial values are

\[\begin{split}T \dot{y} &= (Ku - Dy) \\ y^{(0)} &= Ku / D\end{split}\]
class andes.core.block.LagAntiWindup(u, T, K, lower, upper, D=1, name=None, tex_name=None, info=None)[source]

Lag (low pass filter) transfer function block with an anti-windup limiter.

             upper
           /¯¯¯¯¯¯
     ┌────────┐
     │    K   │
u -> │ ────── │ -> y
     │ D + sT │
     └────────┘
   ______/
   lower

Exports one state variable y as the output and one AntiWindup instance lim.

Parameters
K

Gain

T

Time constant

D

Constant

u

Input variable

define()[source]

Notes

Equations and initial values are

\[\begin{split}T \dot{y} &= (Ku - Dy) \\ y^{(0)} &= K u / D\end{split}\]
class andes.core.block.Washout(u, T, K, name=None, tex_name=None, info=None)[source]

Washout filter (high pass) block.

     ┌────────┐
     │   sK   │
u -> │ ────── │ -> y
     │ 1 + sT │
     └────────┘

Exports state x (symbol x') and output algebraic variable y.

define()[source]

Notes

Equations and initial values:

\[\begin{split}T \dot{x'} &= (u - x') \\ T y &= K (u - x') \\ x'^{(0)} &= u \\ y^{(0)} &= 0\end{split}\]
class andes.core.block.WashoutOrLag(u, T, K, name=None, zero_out=True, tex_name=None, info=None)[source]

Washout with the capability to convert to Lag when K = 0.

Can be enabled with zero_out. Need to provide name to construct.

Exports state x (symbol x'), output algebraic variable y, and a LessThan block LT.

Parameters
zero_outbool, optional

If True, sT will become 1, and the washout will become a low-pass filter. If False, functions as a regular Washout.

define()[source]

Notes

Equations and initial values:

\[\begin{split}T \dot{x'} &= (u - x') \\ T y = z_0 K (u - x') + z_1 T x \\ x'^{(0)} &= u \\ y^{(0)} &= 0\end{split}\]

where z_0 is a flag array for the greater-than-zero elements, and z_1 is that for the less-than or equal-to zero elements.

class andes.core.block.LeadLag(u, T1, T2, K=1, zero_out=True, name=None, tex_name=None, info=None)[source]

Lead-Lag transfer function block in series implementation

     ┌───────────┐
     │   1 + sT1 │
u -> │ K ─────── │ -> y
     │   1 + sT2 │
     └───────────┘

Exports two variables: internal state x and output algebraic variable y.

Parameters
T1BaseParam

Time constant 1

T2BaseParam

Time constant 2

zero_outbool

True to allow zeroing out lead-lag as a pass through (when T1=T2=0)

Notes

To allow zeroing out lead-lag as a pure gain, set zero_out to True.

define()[source]

Notes

Implemented equations and initial values

\[\begin{split}T_2 \dot{x'} &= (u - x') \\ T_2 y &= K T_1 (u - x') + K T_2 x' + E_2 \, , \text{where} \\ E_2 = & \left\{\begin{matrix} (y - K x') &\text{ if } T_1 = T_2 = 0 \& zero\_out=True \\ 0& \text{ otherwise } \end{matrix}\right. \\ x'^{(0)} & = u\\ y^{(0)} & = Ku\\\end{split}\]
class andes.core.block.LeadLagLimit(u, T1, T2, lower, upper, name=None, tex_name=None, info=None)[source]

Lead-Lag transfer function block with hard limiter (series implementation)

     ┌─────────┐          upper
     │ 1 + sT1 │         /¯¯¯¯¯
u -> │ ─────── │ -> ynl / -> y
     │ 1 + sT2 │  _____/
     └─────────┘  lower

Exports four variables: state x, output before hard limiter ynl, output y, and AntiWindup lim.

define()[source]

Notes

Implemented control block equations (without limiter) and initial values

\[\begin{split}T_2 \dot{x'} &= (u - x') \\ T_2 y &= T_1 (u - x') + T_2 x' \\ x'^{(0)} &= y^{(0)} = u\end{split}\]

Second Order#

class andes.core.block.Lag2ndOrd(u, K, T1, T2, name=None, tex_name=None, info=None)[source]

Second order lag transfer function (low-pass filter)

     ┌──────────────────┐
     │         K        │
u -> │ ──────────────── │ -> y
     │ 1 + sT1 + s^2 T2 │
     └──────────────────┘

Exports one two state variables (x, y), where y is the output.

Parameters
u

Input

K

Gain

T1

First order time constant

T2

Second order time constant

define()[source]

Notes

Implemented equations and initial values are

\[\begin{split}T_2 \dot{x} &= Ku - y - T_1 x \\ \dot{y} &= x \\ x^{(0)} &= 0 \\ y^{(0)} &= K u\end{split}\]
class andes.core.block.LeadLag2ndOrd(u, T1, T2, T3, T4, zero_out=False, name=None, tex_name=None, info=None)[source]

Second-order lead-lag transfer function block

     ┌──────────────────┐
     │ 1 + sT3 + s^2 T4 │
u -> │ ──────────────── │ -> y
     │ 1 + sT1 + s^2 T2 │
     └──────────────────┘

Exports two internal states (x1 and x2) and output algebraic variable y.

# TODO: instead of implementing zero_out using LessThan and an additional term, consider correcting all parameters to 1 if all are 0.

define()[source]

Notes

Implemented equations and initial values are

\[\begin{split}T_2 \dot{x}_1 &= u - x_2 - T_1 x_1 \\ \dot{x}_2 &= x_1 \\ T_2 y &= T_2 x_2 + T_2 T_3 x_1 + T_4 (u - x_2 - T_1 x_1) + E_2 \, , \text{ where} \\ E_2 = & \left\{\begin{matrix} (y - x_2) &\text{ if } T_1 = T_2 = T_3 = T_4 = 0 \& zero\_out=True \\ 0& \text{ otherwise } \end{matrix}\right. \\ x_1^{(0)} &= 0 \\ x_2^{(0)} &= y^{(0)} = u\end{split}\]

Saturation#

class andes.models.exciter.ExcExpSat(E1, SE1, E2, SE2, name=None, tex_name=None, info=None)[source]

Exponential exciter saturation block to calculate A and B from E1, SE1, E2 and SE2. Input parameters will be corrected and the user will be warned. To disable saturation, set either E1 or E2 to 0.

Parameters
E1BaseParam

First point of excitation field voltage

SE1: BaseParam

Coefficient corresponding to E1

E2BaseParam

Second point of excitation field voltage

SE2: BaseParam

Coefficient corresponding to E2

define()[source]

Notes

The implementation solves for coefficients A and B which satisfy

\[E_1 S_{E1} = A e^{E1\times B} E_2 S_{E2} = A e^{E2\times B}\]

The solutions are given by

\[E_{1} S_{E1} e^{ \frac{E_1 \log{ \left( \frac{E_2 S_{E2}} {E_1 S_{E1}} \right)} } {E_1 - E_2}} - \frac{\log{\left(\frac{E_2 S_{E2}}{E_1 S_{E1}} \right)}}{E_1 - E_2}\]

Others#

Value Selector#

class andes.core.block.HVGate(u1, u2, name=None, tex_name=None, info=None)[source]

High Value Gate. Outputs the maximum of two inputs.

      ┌─────────┐
u1 -> │ HV Gate │
      │         │ ->  y
u2 -> │  (MAX)  │
      └─────────┘
class andes.core.block.LVGate(u1, u2, name=None, tex_name=None, info=None)[source]

Low Value Gate. Outputs the minimum of the two inputs.

      ┌─────────┐
u1 -> │ LV Gate |
      │         | ->  y
u2 -> │  (MIN)  |
      └─────────┘

Naming Convention#

We loosely follow a naming convention when using modeling blocks. An instance of a modeling block is named with a two-letter acronym, followed by a number or a meaningful but short variaiable name. The acronym and the name are spelled in one word without underscore, as the output of the block already contains _y.

For example, two washout filters can be names WO1 and WO2. In another case, a first-order lag function for voltage sensing can be called LGv, or even LG if there is only one Lag instance in the model.

Naming conventions are not strictly enforced. Expressiveness and concision are encouraged.