4 Fish Beginner'S Guide: Section 3 Section 4.2 Section 2 Sections 2.8 2.9 Section 4
4 Fish Beginner'S Guide: Section 3 Section 4.2 Section 2 Sections 2.8 2.9 Section 4
4.1 Introduction
FISH is a programming language embedded within FLAC that enables the user to define new
variables and functions. These functions may be used to extend FLAC ’s usefulness or add user-
defined features. For example, new variables may be plotted or printed, special grid generators
may be implemented, servo-control may be applied to a numerical test, unusual distributions of
properties may be specified, and parameter studies may be automated.
FISH was developed in response to requests from users who wanted to do things with Itasca software
that were either difficult or impossible with existing program structures. Rather than incorporate
many new and specialized features into the standard code, an embedded language was provided so
that users could write their own functions. Some useful FISH functions have already been written:
a library of these is provided with the FLAC program (see Section 3 in the FISH volume). It is
possible for someone without experience in programming to write simple FISH functions, or to
modify some of the simpler existing functions. Section 4.2 contains an introductory tutorial for non-
programmers. However, FISH programs can also become very complicated (which is true of code
in any programming language); for more details, refer to Section 2 in the FISH volume. Beginners
should not attempt to comprehend Sections 2.8 and 2.9 in the FISH volume (and the FISH files
described in Section 4 in the FISH volume) without considerable experience in programming and
the use of FISH.
As with all programming tasks, FISH functions should be constructed in an incremental fashion,
checking operations at each level before moving on to more complicated code. FISH does less error
checking than most compilers, so all functions should be tested on simple data sets before they are
used for real applications.
FISH programs are simply embedded in a normal FLAC data file: lines following the word DEFINE
are processed as a FISH function; the function terminates when the word END is encountered.
Functions may invoke other functions, which may invoke others, and so on. The order in which
functions are defined does not matter as long as they are all defined before they are used (e.g.,
invoked by a FLAC command). Since the compiled form of a FISH function is stored in FLAC ’s
memory space, the SAVE command saves the function and the current values of associated variables.
Complete definitions of FISH language rules and intrinsic functions are provided in Section 2 in
the FISH volume. This includes rules for syntax, data types, arithmetic, variables and functions.
All FISH language names are described in Section 2 in the FISH volume.
FISH can also be used to implement user-written constitutive models; the procedure is given in
Section 2.8 in the FISH volume. Finally, in Section 2.9 in the FISH volume, an execution speed
optimizer, which can be applied to certain FISH functions, is discussed.
4.2 Tutorial
This section is intended for people who have run FLAC (at least for simple problems) but have not
used the FISH language; no programming experience is assumed. To get the maximum benefit
from the examples given here, you should try them out with FLAC running interactively. The short
programs may be typed in directly. After running an example, give the FLAC command NEW to
wipe the slate clean, ready for the next example. Alternatively, the more lengthy programs may be
created on file and CALLed when required.
Type the lines in Example 4.1 after FLAC ’s command prompt, pressing <Enter> at the end of
each line.
Note that the command prompt changes to Def> after the first line has been typed in; then it
changes back to the usual prompt when the command END is entered. This change in prompt lets
you know whether you are sending lines to FLAC or to FISH. Normally, all lines following the
DEFINE statement are taken as part of the definition of a FISH function (until the END statement is
entered). However, if you type in a line that contains an error (e.g., you type the = sign instead of
the + sign), then you will get the FLAC prompt back again. In this case, you should give the NEW
command and try again from the beginning. Since it is very easy to make mistakes, FISH programs
are normally typed into a file using an editor. These are then CALLed into FLAC just like a regular
FLAC data file. We will describe this process later; for now, we’ll continue to work interactively.
Assuming that you typed in the above lines without error and that you now see the FLAC prompt
flac:, you can “execute” the function abc,* defined earlier in Example 4.1, by typing the line
print abc
The message
abc = 71
should appear on the screen. By defining the symbol abc (using the DEFINE ... END construction,
as in Example 4.1), we can now refer to it in many ways using FLAC commands.
For example, the PRINT command causes the value of a FISH symbol to be displayed; the value is
computed by the series of arithmetic operations in the line
abc = 22 * 3 + 5
* We will use courier boldface to identify user-defined FISH functions and declared variables
in the text.
This is an “assignment statement.” If an equal sign is present, the expression on the right-hand side
of the equal sign is evaluated and given to the variable on the left-hand side. Note that arithmetic
operations follow the usual conventions: addition, subtraction, multiplication and division are done
with the signs +, -, * and /, respectively. The sign ˆ denotes “raised to the power of.”
We now type in a slightly different program (using the NEW command to erase the old one):
Here we introduce a “variable,” hh, which is given the value of 22 and then used in the next line.
If we give the command PRINT abc, then exactly the same output as in the previous case appears.
However, we now have two FISH symbols; they both have values, but one (abc) is known as a
“function,” and the other (hh) as a “variable.” The distinction is as follows.
When a FISH symbol name is mentioned (e.g., in a PRINT statement),
the associated function is executed if the symbol corresponds to a
function. However, if the symbol is not a function name, then the
current value of the symbol is used.
The following experiment may help to clarify the distinction between variables and functions.
Before doing the experiment, note that FLAC ’s SET command can be used to set the value of any
user-defined FISH symbol, independent of the FISH program in which the symbol was introduced.
Now type in the following lines without giving the NEW command, since we want to keep our
previously entered program in memory.
The SET command sets the values of both abc and hh to zero. Since hh is a variable, the first PRINT
command simply displays the current value of hh, which is zero. The second PRINT command
causes abc to be executed (since abc is the name of a function); the values of both hh and abc
are thereby recalculated. Accordingly, the third PRINT statement shows that hh has indeed been
reset to its original value. As a test of your understanding, you should type in the slightly modified
sequence shown in Example 4.4, and figure out why the displayed answers are different.
At this stage, it may be useful to list the most important FLAC commands that directly refer to simple
FISH variables or functions. (In Table 4.1, var stands for the name of the variable or function.)
We have already seen examples of the first two (refer to Examples 4.3 and 4.4); the third case is
useful when histories are required of things that are not provided in the standard FLAC list of history
variables. Example 4.5 shows how this can be done. Example 4.5 shows how the total load on the
top platen of a triaxial test sample can be stored as a history.
Note that the FISH variable load is equal to the sum of four other variables, given by yforce.
The variable yforce is an example of a grid quantity that is available within a FISH program:
there is a complete list of these in Sections 2.5.3 and 2.5.4 in the FISH volume. Grid variables
are simply predefined names of quantities that relate to the FLAC grid. In our example, yforce
is the y-component of the unbalanced gridpoint force; each instance of yforce must be followed
by grid indices, or a pair of numbers denoting a particular gridpoint. In the example, the indices
(1,7), (2,7), (3,7) and (4,7) refer to the four gridpoints at the top of the sample. The
derived variable load, which is the sum of the four top forces, is calculated whenever history
points are taken (every ten steps, by default). At the end of the run, we simply plot out the history
of load (history 1) just like any other history. In a similar way, we may use FISH functions to plot
out a history of any quantity we wish, no matter how complicated the formula to describe it might
be.
In addition to the above-mentioned predefined variable names, there are many other predefined
objects available to a FISH program. These fall into several classes; one such class consists of
scalar variables, which are single numbers. For example:
clock clock time in hundredths of a second
igp total number of gridpoints in the i-direction
izones total number of zones in the i-direction
jgp total number of gridpoints in the j -direction
jzones total number of zones in the j -direction
pi π
step current step number
E
G= (4.1)
2(1 + ν)
E
K= (4.2)
3(1 − 2ν)
Coding Eqs. (4.1) and (4.2) into a FISH function (called derive) can then be done as shown in
Example 4.6:
Note that here we execute the function derive by giving its name by itself on a line; we are not
interested in its value, only what it does. If you run this example, you will see that values are
computed for the bulk and shear moduli, b mod and s mod, respectively. These can then be used,
in symbolic form, in FLAC input, as shown in Example 4.7:
The validity of this operation may be checked by printing out bulk and shear in the usual way.
In these examples, our property input is given via the SET command (i.e., to variables y mod and
p ratio, which stand for Young’s modulus and Poisson’s ratio, respectively).
Note that there is great flexibility in choosing names for FISH variables and functions; the underline
character ( ) may be included in a name. Names must begin with a non-number and must not contain
any of the arithmetic operators (+, –, /, * or ˆ). A chosen name should not be the same as one of the
built-in (or reserved) names; Table 2.1 in Section 2.2.2 in the FISH volume contains a complete
list of names to be avoided, as well as some rules that should be followed.
In the preceding examples, we checked the computed values of FISH variables by giving their
names to a PRINT command explicitly as arguments. Alternatively, we can list all current variables
and functions. A printout of all current values is produced by giving the command
print fish
We now examine ways decisions can be made, and repeated operations done, in FISH programs.
The following FISH statements allow specified sections of a program to be repeated many times:
LOOP var (expr1, expr2)
ENDLOOP
The words LOOP and ENDLOOP are FISH statements, the symbol var stands for the loop variable,
and expr1 and expr2 stand for expressions (or single variables). Example 4.8 shows the use of a
loop (or repeated sequence) to produce the sum and product of the first 10 integers:
In this case, the loop variable n is given successive values from 1 to 10, and the statements inside
the loop (between the LOOP and ENDLOOP statements) are executed for each value. As mentioned,
variable names or an arithmetic expression could be substituted for the number 1 or 10.
A practical use of the LOOP construct is to install a nonlinear initial distribution of elastic moduli
in a FLAC grid. Suppose that the Young’s modulus at a site is given by Eq. (4.3),
√
E = E◦ + c z (4.3)
where z is the depth below surface, and c and E◦ are constants. We write a FISH function to install
appropriate values of bulk and shear modulus in the grid, as in Example 4.9:
end_loop
end_loop
end
set p_ratio=0.25 y_zero=1e7 cc=1e8
install
Again, you can verify correct operation of the function by printing or plotting shear and bulk moduli.
In the function install, we have two loops: the outer loop (with index i) scans all columns in the grid,
and the inner loop (index j) scans all rows. Inside the loops, yc is calculated as the approximate
centroid of each zone (i.e., the average of the y-coordinate for the four surrounding gridpoints). We
assume that the datum (or ground surface reference point) is the top, left-hand gridpoint; then, the
depth of any zone centroid below surface is computed as zz. This is then inserted into the formula
for Young’s modulus given previously, using constants E◦ and c, which have the FISH names y zero
and cc, respectively. Grid values for bulk modulus and shear modulus are calculated as in a previous
example. The variables y( ), shear mod( ) and bulk mod( ) are grid variables. (Recall that we talked
about another grid variable, yforce( ), earlier.) Here, we set properties directly from within a FISH
function, rather than with a PROPERTY command as in our earlier example.
Having seen several examples of FISH programs, let’s briefly examine the question of program
syntax and style. A complete FISH statement must occupy one line; there are no continuation lines.
If a formula is too long to fit on one line, then a temporary variable must be used to split the formula.
Example 4.10 shows how this can be done:
In this case, the sum of 15 variables is split into two parts. Note also the use of the semicolon in line
1 of Example 4.10 to indicate a comment. Any characters that follow a semicolon are ignored by
the FISH compiler, but they are echoed to the log file. It is good programming practice to annotate
programs with informative comments. Some of the programs have been shown with indentation
(i.e., space inserted at the beginning of some lines to denote a related group of statements). Any
number of space characters may be inserted (optionally) between variable names and arithmetic
operations to make the program more readable. Again, it is good programming practice to include
indentation to indicate things like loops, conditional clauses, and so on. Spaces in FISH are
significant in the sense that space characters may not be inserted into a variable or function name.
One other topic that should be addressed now is that of variable type. You may have noticed, when
printing out variables from the various program examples, that numbers are either printed without
decimal points or in “E-format”(i.e., as a number with an exponent denoted by “E”). At any instant
in time, a FISH variable or function name is classified as one of three types: integer, floating-point
or string. These types may change dynamically, depending on context, but the casual user should
not normally have to worry about the type of a variable, since it is set automatically. Consider
Example 4.11:
The variables aa, bb and cc are converted to integer, float and string, respectively, corresponding
to the numbers (or strings) that were assigned to them. Integers are exact numbers (without decimal
points) but are of limited range; floating-point numbers have limited precision (about six decimal
places) but are of much greater range; string variables are arbitrary sequences of characters. There
are various rules for conversion between the three types. For example, dd becomes a floating-point
number, because it is set to the product of a floating-point number and an integer; the variable
ee becomes a string because it is the sum (concatenation) of two strings. The topic can get quite
complicated, but it is fully explained in Sections 2.2.4 and 2.2.5 in the FISH volume.
There is another language element in FISH that is commonly used: the IF statement. Three state-
ments allow decisions to be made within a FISH program:
IF expr1 test expr2 THEN
ELSE
ENDIF
These statements allow conditional execution of FISH program segments; ELSE and THEN are
optional. The item test consists of one of several symbols or symbol-pairs:
= # > < >= <=
The meanings are standard except for #, which means “not equal.” The items expr1 and expr2
are any valid expressions or single variables. If the test is true, then the statements immediately
following IF are executed until ELSE or ENDIF is encountered. If the test is false, the statements
between ELSE and ENDIF are executed if the ELSE statement exists; otherwise, the program jumps
to the first line after ENDIF. The action of these statements is illustrated in Example 4.12:
The displayed value of abc in Example 4.12 depends on the set value of xx. You should experiment
with different test symbols (e.g., replace > with <).
Until now, our FISH programs have been invoked from FLAC either by using the PRINT command,
or by giving the name of the function on a separate line of FLAC input. It is also possible to do the
reverse (i.e., to give FLAC commands from within a FISH function). Most valid FLAC commands
can be embedded between two FISH statements:
COMMAND
ENDCOMMAND
There are two main reasons for sending out FLAC commands from a FISH program. First, it is
possible to use a FISH function to perform operations that are not possible using the predefined
variables that we already discussed. Second, we can control a complete FLAC run with FISH. As
an illustration of the first use of the COMMAND statement, we can write a FISH program to connect
a number of beam segments to the surface of an elastic material. When many beam elements are
required, it becomes tedious to type many separate STRUCTURE commands, each with different
grid indices. However, with FISH, we can send out the commands from within a loop, and adjust
the grid indices automatically each time around the loop, as illustrated in Example 4.13:
After entering these statements, you should do a printout of structural data to verify that seven
beam segments have been created, and that they are connected to appropriate gridpoints. In this
example, we use variables i1 and i2 as parameters to the function place beams. These denote
the starting gridpoint i-index and the ending i-index, which are given values by the SET command.
Note that the STRUCTURE commands sent out from the FISH function have parameters that are
symbolic (as we explained previously), with values that are modified for each circuit of the loop.
One further thing to note: we create a FISH variable jtop that is equal to the built-in scalar jgp.
We cannot use the name jgp directly as an argument to the STRUCTURE command because jgp
(and all the other predefined names) is only recognized within a FISH function.
You can now step FLAC to determine the equilibrium state for the preceding problem (which models
a single load acting on the beam), or you can type in the additional set of lines given in Example 4.14;
this illustrates the second use of COMMAND mentioned previously. The idea here is to produce a
movie without having to give many PLOT commands manually.
By executing the function grid movie, we create a movie with 10 frames, showing the progressive
deformation of the grid. When FLAC has stopped stepping, the movie file “beam load.dcx” can be
viewed with the movie viewer, “MOVIE.EXE,” located in the “ITASCA\Shared\Utility” directory.
In general, when making movies, you should give a fixed WINDOW and a fixed scale (in this case,
magnification), so that auto-scaling is inhibited.
We have now covered some of the aspects of the FISH language and how it interacts with FLAC. A
complete guide to the language is contained in Section 2.2 in the FISH volume, and some examples
are provided in Section 3 in the FISH volume. There is also a useful example of FISH programming
in Section 1.4 in Theory and Background.