Script Directives

 

Script directives are used to facilitate the creation of source code, which will ultimately be used in the building of an executable program representing the project.  Each directive type has a specific purpose, as explained in the following sections.  Script directives always begin with a pound # symbol prefix, where spaces preceding and following the # symbol are allowed.

 

Directives are an important tool when scripting definitions, as they instruct the internal compiler what source code to build and how to build it.  It is highly recommended that directives be used wherever and whenever possible, as they ensure that user defined script will remain compatible with coding standards as they change and evolve over time.

#IF, #ELSEIF, #ELSE, #ENDIF

#IF, #ELSEIF, #ELSE, #ENDIF directives are used specifically as logical structures, in the same manner as their equivalent IF, ELSE, etc. source language intrinsic functions.  Within script however, they are used to include or exclude blocks of script.

 

#IF, #ELSEIF, #ELSE, #ENDIF directives can be used in all segments and should appear as follows:

 

#IF <Logic>

      ...Application_Code...

#ELSEIF <Logic>

      #IF <Logic>

            ...Application_Code...

      #ELSE

            ...Application_Code...

      #ENDIF

#ELSE <Logic>

      ...Application_Code...

#ENDIF

 

If just a simple IF-THEN condition is required, the following shortcut using expression braces may also be used:

 

#IF <Logic> {<Expression>}

 

<Logic> is a logical expression using logical operators.  <Expression> can be a variable definition.

 

 

 

EXAMPLE 10-12:

 

A user needs to change the output of a signal generator model, according to whether a sine or cosine output is required.  The component definition provides an input parameter choice list in the Parameters section, called Type.  If this parameter is 1, then the output is sinusoidal (if 0 then co-sinusoidal).

 

The following code should appear in the Fortran, DSDYN or DSOUT segments of the component:

 

!

! Signal Generator

!

#IF Type == 1

      $OUT = SIN(TWO_PI*$F)

#ELSE

      $OUT = COS(TWO_PI*$F)

#ENDIF

!

 

Where F is a pre-defined variable (perhaps an input parameter or Computations segment variable) and OUT is an output port connection in the Graphic section.

NOTE:  Type == 1 in the above code is a Logical Expression.  See Expression Evaluation for more details.

Using Enfolding Operators, the above example could also be written as:

 

!

! Signal Generator

!

#IF Type == 1 {      $OUT = SIN(TWO_PI*$F)}

#IF Type != 1 {      $OUT = COS(TWO_PI*$F)}

!

 

 

 

 

 

EXAMPLE 10-13:

 

A user has created an electrical component that can either be a simple resistor, inductor or capacitor.  The component definition provides an input parameter choice list in the Parameters section, called Type.  This parameter can either be 1, 2 or 3 to represent a resistor, inductor or capacitor respectively.  Three other input text boxes also exist (called R, L and C) in order to specify the respective values of these elements.

 

The following code should appear in the Branch segment:

 

#IF Type == 1

      $N1  $N2  $R  0.0  0.0

#ELSEIF Type==2

      $N1  $N2  0.0  $L  0.0

#ELSE

      $N1  $N2  0.0  0.0  $C

#ENDIF

 

N1 and N2 are electrical port connections defined in the Graphic section.  Using Enfolding Operators, the above example could also be written as:

 

!

#IF Type == 1 {$N1  $N2  $R   0.0  0.0}

#IF Type == 2 {$N1  $N2  0.0  $L   0.0}

#IF Type == 3 {$N1  $N2  0.0  0.0  $C}

!

 

 

#CASE

The #CASE directive is a short-form notation method, which can be used in place of #IF, #ELSEIF, #ELSE, #ENDIF directives.  It is normally used in conjunction with the ~ Line Continuation Operator to provide a space saving alternative, especially when a great number of #IF, #ELSEIF, #ELSE, #ENDIF directives are required.

 

The #CASE directive can be used in all segments, and should appear as follows:

 

#CASE <Expression> {<Clause_0>} {<Clause_1>} ...

 

<Expression> must return an integer from 0 to n, and can either be a mathematical expression or simply a defined variable.  <Clause_n> represents what is to occur given the value of the expression, and where in the sequence of clauses it resides.  For example, the results of <Clause_0> will occur if <Expression> is equal to 0.  If <Expression> is equal to 1, <Clause_1> will occur, etc.

 

 

 

EXAMPLE 10-14:

 

Consider the signal generator output discussed in Example 10-12:

 

!

! Signal Generator

!

#IF Type == 1

      $OUT = SIN(TWO_PI*$F)

#ELSE

      $OUT = COS(TWO_PI*$F)

#ENDIF

!

 

The following is a simple illustration of an equivalent script using the #CASE directive:

 

!

! Signal Generator

!

       $OUT = ~

#CASE Type {~COS~} {~SIN~}

~(TWO_PI*$F)

!

 

Note that Type should be either 0 or 1.

 

 

 

 

 

EXAMPLE 10-15:

 

The following illustrates how the #CASE directive is used in the Transformers segment of the 3-Phase, 2-Winding classical transformer component, located in the master library.

 

Here, YD1 and Lead are both component input parameters. YD1 can equal either 0 or 1 and Lead either 1 or 2.  Multiplying these two integers can result in 0, 1 or 2, and this information is used to determine which clause to select:

 

!

#CASE YD1*Lead {$A1 $G1~} {$A1 $B1~} {$A1 $C1~}

!

 

 

#STORAGE

This directive is required only if a definition is utilizing EMTDC Storage Arrays.  Its purpose is to define the manner in which memory is used – specifically the type and amount of EMTDC storage to be allocated.  The #STORAGE directive acts as a direct interface to the storage arrays; which provide time step to time step data transfer capability, as well as data transfer between BEGIN and corresponding DSDYN or DSOUT sections.  EMTDC array usage must be specified with a #STORAGE directive to ensure that memory is properly dimensioned by PSCAD at compile time.

 

The #STORAGE directive is used only in the Fortran, DSDYN or DSOUT segments, and should appear as follows:

 

#STORAGE <TYPE>:<Number>

 

The actual #STORAGE statement can appear anywhere in the segment, but should be placed near the top.  <TYPE> refers to the storage array type (see table below).  <Number> is the amount of elements to be stored, and may be a substituted constant.

 

The following storage examples are all valid when using the $ Substitution Prefix Operator with this directive:

 

 

#STORAGE REAL:$(X) INTEGER:$(Y)            // X and Y are literal parameters

#STORAGE REAL:$#DIM(N1) INTEGER:$#DIM(N2)  // N1 & N2 are electrical ports

#STORAGE INTEGER:$#DIM(N1) LOGICAL:7

#STORAGE REAL:${X+Y}

 

 

 

 

Type

EMTDC Storage Array

Description

Inter Time Step Data Transfer

STOR

STOR(NEXC)

This array is left over from PSCAD V2 and is deprecated.  Do not use this array if possible.

REAL

STORF(NSTORF)

For floating point (REAL) storage only.

INTEGER

STORI(NSTORI)

For INTEGER storage only.

LOGICAL

STORL(NSTORL)

For LOGICAL storage only.

COMPLEX

STORC(NSTORC)

For COMPLEX storage only.

 

 

 

 

BEGIN to DSDYN/DSOUT Data Transfer

RTCF

RTCF(NRTCF)

For floating point (REAL) storage only.

RTCI

RTCI(NRTCI)

For INTEGER storage only.

RTCL

RTCL(NRTCL)

For LOGICAL storage only.

RTCC

RTCC(NRTCC)

For COMPLEX storage only.

 

 

 

EXAMPLE 10-16:

 

Consider the following definition script, taken from the Fortran segment of the XYZ Transfer Function component in the master library:

 

#IF NL==0

#STORAGE INTEGER:2 REAL:110

#ELSEIF NL==1

#STORAGE INTEGER:2 REAL:1100

#ELSEIF NL==2

#STORAGE INTEGER:2 REAL:11000

#ENDIF

#FUNCTION REAL XYZFUNC XYZ-Transfer function

! File name: $FILE

      $Z = XYZFUNC($X,$Y,$MODE,$Xoff,$Yoff,$Zoff,$Kx,$Ky,$Kz)

 

 

This script uses the #IF, #ELSEIF, #ELSE, #ENDIF directives, along with #STORAGE to determine the EMTDC storage array requirements for this component.  Depending on the variable NL, which in this component represents the Number of Lines in the File input parameter, the number of REAL elements to be allocated ranges from 110 to 11000.  The number of INTEGER elements is always 2.  

 

Note that the above is how variable storage was dealt with prior to the introduction of substitution in storage directives. A much simpler and more efficient equivalent to the above code is as follows, where the variable NREAL is derived from NL, the Number of Lines in the File input parameter, and substituted as the REAL storage dimension:

 

#STORAGE INTEGER:2 REAL:$(NREAL)

#FUNCTION REAL XYZFUNC XYZ-Transfer function

! File name: $FILE

      $Z = XYZFUNC($X,$Y,$MODE,$Xoff,$Yoff,$Zoff,$Kx,$Ky,$Kz)

 

 

This allocation represents the type and number of EMTDC storage array elements that are used internally by the function XYZFUNC.  That is to say that the XYZFUNC function code utilizes both the STORI and the STORF arrays to store and transfer data from time step to time step during a simulation.

 

 

#LOCAL

This directive is used to declare local variables directly within definition script.  Quite often, it is necessary to declare local variables to say, define a dummy variable for an unused subroutine argument, or perhaps as an intermediary value in a set of equations.

 

The #LOCAL directive instructs the PSCAD compiler to place a local variable declaration directly in the parent module Fortran file when the project is built.  #LOCAL variables do not require the $ Value Substitution Prefix Operator.

 

The #LOCAL directive is used only in the Fortran, DSDYN or DSOUT segments, and should appear as follows:

 

#LOCAL <TYPE> <Name> <Array_Size_1> <Array_Size_2>

 

<TYPE> can be either REAL, INTEGER, LOGICAL, or COMPLEX.  <Name> is the given name for the local variable.  <Array_Size_1> is an optional integer or substituted constant that defines the size of the array. If declaring a matrix, then  <Array_Size_1>  defines the number of rows and  <Array_Size_2> defines the columns.  If the variable is a scalar, then leave <Array_Size_1> and <Array_Size_2> blank.

 

The following storage examples are all valid when using the $ Substitution Prefix Operator with this directive:

 

 

#LOCAL REAL X $#DIM(N1) 2          // N1 is an electrical port

#LOCAL REAL Y $#DIM(N1) $#DIM(N2)  // N1 & N2 are electrical ports

#LOCAL INTEGER Z $(XXX)            // XXX is a literal parameter

#LOCAL INTEGER ZZZ ${X-Y}          // X and Y are literal parameters

#LOCAL COMPLEX D 2                 // D is a locally declared, complex array

 

 

 

 

EXAMPLE 10-17:

 

A user's custom component requires two local variables for use as subroutine arguments.  According to a conditional statement based on a pre-defined variable A, a local INTEGER variable MY_X and a local REAL array Error are defined before the subroutine is called.

 

The #LOCAL declarations should appear as follows:

 

#LOCAL INTEGER MY_X

#LOCAL REAL Error 2

!

#IF A > 1

      MY_X = 1

      Error(1) = 0.2

#ELSE

      MY_X = 0

      Error(2) = 0.8

#ENDIF

!

      CALL SUB1(MY_X, Error)

!

NOTE:  A > 1 in the above code is a Logical Expression.  See Expression Evaluation for more details.

 

 

#BEGIN/#ENDBEGIN

This directive block provides a portal to the BEGIN outer process layer in EMTDC, which is essentially a module based, subroutine that is called prior to both DSDYN and DSOUT at time zero.

 

 

This process layer provides Runtime Configuration, for the support of components existing in modules with multiple instances.  For more information on these topics, see Multiple Instance Modules in Chapter 5 of this manual and/or Runtime Configuration in Chapter 2 of the EMTDC manual.

 

The #BEGIN/#ENDBEGIN directive block is used only in the Fortran, DSDYN or DSOUT segments, and should appear as follows:

 

#BEGIN

   ...

#ENDBEGIN

 

The contents of the directive block is arbitrary and dependent on the component being designed – but will of course require a minimum of standard script in order to be of any use.  Using the #BEGIN block will ensure that time zero code is considered only once at compile time, avoiding a TIMEZERO logic check every time step; thereby ensuring efficient simulation speed. The #BEGIN block will require the following general parts:

 

 

EXAMPLE 10-18:

 

One of the more simple components in the master library to include BEGIN code is the Exponential Functions component.  This component models an exponent function, which can possess both a base and an exponential coefficient:

 

 

The coefficients A and B are declared Constant, meaning that these parameter values could be defined by variables that may possess different numeric values, depending on the instance of its parent module (i.e. the canvas on which this component resides).  Therefore, these input quantities must be stored in sequence within the BEGIN storage arrays, in order to be extracted in the proper sequence according to module instance.

 

The following script is a simplified version of what appears in the Fortran segment of the Exponential Functions component definition (it is assumed base e and a scalar input signal):

 

#BEGIN

      RTCF(NRTCF)   = $A

      RTCF(NRTCF+1) = $B

      NRTCF = NRTCF + 2

#ENDBEGIN

#STORAGE RTCF:2

!  

      $OUT = RTCF(NRTCF) * EXP(RTCF(NRTCF+1) * $IN)

      NRTCF = NRTCF + 2

!

 

The #BEGIN/#ENDBEGIN directive block at the top of this script, ensures that code is placed by the compiler in the BEGIN section of the system dynamics: The coefficients A and B are stored in the proper sequence, in accordance with the present storage pointer value NRTCF.  

 

The rest of the script outside of the #BEGIN/#ENDBEGIN directive block is inserted in either DSDYN or DSOUT sections of the system dynamics.  Notice that the quantities required to represent the coefficients A and B (i.e. RTCF(NRTCF) and RTCF(NRTCF+1) respectively) are extracted from the same storage locations as they were stored at time zero.  The NRTCF pointer is incremented accordingly.

 

See EMTDC Storage Arrays in Chapter 5 of the EMTDC Manual for more details on this functionality.

 

 

#FUNCTION

This directive is used to declare the existence of a function, and the argument type it returns.  #FUNCTION is mandatory if a function is used within a component definition:  It will ensure that a function declaration statement is placed within any source subroutine where the component code is placed.

 

The #FUNCTION directive is used only in the Fortran, DSDYN or DSOUT segments, and should appear as follows:

 

#FUNCTION <TYPE> <Name> <Description>

 

<TYPE> can be either REAL, INTEGER, or LOGICAL.  <Name> is the given name of the function.  <Description> will be included as a comment line near the beginning of the corresponding module source subroutine.

 

 

 

EXAMPLE 10-19:

 

The Hard Limiter component in the master library utilizes a REAL function called LIMIT to determine the value linked to an external port connection O, according to input arguments LL, UL, and I.  LL and UL represent the component input parameters Lower Limit and Upper Limit respectively, whereas the I variable is pre-defined by an input port connection in the Graphic section.

 

The following code appears in the Fortran segment of the Hard Limiter component definition:

 

#FUNCTION REAL LIMIT Hard Limiter

!

      $O = LIMIT($LL, $UL, $I)

!

 

 

#SUBROUTINE

This directive is used to provide a description for a subroutine that is being called from the component.  #SUBROUTINE is used simply for cosmetic purposes, and is not mandatory (although its use is recommended anyway).

 

The #SUBROUTINE directive is used only in the Fortran, DSDYN or DSOUT segments, and should appear as follows:

 

#SUBROUTINE <Name> <Description>

 

<Name> is the given name for the subroutine.  <Description> will be included as a comment line near the beginning of the corresponding module source subroutine.

 

 

 

EXAMPLE 10-20:

 

A user includes a call to a subroutine, named SUB1, within the component definition script.  It is desired that a description be added for clarity.

 

The following code should appear in the Fortran, DSDYN or DSOUT segment of the component:

 

#SUBROUTINE SUB1 User Subroutine

!

      CALL SUB1($X, $Y, $Z)

!

 

X, Y and Z are pre-defined variables that could be port connections, Computations variables or input parameters.

 

 

#OUTPUT

This directive extracts specified data values so that they may be monitored, plotted, or used externally by other components.  #OUTPUT performs two tasks:  It defines a new variable according to a specified name, and then assigns it a value according to an expression.

 

The #OUTPUT directive is used only in the Fortran, DSDYN or DSOUT segments, and should appear as follows:

 

#OUTPUT <TYPE> <Name> <Array_Size> {<Expression>}

 

<TYPE> can be either REAL, INTEGER, or LOGICAL.  <Name> is the given name for the variable.  <Array_Size> is an optional integer, which defines the size of the array.  If the variable has only a single dimension, then leave <Array_Size> blank.  <Expression> can be a mathematical expression, a storage location, or simply a defined variable.

 

 

 

EXAMPLE 10-21:

 

A new variable declared by the #OUTPUT directive may have its value defined in many different ways.  The following list gives examples of some of the possible methods.  Note that the master library also contains a multitude of examples on the use of #OUTPUT within its component definitions.

 

! Defines a REAL variable 'freq' and substitutes

! the value of a pre-defined variable 'Fout'.

!

#OUTPUT REAL freq {$Fout}

!

! Defines an INTEGER variable 'Xon' and assigns

! it the value of a storage location.

!

#OUTPUT INTEGER Xon {STORI(NSTORI+1)}

!

! Defines an REAL variable 'POut' and assigns

! it the value of a given mathematical

! expression.

!

#OUTPUT REAL POut {$V*$I}

!

 

 

#TRANSFORMERS

This directive should be included whenever mutually coupled windings are to be represented in a component definition.  It is normally used in conjunction with the #WINDINGS directive (described below). Examples of master library components that utilize this directive are (of course) the transformers and the p-section components.

 

#TRANSFORMERS has two primary purposes:

  1. Provides a method to sequentially number all components in a project that contain mutually coupled windings, for the purpose of dimensioning EMTDC matrices and arrays.

  2. Provides addressing information for the monitoring of mutually coupled winding currents.  Transformer winding currents are measured using the CDCTR(M,N) internal EMTDC matrix.  CDCTR(M,N) is the electrical current through the Mth winding of the Nth transformer.

The #TRANSFORMERS directive is used only in the Transformers segment, and should appear as follows:

 

#TRANSFORMERS <Number>

 

<Number> indicates the total number of transformers within the component.

 

 

 

EXAMPLE 10-22:

 

The 3-Phase, 2-Winding classical transformer component, located in the Transformers section of the master library, consists of three, single-phase transformers.

 

The directive would appear as follows in the Transformers segment:

 

#TRANSFORMERS 3

#WINDINGS 2

 

 

 

#WINDINGS

This directive is used to assign the number of coupled windings in a transformer, and is normally used in conjunction with the #TRANSFORMERS directive.  PSCAD will look for the highest number associated with all existing #WINDINGS directives and then assigns that number (as the maximum number of windings) to the Map file.

 

The #WINDINGS directive is used only in the Transformers segment, and should appear as follows:

 

#WINDINGS <Number>

 

<Number> indicates the maximum number of coupled windings within that specific component.

 

See Example 10-22 above.

#VERBATIM

This directive is used to pass a line of script directly into the Fortran file from your component, unmodified and unprocessed.

 

The #VERBATIM directive should appear as follows:

 

#VERBATIM {<Text>}

 

<Text> can be any line of text, such as a comment, compiler directive or source code. <Text> will appear in the Fortran file generated by PSCAD exactly as is (ie. verbatim).

 

! Caution should be exercised as ANY line of code will be written to

! the Fortran file, be it Fortran compatible or not!

!

! PSCAD Script:

!

#VERBATIM {! This is a comment line.}

#VERBATIM {  X = 1.0 ! This is a line of Fortran code.}

#VERBATIM {@#$%^&*!& This is a line of rubbish.}

!

! Fortran File:

!

! This is a comment line.

  X = 1.0 ! This is a line of Fortran code.

@#$%^&*!& This is a line of rubbish.

#TOP/#BOTTOM

These directives were added specifically as a solution to initializing the Feedback Loop Selector (zminusone) master library component.

 

Individual lines of script, preceded by one of these directives, is forced to either the top or the bottom of the system dynamics code. In the case of the Feedback Loop Selector, the set initial value is forced to the top of code to ensure it is properly initialized at TIMEZERO.

 

The #TOP/BOTTOM directives should appear as follows, preceding any line of script:

 

#TOP ...

 

or

 

#BOTTOM ...

 

Example:

 

! $Name

#TOP      IF (TIMEZERO) $INR = $InitR

      $INR = $OUTR

 

Will appear in the corresponding source file as:

 

! 140:[zminusone] Feedback Loop Selector 'FBLS'

      IF (TIMEZERO) RT_4 = 1.123

 

! ... 

!

! Other component code from the schematic.

!

! ...

 

! 140:[zminusone] Feedback Loop Selector 'FBLS'

! FBLS

      RT_4 = RT_1