控制器模型建模
- InterPSS 控制器建模语言CML介绍
InterPSS用户自定义控制器建模语言CML是一种以Java Annotation为依托的建模语言,其CML脚本其实就是Java代码。因此,对CML语言的执行和编译需要在Java虚拟机的环境之下进行。
先通过一个简单的例子来引入介绍:
一个简单的励磁系统传递框图
Exciter CML code
@AnController(
input="this.refPoint + pss.vs - mach.vt",
output="this.delayBlock.y",
refPoint="this.delayBlock.u-pss.vs + mach.vt",
display= {"str.Efd, this.output", "str.ExciterState, this.delayBlock.state",
"str.MachVt, mach.vt", "str.MachPe, mach.pe"}
)
public double k = 50.0, t = 0.05, vmax = 10.0, vmin = 0.0;
@AnControllerField(
type= CMLFieldEnum.ControlBlock,
input="this.refPoint + pss.vs - mach.vt",
parameter={"type.Limit", "this.k", "this.t", "this.vmax", "this.vmin"},
y0="mach.efd" )
DelayControlBlock delayBlock;
其中this指该励磁器,pss为励磁器所属发电机的电力系统稳定器,mach为发电机,其他参数意义与上图对应。
由上可见,CML的语法简明、用法简单,输入(intput)、输出(output)、参考值(refPoint)都是采用参数直接运算,而仿真时在内部转化为相应Java代码进行处理,显示(display)为用户自定义输出变量组合。同时,CML支持自定义函数和子模块功能以实现复杂的控制器模型。
CML运用Java Annotation来对控制器进行注释,以此告诉InterPSS在执行期间应该如何去建立一个用于进行电力系统暂态稳定仿真计算的控制器模型。该注释在此分为控制器和控制器模块两种级别:
控制器级别的注释
控制器级别的注释通过如下的Java Annotation结构来进行描述:
public @interface AnController {
String input() default ""; // 定义控制器的输入信号
String output() default ""; // 定义控制器的输出信号
String refPoint() default ""; // 定义控制器的参考点
String[] display() default {}; // 定义需要在InterPSS中体现的输出量
}
其中input,output和refPoint这3项必须用注释变量的形式来表示,例如:input="pss.vs-mach.vt"。
而display这一项可以按照用户自身需要填写内容,如用户需要查看传递函数中某个环节的输入或输出信号,便可在此指定一个或多个相关项用以查看信号详情(可以文本输出的形式,也可直观地通过绘制曲线图的方式)。只要编写过程符合Java Annotation语法,该栏不填写并不会令InterPSS在暂停稳定仿真计算运行过程中出现异常。
控制器模块级别的注释
控制器模块级别的注释通过如下的Java Annotation结构来进行描述:
1). 函数模块
public @interface AnFunctionField {
CMLFieldEnum type() default CMLFieldEnum.Function; // 注①
String[] parameter() default {}; // 注②
String[] input() default {}; // 注③
ILookupTable.Type lookupTableType() default ILookupTable.Type.LinearLine; //注④
String[] dataPoints() default {}; // 注⑤
String y0() default "0.0"; // 注⑥
}
注①:定义控制器模块的类型,语法必须为CMLFieldEnum.Function,CMLFieldEnum.FunctionExpression或CMLFieldEnum.LookupTable这3种的其中之一。
注②:控制器模块相关参数,例如:{"this.a", "this.b", "this.c"}。
注③:控制器模块输入信号,例如:{"this.refPoint", "pss.vs", "mach.vt"};若模块类型为FunctionExpression,则为:{"this.refPoint-mach.vt", "this.voltLimit.y"}。
注④:查询表类型,格式必须为LoopupTable.Type.LinearLine。
注⑤:查找数据点,例如:{"0.0, 5.0", "1.0, 6.0", "2.0, 5.5"}。
注⑥:定义模块输出需要计算的初始化数值,例如:"mach.efd",若该模块在参考点之后,且不是反馈模块时才需要定义该值。
其中模块的parameter和input这2项需要以字符串数组的类型来定义,例如:input = {"mach.efd", "mach.ifd"}。若用户想定义励磁机饱和特性函数Se(Efd) function的话,可以依照如下格式编写脚本:
public double e1 = 3.1, se_e1 = 0.33, e2 = 2.3, se_e2 = 0.1;
@AnFunctionField(
input= { "mach.efd" },
parameter={"this.e1", "this.se_e1", "this.e2", "this.se_e2"} )
SeFunction seFunc;
2) 静态模块或控制模块
public @interface AnControllerField {
CMLFieldEnum type( ); // 注①
String input( ) default ""; // 注②
String[ ] parameter() default {}; // 注③
String y0( ) default "0.0"; // 注④
boolean feedback( ) default false; // 注⑤
int initOrderNumber( ) default 0; // 注⑥
String[] initGroup( ) default {}; // 注⑦
boolean debug( ) default false; // 注⑧
}
注①:定义控制器模块的类型,语法必须为CMLFieldEnum.StaticBlock,CMLFieldEnum.ControlBlock或CMLFieldEnum.Controller。
注②:控制器模块输入信号,例如:"this.refPoint + pss.vs - mach.vt"。
注③:控制器模块相关参数,例如:{"type.Limit", "this.k", "this.t", "this.vmax", "this.vmin"}。
注④:定义模块输出需要计算的初始化数值,例如:"mach.efd",若该模块在参考点之后,且不是反馈模块时才需要定义该值。
注⑤:定义该控制器模块是否是反馈模块。
注⑥:自定义模块的初始化序号,在两种情况下需要作此定义:1)连接到参考点的路径多余1条;2)存在系统无法自行计算出初始化编号顺序的模块。
注⑦:将反馈模块与前向通路模块打包用于进行初始化。
注⑧:选择是否进行模块调试。
其中input,output和refPoint这3项必须用注释变量的形式来表示,例如:input="this.refPoint + pss.vs - mach.vt"。
关于其他相关的静态环节或控制环节模块的信息可查阅CML Introduction。
CML建模语言中模块的初始化问题
如何处理模块初始化的问题是CML建模中的一大难点。因为在控制模块进行电力系统暂态仿真的过程中,需要求解每个传递函数中控制环节的状态空间表达式方程组。这样一来,就需要在求解方程组之前进行环节的初始化,通俗一点说就是通过指定每个环节输出的初值来使环节开始求解状态空间表达式方程组的计算。
CML将会自动初始化所有的控制器模块。在进行初始化之前,将从系统的初始状态获取控制器的输入信号和输出信号。以下是主要的初始化步骤:
1. 为所有在“参考点之前”路径和“参考点之后”路径的控制器模块环节分配初始化次序编号;
2. 用输出信号来初始化所有“参考点之后”路径和所有输入为“参考点之后”环节的反馈环节;
3. 用输入信号来初始化所有“参考点之前”路径和所有输入为“参考点之前”环节的反馈环节;
计算参考点。
如上图所示,初始化顺序应为:1,2,3,4,5,RefPoint。
而在复杂的控制器结构当中,需要添加注释来自行分配初始化顺序用于重载系统原先的初始化序列。例如:
public double k = 50.0, t = 0.05, vmax = 10.0, vmin = 0.0;
@AnControllerField(
...
initOrderNumber=1 ) // 这里就是重载的自行分配初始化顺序内容;
DelayControlBlock delayBlock;
RefPoint的初始化序号永远为0。参考点之后的环节初始化序号为正值,参考点之前的环节初始化序号为负值。而反馈回路的环节没有初始化序号。因此上图的初始化标号如下表:
2种情况下用户需要自行定义初始化序号来重载系统默认序号,分别为:
a). 路径数多于一条
上图中,有两条连接到参考点的路径。系统将会分配环节1和环节2的初始化序号,而用户需要自行定义环节3的初始化序号。
如HighValueFunction之类的函数模块,不会参与到初始化的过程中。因此上图中的环节1和环节2的初始化序号需要用户自行定义。
CML建模环境与调试
CML建模语言脚本代码输入环境
InterPSS桌面版提供了一个图形用户界面,在控制器模型的Type项选择为CML Scripting XXX, 用户可以运用CML建模语言来描述自己需要的自定义控制器。用户自行编写的CML脚本将在运行时自动编译并加载到InterPSS当中用于电力系统暂态稳定的仿真计算。
InterPSS中用于编写CML脚本的图形用户界面
在运行期间,例如<ControllerDescriptionBegin>之类的标签将被一些用于创建Java源文件的代码所替换。该CML脚本将被编译并动态地加载到InterPSS当中用于运行暂态稳定的仿真计算。对比PSASP软件通过DLL进行连接,InterPSS的内部动态插入处理更灵活、方便。
CML控制器模型的调试
当用户在开发控制器模型时,有可能需要一些额外的输出数据来用于调试模型,而InterPSS中也非常人性化地提供了这项功能。
(1) 图形调试信息:
若用户想要获得发电机,线路的输出变量或控制器状态量的信息用于调试时,可以在控制器注释的display项中添加如下信息:
@AnController(
input="pss.vs - mach.vt",
output="this.delayBlock.y",
refPoint="this.delayBlock.u-pss.vs + mach.vt",
display= {"str.Efd, this.output", "str.ExciterState, this.delayBlock.state",
"str.MachVt, mach.vt", "str.MachPe, mach.pe"})
这样在仿真运行结束之后,就可以查看之前在display项中填写的变量和状态量的信息了。
(2) 模块级别调试信息
在定义一个控制环节时,用户可以设置调试注释项的值为true:
@AnControllerField(
...
debug=true )
DelayControlBlock delayBlock;
这样控制器环节在每一步仿真时的详细信息就将被输出到InterPSS下log文件夹的log文件中。
May 14, 2011 4:08:34 PM
com.interpss.dstab.controller.annotate.AbstractAnnotateController eulerStep
INFO: CML field debug delayBlock eulerStep-1
dx_dt = -0.00007031, x = 1.00986496, u = 0.02019722
May 14, 2011 4:08:34 PM
com.interpss.dstab.controller.annotate.AbstractAnnotateController eulerStep
INFO: CML field debug delayBlock eulerStep-2
dx_dt = -0.00007031, x = 1.00986496, u = 0.02019721
用户可以通过这些信息来帮助解决开发过程中遇到的问题。
- 建模实例
连续、旋转直流励磁系统——BPA EA(IEEE 1968 Type1)
EA的传递函数框图
输入数据:
1) 指定:TR, KA, TA, TA1, VRMIN MULT = -1.0, SE.75MAX, SEMAX, EFDMAX, KF, TF, KE, TE
2) VRMAX =(SEMAX+ KE)EFDMAX
3) VRMIN = VRMAX VRMIN MULT
4) 对它励系统KE=1,自励系统KE=0(程序指定为KE=0)
该励磁系统是在IEEE-1968 Exciter Models-Type1的基础上经过进一步改进演变获得的,根据CML的语法,建模的脚本代码如下:
注: 以下代码适应于在InterPSS桌面版模型自定义环境中使用。相应地,控制器以新插件(plug-in)形式开发还涉及数据接口等代码(具体见EA type Exciter Implementation code)
EA Exciter modeling code
<ControllerDescriptionBegin> // do not modify this tag line
@AnController(
input="mach.vt",
output="this.delayBlock.y",
refPoint="this.kaDelayBlock.u - pss.vs + this.krDelayBlock.y + this.washoutBlock.y",
display= { "str.kaDelayBlock, this.kaDelayBlock.y",
"str.seFunc, this.seFunc.y",
"str.delayBlock, this.delayBlock.y",
"str.washoutBlock, this.washoutBlock.y" })
<ControllerDescriptionEnd> // do not modify this tag line
<ControllerFieldDescriptionBegin> // do not modify this tag line
//krDelayBlock----1/(1+sTr)
public double kr = 1.0/*constant*/,tr = 0.04;
@AnControllerField(
type= CMLFieldEnum.ControlBlock,
input="mach.vt",
parameter={"type.NoLimit", "this.kr", "this.tr"},
y0="this.refPoint + pss.vs - this.kaDelayBlock.u0 - this.washoutBlock.y",
initOrderNumber=-1 )
DelayControlBlock krDelayBlock;
//kaDelayBlock----Ka/(1+sTa)
public double ka = 40.0, ta = 0.05;
@AnControllerField(
type= CMLFieldEnum.ControlBlock,
input="this.refPoint + pss.vs - this.krDelayBlock.y - this.washoutBlock.y",
parameter={"type.NoLimit", "this.ka", "this.ta"},
y0="this.ka1DelayBlock.u0" )
DelayControlBlock kaDelayBlock;
//ka1DelayBlock----1/(1+sTa1) limited
public double ka1 = 1.0/*constant*/, ta1 = 0.02, semax = 0.860, efdmax = 5.5, ke2 = 1.0/*ke2 = ke*/, vrmax = (semax+ke2)*efdmax, vrmin = -vrmax;
@AnControllerField(
type= CMLFieldEnum.ControlBlock,
input="this.kaDelayBlock.y",
parameter={"type.NonWindup", "this.ka1", "this.ta1", "this.vrmax", "this.vrmin"},
y0="this.delayBlock.u0 + this.seFunc.y" )
DelayControlBlock ka1DelayBlock;
//delayBlock----1/(Ke+sTe)
public double ke = 1.0, ke1 = 1/ke, te = 2.0, te_ke = te/ke ;
@AnControllerField(
type= CMLFieldEnum.ControlBlock,
input="this.ka1DelayBlock.y - this.seFunc.y",
parameter={"type.NoLimit", "this.ke1", "this.te_ke"},
y0="mach.efd" )
DelayControlBlock delayBlock;
//seFunc----Se
public double e1 = efdmax, se_e1 = semax, e2 = 0.75*efdmax, se_e2 = 0.50;
@AnFunctionField(
input= {"this.delayBlock.y"},
parameter={"this.e1", "this.se_e1", "this.e2", "this.se_e2"} )
SeFunction seFunc;
//washoutBlock----sKf/(1+sTf)
public double kf = 0.03, tf = 0.350, k = kf/tf;
@AnControllerField(
type= CMLFieldEnum.ControlBlock,
input="this.delayBlock.y",
parameter={"type.NoLimit", "this.k", "this.tf"},
feedback = true )
WashoutControlBlock washoutBlock;
<ControllerFieldDescriptionEnd> // do not modify this tag line