Some Tips Concerning the Script Language Of RP Software
Today I want to give the users of our software some potentially useful hints concerning important details of the script language. Partly, this has been motivated by difficulties of users about which I learned in the context of the technical support. I am discussing only features which are relevant for all our most important software products: RP Fiber Power, RP ProPulse, RP Resonator and RP Coating. For constructing sophisticated simulation models, for example, it is highly useful to be familiar with these things. Don't worry, however, if you still get stuck somewhere; as you know, I'm doing my best to provide helpful technical support.
Commands and Expressions
An important distinction is between commands and expressions.
The name of a command is always found at the beginning of a line in the script. After that, there may be additional inputs for that command. For example, a “show” command could read:
show "P_out: ", P_out:d3:"W"
This will display two things in the “Output area”: a string and a variable value, properly formatted with three digits and the unit “W” (watts). Generally, the “show” command can handle any list of expressions which are separated with commas and can be followed by formatting instructions. There can be only one command in one line of the script, but one command can also span several lines, if all of these except for the first one are beginning with a blank (space) character. (This is shown in an example below.)
An expression is something which can be evaluated to a numerical (real or complex) value or string value. It can contain constant values, variables and arrays, function calls and operators, and even control structures (see below). Expressions can also contain so-called blocks between “begin” and “end” or simply between parenthesis; these are essentially lists of smaller expressions which are evaluated one by one, and the result of the last one is returned as the result of the block.
As an example for such a block, consider the following:
max_value(A) := begin var m; m := A; for j := i1(A) to i2(A) step di(A) do m := maxr(m, A[j]); m; end:d3
Here, the second expression is such a block, the value of which will be displayed with three valid digits. (Such a formatting expression can be appended to any expression, even it is a block.) Using the local variable m, the block evaluates the maximum occurring value in an array. The functions i1(), i2() and di() retrieve the range and step size of array index values. Actually, one could also have used a predefined function to simply write:
max_value(A) := A[findmax(A)]
Not only the huge number of predefined functions, but also the possibility to use blocks of expressions and control structures gives you an enormous amount of flexibility. You can do real programming in the scripts. Therefore, there is usually no need to do any preprocessing or postprocessing of data.
Note that an expression can only call functions but not commands. For example, you could not use a “show” command within an expression. In some cases, you may want to display something in the Output area from within an expression. For that purpose, we have provided the function showoutput(), which can display a string value in the Output area. Here, you can use only a single string, but nothing prevents you from concatenating multiple strings in an expression with the “+” operator. For example, you could get the same result as with the previous command by calling the “calc” command with a modified expression:
calc showoutput("Max. array value: " + begin var m; m := A[i1(A)]; for j := i1(A) to i2(A) step di(A)) do m := maxr(m, A[j]); str(m:d3); end)
A detail: I had to use the function str() for converting the numerical value to a string value, using 3 valid digits.
Expressions in our script language support a wide range of control structures – actually more than is available on the level of commands.
Above, you have already seen an example for a “for” loop, where control variable runs through a given range of values. Note that the control variable does not need to have integer values only; you can also use floating point values with a certain step size.
You can also use the traditional “while” loops. An example:
show (var j; j := N; while abs(j - 4 * round(j / 4)) > 0.01 do inc(j))
This takes the value of N and increases it until a value is reached which is an integer multiple of 4.
Similarly, there are “repeat-until” loops:
show (var j; j := N; repeat inc(j) until abs(j - 4 * round(j / 4)) > 0.01)
In a more readable form:
show begin var j; j := N; repeat inc(j); until abs(j - 4 * round(j / 4)) > 0.01; end
And of course there is the usual “if-then-else” structure:
b := if a > 0 then lg(a) else -999
If the “else” part is omitted, it is taken to be 0.
Finally, all these features can be nested as you like: you can have control structures within other control structures, arbitrarily nested function calls, etc. Essentially, there are no limits to your imagination!
In the context of numerical modeling, one often badly needs such features. As an example, imagine that you want to simulate the evolution of ultrashort pulses in a regenerative amplifier. (There is a regenerative amplifier demonstration for RP Fiber Power.) For one type of test, you may have to inject a pulse into the amplifier, do a certain number of roundtrips, get it injected and then send it through some pulse compressor. In a diagram, you want to show a certain result of such tests as a function of the varied parameter such as the pump power or the number of roundtrips per amplification cycle.
Imagine a software which would only allow you to enter such parameters into some form. If you are lucky, this will be sufficient for getting one data point calculated. It would be unlikely, though, that you would be enabled to make the mentioned kind of diagram. With our software products (for example, RP Fiber Power or RP ProPulse), you are absolutely free to produce any kind of such diagrams. Similarly, you could write the data into files of any text format or binary format, using specialized predefined functions for file output.
The Order of Execution
A script is always straight line by line, i.e., from top to bottom. However, not every read command is executed immediately. Those commands which define a diagram or some elements of it (e.g., a graph or curve) only store what has to be done later. Only once the full script has been read successfully to the end, the diagrams are actually made.
You may find that rule somewhat confusing, but it is actually quite useful. Making a diagram can sometimes take significant time. Imagine that the last line of some code for a diagram contains some syntax error which is discovered only after making most of the diagram – that would have wasted significant time.
In some cases, you need to take care that this modified order of execution does not lead to bugs in a script. For example, consider the following code:
diagram 1: x: 0, 10 y: -1, +1 a := 0.5 f: sin(a * x) a := 3 f: cos(a * x)
When the diagram is finally made, both graphs will be made with the value 3 of the variable a. After all, this is the finally assigned value.
Such things can be prevented by delaying the execution of the assignment. Simply write a “!” before the expression, and it will be executed only later on when the diagram is made:
! a := 0.5 f: sin(a * x) ! a := 3 f: cos(a * x)
To be sure, it is often a good idea to delay all calculations which are immediately related to the generation of a diagram.
This article is a posting of the RP Photonics Software News, authored by Dr. Rüdiger Paschotta. You may link to this page, because its location is permanent.
Note that you can also receive the articles in the form of a newsletter or with an RSS feed.
Share this with your friends and colleagues, e.g. via social media:
These sharing buttons are implemented in a privacy-friendly way!