EbsScript is an embedded scripting language within EBSILON professional that gives you access to EBSILON’s input, output and calculation capabilities and to combine these with your own code to:
There are 6 situations where EbsScript can be used
This getting started guide is intended to provide a quick reference to EbsScript for those who are familiar with other programming languages and programming in general. It provides an overview of EbsScript syntax, code snippet examples for common tasks and debugging tips. More details on using EbsScript are available in the EbsScript chapter of the EBSILON help system. The three key parts of the EbsScript Tool, (the Editor, Explorer and Executor) are described in that chapter as well.
EbsScript Syntax EbsScript Operators EbsScript Decision Making Statements EbsScript Looping Statements EbsScript Functions
EbsScript Variable Scope EbsScript Arrays EbsScript Debugging EbsScript Interface Units EbsScript SamplesEbsScript syntax is based on the syntax of the PASCAL programming language plus some extensions. At a high level this means:
const PI = 3.14159;
{ This is a single line comment in EbsScript}
// This is also a single line comment in EbsScript
Comment text is displayed in gray colour in the EbsScript editor
var
val1, val2 :real;
offset : integer;
name : string;
flag : boolean;
pipe : ebsPipe ;
cmp : ebsComp ;
Beyond the above high level rules the following specifics are also useful.
The Statements in EbsScript use specific PASCAL words (or properties of "Delphi"), which are called reserved or key words. For example, the words program for, while, do, var, real, begin and end are all reserved words. You cannot use reserved words as names for your variables and functions / procedures. The following table shows the reserved / key words in EbsScript:
A |
||||
abstract |
add |
addr |
and |
append |
array |
autodispatch |
autosafearray |
autounknown |
|
B |
||||
begin |
bitand |
bitnot |
bitor |
bitwiseand |
bitwiseor |
bitwisexor |
bitxor |
boolean |
break |
C |
||||
callback |
capacity |
case |
char |
class |
comnullptr |
const |
constructor |
continue |
copy |
copymem |
||||
D |
||||
dec |
default |
destructor |
dispatchcall |
dispose |
div |
divide |
do |
downto |
|
E |
||||
else |
end |
enumstrings |
enumtostring |
equal |
except |
explicit |
|||
F |
||||
false |
finalization |
finally |
for |
forward |
freemem |
function |
|||
G |
||||
getmem |
greaterthan |
greaterthan |
||
H |
||||
high |
||||
I |
||||
if |
implementation |
implicit |
in |
inc |
index |
inherited |
initialization |
insert |
internal |
intdivide |
integer |
interface |
||
L |
||||
leftshift |
length |
lessthan |
lessthanorequal |
logicaland |
logicalnot |
logicalor |
logicalxor |
low |
|
M |
||||
mod |
modulus |
multiply |
||
N |
||||
negative |
new |
nil |
not |
notequal |
O |
||||
of |
on |
operator |
or |
otherwise |
overload |
override |
|||
P |
||||
pointer |
positive |
|
println |
private |
procedure |
program |
property |
protected |
public |
R |
||||
raise |
read |
real |
record |
reintroduce |
remove |
repeat |
reserve |
return |
rightshift |
round |
||||
S |
||||
self |
setlength |
setlow |
shl |
shr |
sizeof |
sliceleft |
slicemid |
sliceright |
smartptr |
static |
step |
string |
subtract |
|
T |
||||
then |
to |
true |
trunc |
try |
type |
typeof |
|||
U |
||||
unit |
unittest |
until |
uses |
|
V |
||||
var |
virtual |
|||
W |
||||
while |
write |
|||
EbsScript reserved words will be shown in blue text in the EbsScript editor.
An "identifier" in a program refers to an object (such as a data type, variable, or function) with which it is uniquely named. Therefore, the identifier must be unique within a namespace.
Typically, identifiers use the combination of a string of letters and numbers.
Operator |
Description |
+ |
Adds two operands |
- |
Subtracts second operand from the first |
* |
Multiplies both operands |
div |
Integer Division |
mod |
Modulus Operator AND remainder of after an integer division |
/ |
Floating Point (Real) Division |
Exponentiation |
Use the pow function pow(base, exponent) |
Operator |
Description |
= |
Checks if the values of two operands are equal or not, if yes, then condition becomes true. |
<> |
Checks if the values of two operands are equal or not, if values are not equal, then condition becomes true. |
> |
Checks if the value of left operand is greater than the value of right operand, if yes then condition becomes true. |
< |
Checks if the value of left operand is less than the value of right operand, if yes then condition becomes true. |
>= |
Checks if the value of left operand is greater than or equal to the value of right operand, if yes then condition becomes true. |
<= |
Checks if the value of left operand is less than or equal to the value of right operand, if yes then condition becomes true. |
Operator |
Description |
and |
Boolean AND operator. If both the operands are true, then condition becomes true. |
and then |
Similar to the AND operator, however, it guarantees the order in which the compiler evaluates the logical expression. Left to right and the right operands are evaluated only when necessary. |
or |
Boolean OR Operator. If any of the two operands is true, then condition becomes true. |
or else |
Similar to Boolean OR, however, it guarantees the order in which the compiler evaluates the logical expression. Left to right and the right operands are evaluated only when necessary. |
not |
Boolean NOT Operator. Used to reverse the logical state of its operand. If a condition is true, then the NOT operator will make it false. |
If the boolean expression condition evaluates to true, then the block of code inside the if statement will be executed. If the boolean expression evaluates to false, then the first set of code after the end of the if statement (after the closing end;) will be executed.
if str <> "" then begin
str := StringTrimLeft(str);
str := stringLower(str) ;
end;
EbsScript considers any non-zero or non-nil values to be true. Values of zero or nil are treated as false.
If the boolean expression condition evaluates to true then the if-then block of code will be executed otherwise the else block of code will be executed. For example:
if str <> "" then begin
str := StringTrimLeft(str);
str := stringLower(str) ;
end
else begin
UserInput := "" ;
end;
The syntax of the case statement is:
case (expression) of
L1 : S1;
L2: S2;
...
Ln: Sn;
otherwise: Sotherwise; or else: Selse
end;
Where, L1, L2... Ln are case labels or input values, which can be integers, characters, boolean or enumerated data items. S1, S2, ... Sn are EbsScript statements and each of these statements may have one or more than one case label associated with it. The expression is called the case selector or the case index. The case index may assume values that correspond to the case labels. The case statement must always have an end statement associated with it. The following rules apply to a case statement:
For example:
case MacroInterface.LoadMethod of
0 : Programmable.SPEC3 := 0 ;
1 : Programmable.SPEC3 := 2 ;
2 : Programmable.SPEC3 := 1 ;
otherwise Programmable.SPEC3 := MacroInterface.LoadMethod ;
end;
The type for case of can be string too. Here the individual anchor points must be string constants:
procedure foo(s:string);
begin
case s of "Hallo" : begin
// ...
end;
"Welt" : begin
// ...
end
otherwise begin
// ...
end
end;
end;
The comparison is ALWAYS executed in a case-sensitive way.
Furthermore, entire ranges can now be specified as anchor points:
5 .. 10 : // all values greater or equal 5 and less or equal 10
.. 2 : // all values less or equal 2
15 .. : // all values greater or equal 15
procedure bar(value:integer); begin
case value of ..-10 : begin
println("<= -10");
end;
20.. : begin
println("=> 20");
end;
5..8 : begin
println("5 <= .. <= 8");
end;
4 : begin
println("== 4");
end;
0 : begin
println("== 0");
end
otherwise begin
println("something else");
end
end;
end;
For strings, the lexicographic order is used here.
A while-do loop statement in EbsScript allows repetitive computations until some test condition is satisfied. In other words, it repeatedly executes a target statement or group of statements as long as a given condition is true. The syntax of a while-do loop is:
while (condition) do S;
Where condition is a Boolean or relational expression, whose value would be true or false and S is a statement or group of statements within a "begin ... end;" block. For example:
while number > 0 do
begin
sum := sum + number;
number := number - 1;
end;
When the condition becomes false, program control passes to the line immediately following the loop’s end; statement.
A "for ... do" loop is a repetition control structure that allows you to efficiently write a loop that needs to execute a specific number of times. The syntax for the for-do loop in EbsScript is as follows:
for < loop-variable-name > := < initial_value > to [downto] < final_value > do S;
Where loop-variable-name specifies a variable of ordinal type, called the control variable or index variable; initial_value and final_value values are values that the control variable can take; and S is the body of the for-do loop that can be a statement or a group of statements. For example:
for i:=1 to n do
begin
MacroInterface.ERRARRAY.x[i] := Programmable.RA_2.x[i];
MacroInterface.ERRARRAY.y[i] := Programmable.RA_2.y[i];
end;
This is how control flows in a for-do loop in EbsScript:
A "repeat ... until" loop is similar to a while loop, except that a "repeat ... until" loop is guaranteed to execute at least one time. The syntax for the "repeat ... until" loop in EbsScript is as follows:
Repeat
S1;
S2;
...
Sn;
until condition;
For example:
repeat
sum := sum + number;
number := number - 1;
until number = 0;
Notice that the conditional expression appears at the end of the loop, so the statement(s) in the loop will execute once before the loop condition is tested. If the condition is true, the flow of control jumps back up to repeat and the statement(s) in the loop execute again. This process repeats until the given condition becomes false.
The break statement in EbsScript has the following uses:
The continue statement in EbsScript works like the break statement. Instead of forcing termination, however, continue forces the next iteration of the loop to take place, skipping any code in between. When used in a "for ... do" loop, the continue statement causes the conditional test and increment portions of the loop to execute. When used in "while ... do" and "repeat...until" loops, the continue statement causes the program control pass to the loop’s conditional tests.
A function definition in EbsScript consists of a function header, local declarations and a function body. The function header consists of the keyword function and a name given to the function. These are the parts of a function declaration:
Arguments: The argument(s) establish the linkage between the calling program and the function identifiers and are also called the formal parameters.
Return Type: All functions must return a value, so all functions must be assigned a type. The function-type is the data type of the value the function returns. It may be standard, user-defined scalar or sub range type but it cannot be structured type.
Local declarations: Local declarations refer to the declarations for labels, constants, variables, functions and procedures, which are applicable to the body of function only.
Function Body: The function body contains a collection of statements that define what the function does. It should always be enclosed between the reserved words begin and end. The function body is the part of a function where all computations are done. There must be an assignment statement of the type function_name := expression; in the function body that assigns a value to the function name. This value is returned when the function is executed. The last statement in the body must be an end statement. To call a function, you simply need to pass the required parameters along with function name, and if the function returns a value, then you can store the returned value by assigning it to a variable. For example:
function test (a, b: Integer ; c: real) : real;
begin
test:= (a+b)*c;
end;
begin
println ( test(2, 5, 7.1));
end.
Note the semicolon after the final function end statement as well.
Scope in any programming is that region or portion of the program where a defined variable exists. Beyond that or outside of its scope, the variable cannot be accessed. There are three places, where variables can be declared in EbsScript:
Local Variables
Variables that are declared inside a function / procedure or code block are called local variables. They can be used only by statements that are inside that function / procedure or block of code. Local variables are not known to functions / procedures or code blocks outside their own.
Global Variables
Global variables are defined outside of a function, usually at the top of the script. The global variables will hold their value throughout the lifetime of your script and they can be accessed inside any of the functions defined in the script. A global variable can be accessed by any function. That is, a global variable is available for use throughout your entire script after its declaration.
See the Free Pascal language reference more general information on scope of variables.
EbsScript provides a data structure called the array, which can store a fixed-size sequential collection of elements of the same type. An array is used to store a collection of variables of the same type. Instead of declaring individual variables, such as number1, number2, ... and number100, you declare one array variable called numbers and use numbers[1], numbers[2], and ..., numbers[100] to represent individual variables. A specific element in an array is accessed by an index. All arrays consist of contiguous memory locations. The lowest address corresponds to the first element and the highest address to the last element. Note that if you want a C style array starting from index 0, you just need to start the index from 0, instead of 1.
The general form of type declaration of one-dimensional array is:
type array-identifier = array[index-type] of element-type;
Where:
array-identifier indicates the name of the array type.
index-type specifies the subscript of the array; it can be any scalar data type except real
element-type specifies the types of values that are going to be stored
For example:
type
vector = array [ 1..25] of real;
var
velocity: vector;
Velocity is a variable array of vector type, which is sufficient to hold up to 25 real numbers. To start the array from 0 index, the declaration would be:
type
vector = array [ 0..24] of real;
var
velocity: vector;
Types of Array Subscript
In EbsScript, an array subscript can be of any scalar type like, integer, boolean, enumerated or sub range, except real. Array subscripts can have negative values too. For example:
type
temperature = array [-10 .. 50] of real;
var
day_temp, night_temp: temperature;
Finally, an example where the subscript is of character type:
type
ch_array = array[char] of 1..26;
var
alphabet: ch_array;
c: char;
begin
...
for c:= 'A' to 'Z' do
alphabet[c] :=
An element is accessed by indexing the array name. This is done by placing the index of the element within square brackets after the name of the array. For example:
a: integer;
a: = alphabet['A'];
a:= numbers[10];
uses @KernelScripting
Since the Kernel Scripting is embedded in the solver for the overall equation system, you cannot debug it in the editor. All debugging functions in the EbsScript editor will be disabled, if you edit a kernel script. To work around this limitation one can print intermediate results with println() commands like:
println("FCTRLMODE =", ::Control.FCTRLMODE);
and review them AFTER calculation in the EbsKern-Output tool bar. To display the EbsKern-Output too bar, follow these steps:
1. Choose Toolbars > from the View pull down menu.
2. Choose EbsKern-Output from the sub menu to display the EbsKern-Output window shown below:
In the EbsKernel output bar, a sheet (tab) is created for each component 93 contained in the model being run along with a complete sheet (tab) for all outputs.
If your script code does not use Kernel Scripting, then you can debug your code while an EbsScript is running. Debugging makes it possible to interrupt the execution of the script and to view and also change the values of variables. The EbsScript debugger can be found under the Debug menu item.
In order to debug, breakpoints must be set in the script by moving the cursor to the desired line in the EbsScript editor window and clicking F9 (“Switch breakpoint”). Clicking on F9 one more time will remove the breakpoint.
The script can then be started with F5 (Start/continue) and will run up to the first breakpoint you set. It is possible to see the values of variables displayed in the monitoring bar (in the EbsScript editor under View > Toolbars > Console) and to change them.
You can then continue the execution of the script step-by-step with F10 (across procedure activations) and F11 (into procedures), respectively, or run up to the next breakpoint with F5. The Debugger window is shown below:
Note, you may also run the debugger on some kernel scripts by copying the content of your kernel script to an external script provided it does not access stream or component data by using functions such as "ksGetPipeValue". In order to correctly interpret the variable names of the model, the external script has to run in the same context (i.e. on the model level which by default is called (Global), or inside the respective macro object which contains the kernel script, in the example above called ‘Control’). You can select the appropriate context from a drop-down in the EbsScript menu that is shown circled in red in the screen shot above.
If your kernel script does access stream or component data then using "println" statements and the EbsKern-Output window is the only means of debugging currently available.
uses @KernelScripting;
The "@" before the unit name acts as an identification for an Interface-Unit. EBSILON Professional provides the following interface units:
KernalScripting
Fluid
Variant
Units
Covariance
System
Epos
KernelExpression
MacroHelpers
TimeSeries
NNTool
Standard
Debug
You can see what functions these interface units provide by opening the EbsScript Editor (select the EbsScript Editor… item on the Calculation pull down menu or press F8) and then selecting the Open Interface Unit > item on the EbsScript Editor’s EbsScript pull down menu. Note some of the interface units like NNTool may require additional licensing.
It is also possible to define custom units in Pascal, which can be used by any script.
Below is an example of such a custom unit:
unit EngineCalcs;
interface
procedure EnginePreProcessing;
procedure EnginePostProcessing;
procedure DoMyCalculations( par1:real; var retval1:real, var retval2:real);
implementation
uses @kernelscripting,@fluid;
procedure AddMessage( MessageNumber:integer; MessageMessage: string);
var len:integer;
begin
//::StatusText.text:=printToString( ::StatusText.text, "\n",MessageMessage );
len:=MacroInterface.Messages.getSize+1;
MacroInterface.Messages.size:=len;
MacroInterface.Messages.x[ len ]:= MessageNumber;
end;
procedure EnginePreProcessing;
var i,j:integer;
x,y:real;
begin
…
…
…
end;
procedure EnginePostProcessing;
var i,j:integer;
x,y:real;
begin
…
…
…
end;
procedure DoMyCalculations( par1:real; var retval1:real, var retval2:real);
var i,j:integer;
x,y:real;
begin
…
…
…
retval1 := …;
retval2 := …;
end;
end.
Example usages:
from a macro in the “script before calculation”:
uses EngineCalcs;
begin
EnginePreProcessing;
end;
from any Component 93 or “Kernel Expression” (=special input fields in components):
uses EngineCalcs;
var p1, r1,r2;
begin
p1:= sqrt(23);
DoMyCalculations(p1,t1,r2);
…
…
end;
Running simulations on different profiles in a model:
var
i: integer;
begin
for i:=1 to 10 do
begin
setCalcProfileByName(“first_profile_name”);
simulate ;
setCalcProfileByName(“second_profile_name”);
simulate ;
end;
end;
Unit of Measure Conversion – pressure to psia
You may directly access the quantities in the model from within the EbsScript. Note that model quantities are internally stored using the SI unit system. This means the Units of Measure (UOM) for model quantities that have units are as follows:
Therefore, a direct assignment like
Turbine.M1N := 100;
always means 100 kg/s. EbsScript provides the Units interface unit to allow you to perform UOM conversions in your script code. The following example converts a pressure from bar to psia:
uses @Units
unitsConvert(comp122.P1, “BAR”, myPinPsia, “PSIA”) ;
Below is a link to the list of all available units of measure in Ebsilon:
Ebsilon Units Of Measure
Setting Specification Values with EbsScript
To set a specification value with EbsScript paste EbsScript code for a function that calculates the desired specification value into the specification input field. For example:
function evalexpr:REAL;
var
begin
// TODO: calculate specification value
evalexpr:= calc_result
end;
Perform a property calculation
You can call any EBSILON property calculations for within EbsScript. For Example calculate Enthalpy from P & T:
uses @fluid;
var
Pres,Temp,rResult:real;
iphase:PhaseEnum;
begin
Pres:=1.013;
Temp:=20.0;
fluidTableWST( rResult, iPhase, Pres, Temp, FuncH_OF_PT);
println (rResult);
end;
The following section of the code can be more convenient in case the user has a stream type to relate to:
begin
fluidTableObject( {var rResult:Real}, {var phase:PhaseEnum}, {arg1:Real}, {arg2:Real}, {func:FuncEnum}, {obj:ebsData} );
end;
The last parameter (obj:ebsData) represents a stream, either by name from the flow sheet, or by reference to a port (For example: ‘componentname._2’ for the stream connected to port 2).
Below is a screen shot of the available functions of the Interface Unit – Fluids from the EbsScript key words window:
Under the following link is the list of available property function calls which can be accessed from the EbsScript: Calculation Functions
Get P, H and M from a port on a component
uses @kernelscripting;
var
m14,h14,p14:real;
begin
m14:=ksGetPipeValue(14,PhysValueM);
h14:=ksGetPipeValue(14,PhysValueH);
p14:=ksGetPipeValue(14,PhysValueP);
end;
Check if a particular port (Port 7) on a macro object is connected and get the upstream component:
uses @MacroHelpers
var
txt : string;
ThisMacro : ebsMacro ;
pipe : ebsPipe ;
cmp : ebsComp ;
ThisMacro := getContainerMacro(MacroInterface); // Check if logic line is connected
if isNull(getPipeAtLink(ThisMacro,7)) then
begin
txt := ThisMacro.name + " fuel logic line at Pin 7 not connected." ;
MessageBox(txt)
exit(-1, txt); // terminate the run and set error message to txt
end;
// There's a logic line at Pin 7. Get it.
pipe := getPipeAtLink(ThisMacro,7) ; // Check if it's connected to a pipe
if isNull(GetPipeAtLogpipe(pipe, 1)) then
begin
txt := ThisMacro.name + " logic line at Pin 7 not connected to a pipe." ;
MessageBox(txt);
exit(-1, txt); // terminate the run
end;
// It's connected to a pipe. Get it.
pipe := GetPipeAtLogpipe(pipe, 1) ;
// Get component at upstream end of that pipe
cmp := GetCompAtPipe(pipe, 1) ;
Call code in a COM Object
callEbsScriptServer( {class:string}, {var var1:(array of) primitive type}, {var var2:(array of) primitive type}{, callBackProc:callback procedure = nil} );
See Block 750.ebs example