Fish Reference
Fish Reference
2 FISH REFERENCE
This section contains a detailed reference to the FISH language. Following the introduction, Sec-
tion 2.2 describes the rules of the language, and how variables and functions are used. Section 2.3
explains FISH statements, and Section 2.4 describes how the FISH language links with FLAC.
Predefined FISH variables, functions and arrays are described in Section 2.5.
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 is a “compiler” (rather than an “interpreter”). Programs entered via a FLAC data file are
translated into a list of instructions (in “pseudo-code”) stored in FLAC ’s memory space; the original
source program is not retained by FLAC. Whenever a FISH function is invoked, its compiled pseudo-
code is executed. The use of compiled code (rather than interpreted source code) enables programs
to run much faster. However, unlike a compiler, variable names and values are available for printing
at any time; values may be modified by the user by using FLAC ’s SET command.
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.
Section 2.6 discusses extensions to FISH for file manipulation, and Section 2.7 describes the use
of socket communication to transfer data between FLAC and another Itasca code. The use of these
functions requires a reasonable understanding of programming techniques and constructs; FISH
can be used without reference to these extensions.
FISH can also be used to implement user-written constitutive models; the procedure is described in
Section 2.8. Finally, in Section 2.9, an execution-speed optimizer which can be applied to certain
FISH functions is discussed.
2.2.1 Lines
FISH programs can be embedded in a normal FLAC data file or may be entered directly from the
keyboard. Lines following the word DEFINE are taken to be statements of a FISH function; the
function terminates when the word END is encountered. A valid line of FISH code must take one
of the following forms.
1. The line starts with a statement such as IF, LOOP, etc. (see Section 2.3).
2. The line contains one or more names of user-defined FISH functions, separated
by spaces – e.g.,
fun 1 fun 2 fun 3
where the names correspond to functions written by the user; these functions
are executed in order. The functions need not be defined prior to their reference
on a line of FISH code (i.e., forward references are allowed).
3. The line consists of an assignment statement (i.e., the expression on the right
of the = sign is evaluated, and the value is given to the variable or function
name on the left of the = sign).
4. The line consists of a FLAC command, provided that the line is embedded in a
section of FISH code delimited by the COMMAND – ENDCOMMAND statements
(see Section 2.3.3).
5. The line is blank or starts with a semicolon.
FISH variables, function names and statements must be spelled out in full; they cannot be truncated,
as in FLAC commands. No continuation lines are allowed; intermediate variables may be used to
split complex expressions. FISH is “case-insensitive” by default (i.e., it makes no distinction
between uppercase and lowercase letters); all names are converted to lowercase letters. Spaces
are significant (unlike in FORTRAN) and serve to separate variables, keywords, and so on; no
embedded blanks are allowed in variable or function names. Extra spaces may be used to improve
readability (for example, by indenting loops and conditional clauses). Any characters following a
semicolon ( ; ) are ignored; comments may be embedded in a FISH program by preceding them
with a semicolon. Blank lines may be embedded in a FISH program.
Variable or function names must start with a non-number, and must not contain any of the following
symbols.
. , * / + - ˆ = < > # ( ) [ ] @ ; ’ "
User-defined names can be any length, but they are truncated in printout and in plot captions, due to
line-length limitations. In general, names may be chosen arbitrarily, although they must not be the
same as a FISH statement (see Section 2.3) or a predefined variable or function (see Section 2.5).
There are also many other words used in FLAC input that should be avoided. The list contained
in Table 2.1 shows all words that could give rise to a conflict if used to name a FISH variable or
function. However, the potential conflict depends on the way in which the chosen name is used.
For example, the word gravity could be used as a FISH variable, provided that it is simply referred
to inside a FISH function; a conflict would arise only if it is necessary to use the SET command to
set its value, since gravity is a valid argument to the SET command. Similarly, it may be impossible
to print the value of a FISH variable if its name is the same as a parameter for the PRINT command.
The list of property names for built-in models (see PROPERTY command in Section 1.2 in the
Command Reference) should also be consulted before choosing property names for a user-written
constitutive model (see Section 2.8.4). If in doubt, avoid any of the names listed in Table 2.1 or for
the PROPERTY command, or contractions of the names (since FLAC allows truncation of keywords
and commands).
Table 2.1 List of words in FLAC and FISH that may conflict with chosen names
a3 char dytime free jzones output
a4 clock ep friend large p stress
abs close echo fsi legend pac
acos cm max ega fsr lff pnt palette
and columns elastic fstring limits parse
angle command else ftens list pause
anisotropic config end g2flow lmul pfast
app pnt constitutive model end case gen ln pi
appgw pnt constitutivemodel end command get mem log plot
apply cos end if gflow loop pltangle
appth pnt cparse end loop gmsmul lose mem pltcohesion
area crdt end section gp copy mark pltfriction
array creep endcase gpp mat inverse plttension
asin crtdel endcommand grand mat transpose poro2
aspect crtime endif grid max power
asxx csc endloop gwdt maxdt pp
asxy csxx endsection gwtdel mechanical pre parse
asyy csxy error gwtime mem preparse
aszz csyy ev p hbm memory print
atan cszz ev tot hbs message prop
atan2 cycle ex 1, ex 2, etc. help min pslow
att pnt damp exit his mindt quit
attach damping exp hisfile mode r
back datum f prop ieb model r integrate
baud define f2mod ieb pnt mohr-coulomb range
bicoe degrad fc arg ierr monchrome rayleigh
bsxx density filcolor if movie read
bsxy disp fish iface ncontours reset
bsyy do update fish msg igp ncwrite restore
bszz dsxx fishcall imem nerr return
call dsxy fix implicit nerr fish rez exe
case dsyy flags in new rff pnt
case of dszz float information ngrwater rsat
caseof dt flow inimodel nmechanical s 3dd
cf axi dump flprop initial not s dyn
cf creep dy fmem int nstep s echo
cf dyn dy state fmod int pnt nthermal s flow
cf ext dydt fobl interface null s imp
cf gw dydt gpi fobu itasca nwgpp s log
cf ps dydt gpj force izones open s mech
cf therm dynamic fos jerr or s mess
cga dytdel fos f jgp out s movie
Table 2.1 List of words in FLAC and FISH that may conflict with chosen names (cont.)
s tens tflow wdens zde11
s therm thdt while zde12
sat then while stepping zde22
save thermal whilestepping zde33
sclin theta window zdpp
sclose thtdel wipp zdrot
section thtime wk11 zmsmul
set title wk12 zporos
sgn tolerance wk22 zsub
sig1 trac pnt write zs11
sig2 track x zs12
sin type xacc zs22
sm max ubiquitous xbody zs33
small ucs xcen ztea
solve udcoe xdisp zteb
sopen udm pnt xflow ztec
sqrt umul xforce zted
sread unbal xform ztsa
ss unbflow xgrav ztsb
ssi unmark xnwflow ztsc
ssi3d urand xreaction ztsd
ssr v ngw xtable zvisc
ssr3d v nmech xvel zxbar
state v ntherm xywrite
step vector y
stop vga yacc
str pnt vgp0 ybody
string vgpcnw ycen
structure vgpcw ydisp
swrite viscous yflow
sxx visrat yforce
sxy vol strain ygrav
sys vs ynwflow
syy vsi yreaction
szz vsr ytable
tab pnt vsxx yvel
table vsxz z copy
table size vsyy z group
tan vszz z hyst
temperature water z model
tenflg wbiot z prop
tension wbulk zart
Variable and function names are recognized globally (as in the BASIC language), except for property
variables associated with user-defined constitutive models (see Section 2.8.4). As soon as a name
is mentioned in a valid FISH program line, it is thereafter recognized globally, both in FISH code
and in FLAC commands (for example, in place of a number); it also appears in the list of variables
displayed when the PRINT fish command is given. A variable may be given a value in one FISH
function and used in another function or in a FLAC command. The value is retained until it is
changed. The values of all variables are also saved by the SAVE command and restored by the
RESTORE command.
The only object in the FISH language that can be executed is the “function.” Functions have no
arguments; communication of parameters is through the setting of variables prior to invoking the
function. (Recall that variables have global scope.) The name of a function follows the DEFINE
statement, and its scope terminates with the END statement. The END statement also serves to return
control to the caller when the function is executed. (Note that the EXIT statement also returns control
– see Section 2.3.2.) Consider Example 2.2, which shows function construction and use.
The value of xxx is changed when the function is executed. The variable aa is computed locally,
but the existing value of bb is used in the computation of xxx. If values are not explicitly given to
variables, they default to zero (integer). It is not necessary for a function to assign a value to the
variable corresponding to its name. The function xxx may be invoked in one of several ways:
(1) as a single word xxx on a FISH input line;
(2) as the variable xxx in a FISH formula – e.g.,
new var = (sqrt(xxx)/5.6)ˆ4;
A function may be referred to in another function before it is defined; the FISH compiler simply
creates a symbol at the time of first mention, and then links all references to the function when it is
defined by a DEFINE command. A function can be deleted or redefined.
Function calls may be nested to any level (i.e., functions may refer to other functions, which may
refer to others, ad infinitum). However, recursive function calls are not allowed (i.e., execution of a
function must not invoke that same function). Example 2.3 shows a recursive function call, which
is not allowed, because the name of the defining function is used in such a way that the function
will try to call itself. The example will produce an error on execution.
Example 2.4 Removing recursion from the function shown in Example 2.3
new
def stress_sum
sum = 0.0
loop i (1,izones)
sum = sum + sxx(i,1)
end_loop
stress_sum = sum
end
The difference between variables and functions is that a function is always executed whenever
its name is mentioned; a variable simply conveys its current value. However, the execution of a
function may cause other variables (as opposed to functions) to be evaluated. This effect is useful,
for example, when several histories of FISH variables are required – only one function is necessary
in order to evaluate several quantities, as in Example 2.5.
The function h var 1 would be executed by FLAC ’s history logic every few steps but, as a side
effect, the values of h var 2 and h var 3 would also be computed and used as history variables.
There are three data types used for FISH variables or function values:
1. Integer (exact numbers in the range −2,147,483,648 to +2,147,483,647);
2. Floating-Point (approximate numbers with about six decimal digits of precision, with
a range of approximately ±10−308 to ±10308 ); and
3. String (packed sequence of any printable characters; the sequence may be any length,
but it will be truncated on the printout. Strings are denoted in FISH and FLAC by a
sequence of characters enclosed by single quotes – e.g., ‘Have a nice day’ – note
that the use of strings in FLAC is restricted to titles and file names. See Section 2.4.1.)
A variable in FISH can change its type dynamically, depending on the type of the expression to
which it is set. To make this clear, consider the assignment statement
var1 = var2
If var1 and var2 are of different types, then two things are done: first, var1’s type is converted to
var2’s type; second, var2’s data are transferred to var1. In other languages, such as FORTRAN
or C, the type of var1 is not changed, although data conversion is done. By default, all variables
in FISH start their life as integers; however, a statement such as
var1 = 3.4
causes var1 to become a floating-point variable when it is executed. The current type of all
variables may be determined by giving the FLAC command PRINT fish – the types will be denoted
in the printout.
The dynamic typing mechanism in FISH was devised to make programming easier for non-
programmers. In languages such as BASIC, numbers are stored in floating-point format, which can
cause difficulties when integers are needed for, say, loop counters. In FISH, the type of the variable
adjusts naturally to the context in which it is used. For example, in the code fragment
n = n + 2
xx = xx + 3.5
the variable n will be an integer and will be incremented by exactly 2, and the variable xx will
be a floating-point number, subject to the usual truncation error but capable of handling a much
bigger dynamic range. The rules governing type conversion in arithmetic operations are explained
in Section 2.2.6. The type of a variable is determined by the type of the object on the right-hand
side of an assignment statement; this applies both to FISH statements and to assignments done with
the FLAC SET command. Both types of assignment may be used to change the type of a variable
according to the value specified, as follows.
1. An integer assignment (digits 0-9 only) will cause the variable to become an
integer (e.g., var1 = 334).
2. If the assigned number has a decimal point or an exponent denoted by “e”
or “E,” then the variable will become a floating-point number (e.g., var1 =
3e5; var2 = -1.2).
3. If the assignment is delimited by single quotes, the variable becomes a string,
with the “value” taken to be the list of characters inside the quotes (e.g., var1
= ‘Have a nice day’).
Type conversion is also done in assignments involving predefined variables or functions; these rules
are presented in Section 2.5.
A variable’s type can be preassigned during the compilation phase if required: the FISH statements
INT, FLOAT and STRING cause the associated variable to be initialized to the given type. This is
discussed in Section 2.3.1. Normally, it is unnecessary to do preassignment, except for variables
used in constitutive models that will be optimized (see Sections 2.8 and 2.9).
If there is any doubt about the order in which arithmetic operators are applied, then parentheses
should be used for clarification.
If either of the two arguments in an arithmetic operation is of floating-point type, then the result
will be floating-point. If both of the arguments are integers, then the result will be integer. It is
important to note that the division of one integer by another causes truncation of the result (for
example, 5/2 produces the result 2, and 5/6 produces the result 0).
2.2.7 Strings
There are three main FISH intrinsic functions that are available to manipulate strings:
in(var) prints out variable var if it is a string, or the message “Input?” if
it is not, and then waits for input from the keyboard. (The returned
value depends on the characters that are typed. FISH tries to decode
the input first as an integer and then as a floating-point number; the
returned value will be of type int or float if a single number that can
be decoded as integer or floating-point, respectively, has been typed
in. The number should be the only thing on the line. However, if it is
followed by a space, comma or parenthesis, then any other characters
on the line are ignored. If the characters typed in by the user cannot be
interpreted as a single number, then the returned value will be a string
containing the sequence of characters. The user’s FISH function can
determine what has been returned by using the function type( ).)
out(s) prints out the message contained in s to the screen (and to the log file,
if it is open). The variable s must be of type string. The returned
value of the function is zero if no error is detected, and 1 if there is
an error in the argument (e.g., if s is not a string).
string(var) converts var to type string. If var is already of type string, then the
function simply returns var as its value. If var is int or float, then a
character string that corresponds to the number as it would be printed
out will be returned. However, no blanks are included in the string.
One use of these functions is to control interactive input and output. Example 2.6 demonstrates
this for user-supplied input parameters for Young’s modulus and Poisson’s ratio.
The only arithmetic operation that is valid for string variables is addition; as demonstrated in
Example 2.6, this causes two strings to be concatenated.
It is invalid for only one argument in an arithmetic operation to be a string variable. The intrinsic
function string( ) must be used if a number is to be included as part of a string variable (see variable
xx in Example 2.6). Also, note the use of intrinsic function type( ), which identifies the type of
argument (see Section 2.5.5). Further string manipulation may be performed with the intrinsic
functions parse, pre parse and cparse, as described in Section 2.6.
A FISH function can be deleted or redefined. If the same name as an existing function is given on a
DEFINE line, the code corresponding to the old function is first deleted (and a warning printed), and
the new code substituted. If an END immediately follows the DEFINE line, then no new function
is created (i.e., the old function is just deleted). Such a “null” function is also assumed to be
nonexistent, even if some declarative statements come between the DEFINE and END (e.g., float,
int, etc.). A few notes of caution:
1. A constitutive model function cannot be deleted or redefined (since existing
grid variables would be left as orphans).
2. The variables that are used in a function still exist even if the function is deleted;
only the code is deleted. Since variables are global, it is likely that they are
used elsewhere. The name of a deleted function will still exist as a variable.
3. If a function is replaced by another of the same name, all calls to the old function
will be replaced automatically by calls to the new one, as demonstrated in
Example 2.7:
There are a number of reserved words in the FISH language; they must not be used for user-
defined variable or function names. The reserved words, or statements, fall into three categories,
as explained below.
The following words are normally placed at the beginning of a FISH function. They alter the
characteristics of the function or its variables, but do not affect the flow of control within the
function. They are only interpreted during compilation.
ARRAY var1(n1, n2 . . . ) <var2(m1, m2 . . . )> <var3(p1, p2 . . . )> . . .
This statement permits arrays of any dimension and size to be included in FISH code.
In the above specification, var1 is any valid variable name, and n1, n2 . . . are
either actual integers, or single user-defined variables (not expressions) that have
integer values at the time the ARRAY statement is processed. There may be several
arrays specified on the same line (e.g., var2, above); the number of dimensions may
be different for each array. The ARRAY statement is a specification and is acted
on during compilation, not execution (it is ignored during execution). Note the
following.
1. The given name may be an existing single variable. If so, it is converted to an
array and its value is lost. If the name does not already exist, it is created.
2. The given name may not be that of a function or the name of an existing array
(i.e., arrays cannot be redefined).
3. The given dimensions (n1, n2, . . . ) must be positive integers or evaluate to
positive integers (i.e., indices start at 1, not 0).
4. There is no limit to the number and size of the array dimensions, except memory
capacity and the maximum line length.
5. Arrays may be declared and used in optimized functions, limited as follows.
a) Only the first element of an array is checked for type; the re-
maining elements are assumed to be of the same type. It is the
user’s responsibility to ensure this; otherwise, the results will
be wrong. In particular, mixed-type arrays will fail.
b) An array name may be given the type int or float prior to the
ARRAY statement, in order to initialize all elements to the given
type – e.g.,
float abc
array abc(3,3)
The following statements serve to direct the flow of control during execution of a FISH function.
Their position in the function is of critical importance, unlike the specification statements described
above.
DEFINE function-name
END The FISH program between the DEFINE and END commands is compiled and stored in
FLAC ’s memory space. The compiled version of the function is executed whenever
its name is mentioned, as explained in Section 2.2.4. The function name (which
should be chosen according to the rules in Section 2.2.2) does not need to be assigned
a value in the program section that follows.
CASEOF expr
CASE n
ENDCASE The action of these control statements is similar to the FORTRAN-computed GOTO,
or C’s SWITCH statement. It allows control to be passed rapidly to one of several
code segments, depending on the value of an index. The use of the keywords is
illustrated in Example 2.8.
Synonym: CASE OF END CASE
The object expr following CASEOF can be any valid algebraic expression; when
evaluated, it will be converted to an integer. The items i1, i2, i3, . . . must be integers
(not symbols) in the range 0 to 255. If the value of expr equals i1, then control jumps
to the statements following the CASE i1 statement; execution then continues until
the next CASE statement is encountered. Control then jumps to the code following
the ENDCASE statement; there is no “fall-through” as in the C language. Similar
jumps are executed if the value of expr equals i2, i3, and so on. If the value of
expr does not equal the numbers associated with any of the CASE statements, then
any code immediately following the CASEOF statement is executed, with a jump
to ENDCASE when the first CASE is encountered. If the value of expr is less than
zero or greater than the greatest number associated with any of the CASEs, then an
execution error is indicated, and processing stops. The numbers n (e.g., i1, i2, i3)
need not be sequential or contiguous, but no duplicate numbers may exist.
CASEOF . . . ENDCASE sections may be nested to any degree; there will be no conflict
between CASE numbers in the different levels of nesting (e.g., several instances of
CASE 5 may appear, provided that they are all associated with different nesting
levels). The use of CASE statements allows rapid decisions to be made (much
more quickly than for a series of IF . . . ENDIF statements). However, the penalty is
that some memory is consumed; the amount of memory depends on the maximum
numerical value associated with the CASE statements. The memory consumed is
one plus the maximum CASE number in double-words (four-byte units).
IF expr1 test expr2 THEN
ELSE
ENDIF These statements allow conditional execution of FISH code segments; ELSE is op-
tional and the word THEN may be omitted if desired. The item test consists of one
of the following symbols, or symbol pairs:
= # > < >= <=
The meanings are standard, except for #, which means “not equal.” The items expr1
and expr2 are any valid algebraic expressions (which can involve functions, FLAC
variables, etc.). 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 be-
tween ELSE and ENDIF are executed if the ELSE statement exists; otherwise, control
jumps to the first line after ENDIF. All the given test symbols may be applied when
expressions expr1 and expr2 evaluate to integers or floating-point values (or a mix-
ture). If both expressions evaluate to strings, then only two tests are valid: = and #;
all other operations are invalid for strings. Strings must match exactly for equality.
IF . . . ELSE . . . ENDIF clauses can be nested to any depth.
Synonym: END IF
EXIT This statement causes an unconditional jump to the end of the current function.
EXIT SECTION
This statement causes an unconditional jump to the end of a SECTION; FISH program
sections are explained below.
COMMAND
ENDCOMMAND
FLAC commands may be inserted between this pair of FISH statements; the com-
mands will be interpreted when the FISH function is executed. There are a number of
restrictions concerning the embedding of FLAC commands within a FISH function.
The NEW and RESTORE commands are not permitted to be invoked from within a
FISH function. The lines found between a COMMAND – ENDCOMMAND pair are
simply stored by FISH as a list of symbols; they are not checked at all, and the
function must be executed before any errors can be detected.
A FISH function may appear within a COMMAND – ENDCOMMAND pair, and may
not contain the COMMAND – ENDCOMMAND statements. A function that contains
the WHILESTEPPING statement, or a function that is the subject of a fishcall, may not
contain the COMMAND statement.
Comment lines (starting with ;) are taken as FLAC comments, rather than FISH
comments; it may be useful to embed an explanatory message within a function, to
be printed out when the function is invoked. If the echo mode is off (SET echo = off),
then any FLAC commands coming from the function are not displayed to the screen
or recorded to the log file.
Synonym: END COMMAND
The following list contains all of the FLAC commands that refer directly to FISH variables or entities.
There are many other ways FLAC and FISH may interact; these are described in Section 2.4.2.
CONFIG extra n
The extra keyword, given in the CONFIG command, causes space for extra grid
variables to be allocated; the value n specifies the number of extra variables. Such
variables may be used as “scratch-pad” space by a FISH function, or may be used
to store some grid-related quantities that are later printed or plotted. The extra grid
arrays are referred to in a FISH function as ex 1(i,j) . . . ex n(i,j), where
n is the number given in the CONFIG command. The extra grid variables are always
of the floating-point type.
HISTORY var
causes a history of the FISH variable or function to be taken during stepping. If var
is a function, then it will be evaluated every time histories are stored (controlled by
HISTORY nstep command); it is not necessary to register the function with a fishcall.
If var is a FISH variable, then its current value will be taken. Hence, caution should
be exercised when using variables (rather than functions) for histories. var may also
be a material property name; either of a built-in model or a user-defined model. In
both cases, the property name must be given in full, followed by the zone indices
(i, j). The history may be plotted in the usual way.
HISTORY ex n (i,j)
causes a history of the extra grid variable ex n (i, j) to be taken during stepping. n is
the numerical identifier for the extra grid variable, and i, j correspond to grid indices.
INITIAL ex n value
initializes the values of the extra grid variable, number n. The var keyword may also
be used, as explained in Section 1.2 in the Command Reference under the INITIAL
command.
MODEL var
sets the model (in a particular range of zones) to the user-defined constitutive model
var, written as a FISH function. If the model defines new properties, then the names
of these may be given as parameters to the PROPERTY, PRINT and PLOT commands.
PLOT ex n <zone>
plots contours of the extra grid variable n, treating it as a scalar quantity. FLAC will
assume the variable is associated with gridpoints unless the keyword zone is given,
which indicates that the extra grid variable is associated with zone centroids.
PLOT ex n,m <zone>
plots vectors of the extra grid variables n and m, where n corresponds to the x-
component of the vector, and m corresponds to the y-component. FLAC will assume
the variables are associated with gridpoints unless the keyword zone is given, which
indicates that the extra grid variables are associated with zone centroids.
PLOT ex n,m,l <zone>
plots tensors constructed from the extra grid variables n, m and l, where n corresponds
to the xx-component of the tensor, m corresponds to the yy-component, and l to
the xy-component. (The tensor is assumed to be symmetric.) FLAC assumes the
variables are associated with gridpoints unless the keyword zone is given, which
indicates that the extra grid variables are associated with zone centroids.
PRINT var
prints out the value of the FISH variable var. var (and everything else that it calls)
will be executed if it is a FISH function.
PRINT ex n <zone>
prints out the values of the extra grid variable, number n. The usual range limits may
be used, as with any other FLAC grid variable. The keyword zone inhibits printing
of the last row and column (which are only used for gridpoints).
PRINT fish
prints out a list of FISH symbols and either their current values or an indication of
their type. c m indicates a constitutive model function. Variables with names that
start with a dollar sign ($) are not printed with the above command, but they may be
printed with the PRINT $fish command.
PRINT fishcall
prints the current associations between fishcall ID numbers and FISH functions (see
Section 2.4.4).
SET fishcall n <remove> name
The FISH function name will be called in FLAC from a location determined by the
value of the fishcall ID number n. The currently assigned ID numbers are listed in
Table 2.2. The optional keyword remove causes the FISH function to be removed
from the list when placed before the FISH function name.
In general, FLAC and FISH operate as separate entities; FISH statements cannot be given as FLAC
commands, and FLAC commands do not work directly as statements in a FISH program. However,
there are many ways the two systems may interact; some of the more common ways are listed:
1. Direct use of function – A FISH function is executed at the user’s request by
giving its name on an input line. Typical uses are to generate geometry, set up
a particular profile of material properties, or initialize stresses in some fashion.
2. Use as a history variable – When used as the parameter to a HISTORY com-
mand, a FISH function is executed at regular times throughout a run, whenever
histories are stored.
3. Automatic execution during stepping – If a FISH function makes use of the
generalized fishcall capability (or contains the WHILESTEPPING statement),
then it is executed automatically at every step in FLAC ’s calculation cycle,
or whenever a particular event occurs. (See Section 2.4.4 for a discussion on
fishcall.)
4. Use of function to control a run – Since a FISH function may issue FLAC
commands (via the COMMAND statement), the function can be used to “drive”
FLAC in a way that is similar to that of a controlling data file. However, the
use of a FISH function to control operation is much more powerful, since
parameters to commands may be changed by the function.
5. Use of constitutive model functions – A FISH function that can be used instead
of the built-in constitutive models may be written. This type of user-written
model is integrated into FLAC ’s calculation cycle at the deepest level (see
Section 2.8 for complete details).
The primary way of executing a FISH function from FLAC is to give its name as FLAC input. In
this way, FISH function names act just like regular commands in FLAC. However, no parameters
may follow the function name so given. If parameters are to be passed to the function, then they
must be set beforehand with the SET command (explained later).
There is another important link between FISH and FLAC: a FISH symbol (variable or function
name) may be substituted anywhere in a FLAC command that a number is expected. This is a very
powerful feature, because data files can be set up with symbols rather than with actual numbers.
Example 2.9 shows how a data file that is independent of the mesh density can be constructed; the
same geometry (a hole in the center of a block) can be set up for any number of elements in the
mesh simply by changing two numbers in the SET command.
Example 2.9 illustrates several of the points made above: the function make hole is invoked by
giving its name on a line; the parameters controlling the function are given beforehand with the SET
command; there are no numerical values in the FLAC input (they are all replaced by symbols).
String variables may be used in a similar way, but their use is much more restricted than the use of
numerical variables. A FISH string variable may be substituted (a) wherever a file name is required,
or (b) as a parameter to the TITLE command. In these cases, the @ sign must be attached to the FISH
name of the variable, so that FLAC can distinguish between a literal name and a variable standing
for a name. Example 2.10 illustrates the syntax.
The intrinsic function string( ) is described in Sections 2.2.7 and 2.5.5; it converts a number to a
string.
Another important method of using a FISH function is to control a FLAC run or a series of FLAC
operations. FLAC commands are placed within a COMMAND . . . ENDCOMMAND section in the
function. The whole section may be within a loop, and parameters may be passed to FLAC com-
mands.
This approach is illustrated in Example 2.11, in which 14 complete runs are done, each with a
different value of Poisson’s ratio. The results are summarized by a movie which displays the
exaggerated grid distortion for each case in sequence.
end
grid 5 5
mod elas
prop dens=2000
set grav 10
fix x y j=1
set youngs 1e8
movie file s.mov on
window -2 7 -2 7
series
For each run (i.e., execution of the loop), all grid variables are reset and properties redefined.
The title is also redefined, so that the current value of Poisson’s ratio is displayed on each plotted
screen. For successful movies, it is usually necessary to specify a fixed window and fixed scales (e.g.,
magnification, vector scales or contour intervals) so that comparison between frames is meaningful.
FLAC has a built-in error-handling facility that is invoked when some part of the program detects an
error. There is a scheme for returning control to the user in an orderly fashion, no matter where the
error may have been detected. The same logic may be accessed by a user-written FISH function, by
using the predefined scalars error, nerr, ierr and jerr (see Section 2.5.1 for a complete list of scalars).
If a FISH function sets error to a nonzero value (e.g., 1), then the error-handling facility of FLAC
is invoked immediately, and a message is printed. For example, stepping and FISH processing stop
as soon as error is set. The message that is printed depends on the value of nerr that is set:
nerr
0 no message
>0 FLAC ’s internally defined message is displayed.
<0 The value of the FISH variable fish msg is displayed if it contains a string;
otherwise, nothing is printed. The FISH function that sets nerr should assign
a string to fish msg, which should not be a function name. If fish msg
does not exist as a FISH variable, then a generic message is indicated.
The value of nerr may also be tested. It will contain the last error number to be processed by FLAC.
The variables ierr and jerr correspond to the grid (i, j) numbers that are displayed by some messages.
If nerr is tested in an optimized function, the value of zero will always be returned.
It is important to note that execution of a FISH function is terminated immediately after the statement
error = 1 is encountered; all required assignments (e.g., setting nerr and fish msg) should be
done before setting error. Note that error can also be tested, but it always returns a value of zero.
The error-handling mechanism may also be used in situations that do not involve “errors.” For
example, stepping may be halted when a certain condition is detected, as illustrated in Example 2.12:
The run will stop when the unbalanced force is less than the set value. Note that in this example
the test of step is necessary because the unbalanced force is zero at the start of stepping.
2.4.4 FISHCALL
FISH functions may be called from several places in the FLAC program. The form of the command
is
SET fishcall n <remove> name
Setting a fishcall causes the FISH function name to be called from FLAC from a location determined
by the value of ID number n. Currently, the ID numbers shown in Table 2.2 are assigned (at present,
they are all in the calculation cycle). The numbers indicate the position where fishcall is located in
the program. Note that ID numbers (in Table 2.2) are placed between specific components of the
calculation cycle. This indicates the stage at which the corresponding FISH function is called. For
example, a function associated with ID 1 would be called just before the stress calculation (stresses
from velocities); ID 4 functions would be called just after.
The ID number 0 also corresponds to functions that contain the WHILE STEPPING statement (i.e.,
these functions are automatically mapped to ID 0). Any number of functions may be associated
with the same ID number (although the order in which they are called is undefined; if the order is
important, then one master function should be called, which then calls a series of sub-functions).
Also, any number of ID numbers may be associated with one FISH function. In this case, the same
function will be invoked from several places in the host code.
Parameters may be passed to FISH functions called by using the intrinsic function fc arg(n), where
n is an argument number. The meanings of the parameters (if any) are listed in Table 2.2.
In addition to the fishcall ID numbers shown in Table 2.2, there is also a fishcall 20 which can be
called before the calculation cycle. This fishcall provides control over the convergence criterion
used in the SOLVE fos command. Arguments available for this fishcall are
fc arg(1) input: lower FOS bracket
fc arg(2) input: upper FOS bracket
fc arg(3) output: set 0 to continue, or 1 to stop solve process
There is a printout keyword fishcall (the abbreviation is fishc) that lists the current associations
between ID numbers and FISH functions (i.e., PRINT fishcall).
The SET fishcall command normally adds the given name to the list already associated with the
given ID number. However, the keyword remove, placed before the FISH name, causes the FISH
function to be removed from the list. For example,
set fishcall 2 remove xxx
will remove the association between function xxx and ID number 2. Note that a FISH function
may be associated twice (or more) with the same ID number. In this case, it will be called twice
(or more). The remove keyword will remove only one instance of the function name.
The data file in Example 2.13 illustrates the use of a fishcall. The function rotation is called just
before the motion calculation in FLAC (ID = 14) in order to apply x- and y-velocity components
to rotate a grid. This example tests the stress calculation in the large-strain mode; the σxx and σyy
stress components return to their initial values following a cyclic rotation of 30 degrees.
hist sxx i 1 j 1
hist syy i 1 j 1
hist z_area
def series
loop nn (1,10)
command
step 100
plot grid vel stres
end_command
end_loop
end
movie file rotate.mov on
window -1 2 -1 2
series
movie off
plot hold his 3 4 vs 1
There are certain functions, variables and arrays that are built into FISH – the names of these
entities must be avoided when naming user-defined variables or functions. This section describes
all predefined entities, except those pertaining to user-defined constitutive models (which are listed
in Section 2.8). The entities are organized into several categories: scalars (single variables), grid
variables, constitutive model variables, general intrinsic functions, table functions and variables
that give access to FLAC ’s data structure. In some cases, an entity is listed under more than one
category, as appropriate.
The variables listed in this category have a single value, and are specifically related to internal FLAC
data structures or the solution process. An asterisk (*) denotes that the variable may be assigned a
value within a user-written function; otherwise, the variable’s value may only be tested, not set.
app pnt pointer to APPLY list structure (integer); see Section 2.5.7
appgw pnt pointer to the data structure representing applied groundwa-
ter items (integer); see Section 2.5.7
appth pnt pointer to the data structure representing applied thermal
items (integer); see Section 2.5.7
att pnt pointer to ATTACH list structure (integer); see Section 2.5.7
cf axi axisymmetry option (CONFIG axisymmetry) = 1 if config-
ured, else 0
cf creep creep option (CONFIG creep) = 1 if configured, else 0
cf dyn dynamic option (CONFIG dynamic) = 1 if configured, else 0
cf ext extra grid variables (CONFIG extra) returns the number of
grid variables
cf gw groundwater option (CONFIG gwflow) = 1 if configured, else
0
cf ps plane-stress option (CONFIG p stress) = 1 if configured, else
0
cf therm thermal option (CONFIG thermal) = 1 if configured, else 0
cm max * only defined within a constitutive model; the model must
return cm max as the maximum confined modulus; see Sec-
tion 2.8
crtdel timestep for creep calculation (as set by the SET crdt com-
mand)
crtime “creep time”
dydt gpi returns critical gridpoint i value resulting from dynamic
timestep
dydt gpj returns critical gridpoint j value resulting from dynamic
timestep
dytdel timestep for dynamic calculation (as set by the SET dydt
command)
dytime “dynamic time” – real time used in a fully dynamic simula-
tion
error * If error (an integer) is set by a FISH function to a nonzero
value, then control immediately passes to FLAC ’s error pro-
cessor; see Section 2.4.3. error always returns zero if it is
tested.
fos final value of fos
fos f current value of multiplier, F , used in SOLVE fos; see Sec-
tion 2.5.1.1 for more details
gwtdel timestep for groundwater calculation (as set by SET gwdt
command)
gwtime “groundwater time” (consolidation time)
ieb pnt pointer to IEB list structure (integer); see Section 2.5.7
ierr * zone or gridpoint number printed out in some error mes-
sages (integer). It can be set or tested; see Section 2.4.3 for
details.
igp total number of gridpoints in i-direction (integer)
int pnt pointer to interface list structure (integer); see Section 2.5.7
izones total number of zones in i-direction (integer)
jerr * zone or gridpoint number printed out in some error mes-
sages (integer). It can be set or tested; see Section 2.4.3 for
details.
jgp total number of gridpoints in j -direction (integer)
There is also a group of scalar variables (not listed above) that are only for use in a user-defined
constitutive model (see Section 2.8).
The FISH scalar fos f is the current F used in the SOLVE fos calculation. fos f is only nonzero
during cycling that is done as part of the fos solution; therefore fos f can be tested against zero
to determine whether fos is active. At the start of a fos solution, an elastic simulation is done to
determine the characteristic time for the system. During cycling in this phase, fos f has the value
1 × 1020 . The user-written FISH code may test for this value, to set non-failing conditions, for
example.
Note that SOLVE fos executes multiple save/restore cycles, so that a user-written FISH function will
not retain variable values between cycles. Any information to be passed between cycles can be
written to file. See the following example, which writes successive values of F to an ASCII file.
In general, a binary file can be used to speed communication between successive incarnations of a
user-written FISH function.
nlines = parse(arr(1),1)
if nlines > 0
loop n (1,nlines)
oo = read(arr,1)
arrsav(n) = arr(1)
endLoop
endif
nlines = nlines + 1
oo = close
oo = open(’result.out’,1,1) ; Write out old results + new one
arr(1) = string(nlines)
oo = write(arr,1)
if nlines > 1
loop n (1,nlines-1)
arr(1) = arrsav(n)
oo = write(arr,1)
endLoop
endif
arr(1) = string(fos_f)
oo = write(arr,1)
oo = close
endif
end
set fflag=0
solve fos
11
1.0000E+20
1.0000E+00
2.0000E+00
1.5000E+00
1.2500E+00
1.3750E+00
1.4375E+00
1.4063E+00
1.3906E+00
1.3984E+00
1.3945E+00
The variables listed in this category have a single value and are not specifically related to FLAC;
they are general-purpose scalars. An asterisk (*) denotes that a variable may be assigned a value
within a user-written function; otherwise, the variable’s value may only be tested, not set. The
variables listed below are of floating-point type unless declared otherwise.
clock number of hundredths-of-a-second from midnight
cycle current cycle (step) number
degrad π/180 (used to convert degrees to radians – for example,
a = cos(30*degrad) gives the cosine of 30◦ )
The following reserved names refer to gridpoint or zone variables that require the row and column
numbers to be specified in parentheses immediately following the name. For example, the xx-stress
in zone (i,j) would be referred to as sxx(i,j), where i and j evaluate to legal integers that
correspond to grid indices; an error will be indicated during execution if the indices are out of the
range of the grid. Both i and j may be arbitrarily complex expressions involving functions or
even other FLAC array names. The following variable names must be spelled out in full in FISH
statements; they cannot be truncated, as in FLAC commands. An asterisk (*) denotes that the
variable (or individual bit) can be modified by a FISH function; otherwise, its value may only be
tested. All grid variables are of floating point type except for flags and model, which are integers,
and damp, which depends on its arguments.
Zone variables evaluate to zero if the zone is null, and gridpoint variables evaluate to zero if the
gridpoint is surrounded by null zones.
where i,j are the zone indices and vname is a string denoting
the variable:
‘modfac’ Mt
‘e1’ γ1
‘e2’ γ2
† If the zone is not associated with a group name, then var will contain the integer zero. Thus, before
printing out a string, var should be tested, using the type () function – e.g.,
loop i (1, izones)
loop j (1, jzones)
var = z group(i,j)
if type(var) = 3
oo = out(string(i) +’,’+string(j)+’ Group = ’+var)
else
oo = out(string(i)+’,’+string(j)+’ No group’)
endif
endLoop
endLoop
When z group(i,j) is used as the destination in an expression, the source variable is used as the group
name to be associated with the zone (i,j ). If the zone is already associated with a group name, the
association is replaced with the new one. If the group name does not already exist, it is created. If
the source variable is not a valid string, nothing is done, and no error is indicated. For example:
grid 10 10
model elas
def qqq
z group(2,2) = ’fred’
z group(3,3) = 33
end
qqq
In this case, zone (2,2) receives the group name “fred,” but zone (3,3) receives nothing. The
command PRINT group lists “fred” as a valid group name. A group name can be associated with a
zone even if the zone contains the null model. Note that the group name must contain at least two
characters.
when i,j are the zone indices, and pname is a string denoting
the property name. Refer to the model description for a list of
property names. Note that the Finn and Hoek-Brown models
are DLLs; their property names are listed in Section 1 in the
Command Reference.
z prop also returns the plasticity state number for the Finn
and Hoek-Brown models:
num = z prop(i,j,‘state’)
There are eight FISH zone “variables” (ssr, ssi, ssr3d, ssi3d, vsr, vsi, fsr, fsi) that give user-written
functions access to zone strains and strain rates. Note that “shear strain rate” in this context means
the square root of the second invariant of the deviatoric strain rate. The following four (read-only)
functions return only single values for the zone identified by index i, j:
ssi(i, j) maximum shear strain increment (in the 2D plane)
ssr(i, j) maximum shear strain rate (in the 2D plane)
ssi3d(i, j) maximum shear strain increment (3D formulation)
ssr3d(i, j) maximum shear strain rate (3D formulation)
vsi(i, j) volumetric strain increment
vsr(i, j) volumetric strain rate
The following two functions provide all the tensor components, rather than invariants, providing
four components in the array arr, which the user must declare beforehand, and which must have at
least four elements (the function itself returns a zero).
fsi(i, j, arr) full strain increment tensor
fsr(i, j, arr) full strain rate tensor
The components in arr are ordered according to the array index (given as the first number in the
following table):
index 1 xx
index 2 yy
index 3 zz
index 4 xy
The computation of strain rate is identical to that performed by FLAC when it applies constitutive
relations to a zone (see Section 1.3.3.1 in Theory and Background) – i.e., strain rates are based on
gridpoint velocities and the current coordinates. “Strain increments” are computed with the same
equations, but with displacements substituted for velocities. There are two potential problems with
the latter calculation. First, gridpoint displacements may be reset or changed at any time by the
user; they are not used in the calculation process. It is the user’s responsibility to ensure that
displacements are accumulated over a meaningful interval. Second, the strain increments are based
on the geometry at the time of measurement. If the geometry changes significantly during a large-
strain simulation, the measured strain will depend on the assumption made about the reference state
for geometry; there are many different formulations for strain to be found in the literature when
large displacements are involved. The conscientious user may wish to use only the function fsr,
and derive strain increments by accumulation, using some formula of choice.
Note that the functions described take a long time to execute. It is rather inefficient to compute the
values for each zone at each timestep, if strains are only needed for certain zones (e.g., for histories);
the recording logic should be done only for the zones of interest. Further, since each function takes
about the same time to execute, it is better to use fsr or fsi if more than one strain component is
needed for a zone.
Example 2.15 illustrates the application of these functions to calculate strain components. Note
that the arrays must be defined first. The strain increment and strain rate tensors are then calculated
and the arrays are filled with the statements
dum = fsr(i,j,ar)
dum = fsi(i,j,ai)
def qqq
array ar(4) ai(4)
loop i (1,izones)
loop j (1,jzones)
dum = fsr(i,j,ar)
dum = fsi(i,j,ai)
ex_1(i,j) = sqrt((ar(1)-ar(2))ˆ 2 + 4.0 * ar(4)ˆ 2) / 2.0
ex_2(i,j) = sqrt((ai(1)-ai(2))ˆ 2 + 4.0 * ai(4)ˆ 2) / 2.0
ex_3(i,j) = ar(1) + ar(2) + ar(3)
ex_4(i,j) = ai(1) + ai(2) + ai(3)
ex_5(i,j) = ai(1)
ex_6(i,j) = ai(2)
endLoop
endLoop
end
qqq
;--- to test, give the following commands, line by line, & compare
print ssr ex_1 zon
print ssi ex_2 zon
print vsr ex_3 zon
print vsi ex_4 zon
pause
plot hold ex_6 zone fil bou
Property values for all constitutive models may be accessed (changed, as well as tested) in any
general FISH function (i.e., any function that is not a constitutive model itself). As with the general
grid variables, indices must immediately follow the name of the property, enclosed in parentheses.
Property names must be spelled out in full, followed by zone indices (which may be single numbers,
symbols, or complicated expressions); for example, a valid FISH statement would be
cohesion(2,3) = 3.4e5
If you write a function that tries to change a property variable, but the associated model does not
exist at the specified grid location (i,j), then no error will be given; however, no change will be
made to the grid. Similarly, if you try to obtain the value of a property for which no corresponding
model exists at the specified grid location, the value will be returned as zero; no error will be
detected. Consult Section 1.2 in the Command Reference and Section 1 in Constitutive Models
for further information on the meanings and usage of the following variables.
∗ available only for creep model option — see Section 1 in Creep Material
Models
† available only for thermal model option — see Section 1 in Thermal Analysis
Note that properties for the CPP built-in models are accessed with the z prop function rather than
directly by name.
In addition to the above set of names corresponding to built-in models, the properties of user-written
models may be addressed in a similar manner. Again, the names must be spelled in full, and zone
indices must follow the name if the property is referred to outside of the function in which it is
defined. See Section 2.8 for a discussion on properties for user-written models. Properties of DLL
models are set and retrieved with the z prop function (Section 2.5.3.2).
All functions return floating-point values except for and, or, not, int and type, which return integers,
and get mem, which returns a pointer. The functions max, min, abs and sgn return integers if their
argument(s) are all integer; otherwise, they return as floating-point. All functions must be placed on
the right-hand side of an assignment statement, even if the function’s return value is of no interest.
For example,
ii = out(’ Hi there!’)
is a valid way to use the out function. In this case, ii is not used.
abs(a) absolute value of a
acos(a) arc cosine of a (result is in radians)
and(a,b) bit-wise logical and of a and b
asin(a) arc sine of a (result is in radians)
atan(a) arc-tangent of a (result is in radians)
atan2(a,b) arc-tangent of a/b (result is in radians) (NOTE: b may be
zero.)
cos(a) cosine of a (a is in radians)
cparse(s, nc1, nc2) see Section 2.6
error string
This function causes an error condition. FISH function pro-
cessing (and command processing) stops immediately. The
message reported is string. This function can be used for
assignment only. (string = error is not allowed.)
exp(a) exponential of a
The functions described in the previous section are conventional in the sense that they simply return
a value, given some parameter(s), or they are executed for some effect. In other words, they always
appear on the right-hand side of any assignment statement. In contrast, the functions described in
this section (except the array functions in Section 2.5.6.2) may appear on either side of an assignment
(= sign). They act partly as functions and partly as arrays.
2.5.6.1 Tables
The functions table, xtable, ytable and table size allow FISH functions to create and manipulate
FLAC tables, which are indexed arrays of number pairs used in several of FLAC ’s commands and
operations. However, tables are different from arrays in other programming languages. Tables are
dynamic data structures; items may be inserted and appended, and interpolation between values may
be done automatically. Consequently, the manipulation of tables by FISH is time-consuming. Use
them with caution! The action of each function depends on whether it is the source or destination
for a given data item; hence, each function is described twice.
A table is a list of pairs of floating-point numbers, denoted for convenience as x and y, although the
numbers may stand for any variables, not necessarily coordinates. Each table entry (or (x,y) pair)
also has a sequence number in the table. However, the sequence number of a given (x,y) pair may
change if a new item is inserted in the table. Sequence numbers are integers that start from 1 and
go up to the number of items in the table. Each table has a unique identification number, which
may be any integer except zero.
There are two distinct ways that tables may be used in a FISH function. The table function behaves
in the same way as the regular FLAC TABLE command (i.e., insertion and interpolation is done
automatically). The other functions, xtable and ytable, allow items to be added or updated by
reference to the sequence numbers; no interpolation or insertion is done.
y = table(n,x) The existing table n is consulted, and a y-value is found by in-
terpolation, corresponding to the given value of x. The value
of x should lie between two consecutive stored x-values, for
the results to be meaningful. An error is indicated if table n
does not exist.
2. The functions xtable and ytable, rather than table, should be used to update
values in an existing table. Although table will update an (x,y) pair if the given
x is identical to the stored x, there may be slight numerical errors, which can
result in insertion rather than updating.
3. In a FISH function that replaces old table values with new values, it is necessary
to create the table first, since the action of retrieving old values will produce
an error. A complete table may be created (and its entries all set to zero) by a
single statement, as illustrated:
xtable(4,100) = 0.0
If table 4 does not exist, then it is created. 100 entries are also created, each
containing (0.0,0.0). Subsequent statements, such as
xtable(4,32) = xtable(4,32) + 1.0
ytable(4,32) = ytable(4,32) + 4.5
will update table values, but will not alter the length of the table. If the latter
statements are executed before table 4 exists, then an error will be detected.
4. Stored values (both x and y) in tables are always floating-point numbers. Given
integers are converted to floating-point type before storing. Be careful about
precision!
As an example in the use of table functions, the input sequence in Example 2.16 produces a smooth-
pointed, star-shaped hole in a FLAC grid.
xxx
def set_mat
array aa(nn,nn) bb(nn,nn)
end
Example 2.18 contains an example and validation of the matrix inversion function, mat inverse().
Warning! This section is intended for experienced programmers who are familiar with the use of
linked lists. The techniques described here are powerful because they provide access to most of the
internal data in FLAC, but they are dangerous if used without full understanding.
Most of FLAC ’s data are stored in a single, one-dimensional array. A FISH program has access to
this array via imem and fmem, which act like array names for integer and floating-point numbers,
respectively. Given index iad (which must be an integer), floating-point (f) or integer (i) values can
be found from
f = fmem(iad)
i = imem(iad)
Values can also be inserted in the array:
fmem(iad) = f
imem(iad) = i
These functions are potentially very dangerous, as any data can be changed in FLAC ’s main array.
Only experienced programmers should use them. No checking is done to verify that iad is an
integer, so the user must be very careful. The use of these functions is explained in Section 2.5.7.
In some cases (notably, when using structural piles) it is possible to replace FLAC ’s built-in logic
with the same user-defined logic embodied in a FISH function. In order to communicate parameters
between the FISH function and FLAC ’s internal logic, the special function fc arg is provided. The
function may appear as the source or destination (right- or left-hand side of an expression), and is
used as follows.
aa = fc arg(n)
fc arg(m) = bb
The arguments n and m can be any FISH variable or expression that evaluates to an integer in the range
1 to 20, inclusive (i.e., there are 20 separate parameters that can be used to pass information). The
parameters may be integers or floating-point values (but not strings). For an example application
of fc arg, see the user-defined normal coupling-spring function, cs nfunction, described in
Section 1.11.4 in Structural Elements.
Warning! This section is intended for experienced programmers who are familiar with the use of
linked lists. The techniques described here are powerful because they provide access to most of the
internal data in FLAC, but they are dangerous if used without full understanding.
Most of FLAC ’s data are stored in a single, one-dimensional array. A FISH program has access to
this array via imem and fmem (described in Section 2.5.6.3), which act like array names for integers
and floating-point numbers, respectively. Note that imem(n) and fmem(n) map into the same location
in memory; it is the responsibility of the programmer to access the correct type of data. Pointers
to the main data structures are listed in Section 2.5.1. Each data structure consists of one or more
linked lists, with offsets to individual data items listed in Section 4. Memory can also be allocated
from a FISH program, and used to store whatever the programmer wishes. In many cases, FLAC ’s
data blocks contain one or more spare words that can be used as pointers to extension arrays that can
be allocated as needed. Although tables can be used to store data, it is much faster to use allocated
memory directly via imem and fmem.
To illustrate the use of the concepts outlined above, let us write a FISH program (see Example 2.19)
to record the maximum normal force that acts within an interface, and the step number and gridpoint
at which the maximum occurred. After setting up the problem, we allocate a block of memory to
contain the recorded data, and set up a pointer to it from the interface. The logic should work for
any number of interfaces, although Example 2.19 only involves one interface.
ip = int_pnt
loop while ip # o ;scan interfaces
ii = imem(ip+1)
loop nside (2,3) ;scan 2 sides
ispt = imem(ip+nside)
loop while ispt # 0 ; scan nodes on one side
if abs(fmem(ispt+5)) > fmem(ii) then
fmem(ii) = abs(fmem(ispt+5))
imem(ii+1) = ispt
imem(ii+2) = step
end_if
ispt = imem(ispt)
end_loop
end_loop
ip = imem(ip)
end_loop
end
def sho_data
ip = int_pnt
loop while ip # 0
ii = imem(ip+1)
s1 = ’ max-Fn = ’+string(fmem(ii))+’, ’
ig = imem(imem(ii+1)+2)
jg = imem(imem(ii+1)+3)
s2 = ’ g.p.(’+string(ig)+’,’+string(jg)+’)’
s3 = ’ at step ’+string(imem(ii+2))
xx = out(s1+s2+s3)
ip = imem(ip)
end_loop
end
step 500
sho_data
The set of FISH functions described in this section enable data to be written to, and read from, a
file. There are two modes, namely an “ASCII” mode that allows a FISH program to exchange data
with other programs, and a “FISH” mode that enables data to be passed between FISH functions.
In FISH mode, the data are written in binary, without loss of precision, whereas numbers written
out in ASCII form may lose precision when read back into a FISH program. In FISH mode, the
value of the FISH variable is written to the file, not the name of the variable. Only one file may be
open at any one time.
close The currently open file is closed; 0 is returned for a successful operation.
open(filename, wr, mode)
This function opens a file, named filename, for writing or reading. The
variable filename can be a quoted string or a FISH string variable.
Parameter wr must be an integer with one of two values:
0 file opened for reading; file must exist
1 file opened for writing; existing file will be overwritten
Parameter mode must be an integer with one of two values:
0 read/write of FISH variables; only the data corresponding to the
FISH variable (integer, float or string) are transferred, not the
name of the variable.
1 read/write of ASCII data; on a read operation the data are
expected to be organized in lines, with CR/LF between lines. A
maximum of 200 characters per line is allowed.
The returned value denotes one of several conditions:
0 file opened successfully
1 filename is not a string
2 filename is a string, but is empty
3 wr or mode (not integers)
4 bad mode (not 0 or 1)
5 bad wr (not 0 or 1)
6 cannot open file for reading (e.g., file does not exist)
7 file already open
8 not a FISH mode file (for read access in FISH mode)
read(ar, n)
reads n records into the array ar. Each record is either a line of ASCII
data or a single FISH variable. The array ar must be an array of at least n
elements. The returned value is
0 requested number of lines were input without error
−1 error on read (except end-of-file)
n positive value indicates that end-of-file was encountered after
reading n lines
In FISH mode, the number and type of records must match exactly the
number and type of records written. It is up to the user to control this. If
an arbitrary number of variables is to be written, the first record could be
made to contain this number, so that the correct number could subsequently
be read.
write(ar, n)
writes n records from the first n elements of the array ar. Each record is
either a line of ASCII data, or a single FISH variable. For ASCII mode,
each element written must be of type string. The array ar must be an array
of at least n elements. The returned value is
0 requested number of lines were output without error
−1 error on write
n positive value (in ASCII mode) indicates that the nth element
was not a string (hence only n−1 lines were written). An error
message is also displayed on the screen.
The following intrinsic functions do not perform file operations, but can be used to extract items
from ASCII data that are derived from a file.
cparse(s, nc1, nc2)
This function scans the string s and decodes the characters between columns
nc1 and nc2 (inclusive) as integer, float or string.
parse(s, i)
This function scans the string s and decodes the ith item, which it returns.
Integers, floats and strings are recognized. Delimiters are the same as for
general commands (i.e., spaces, commas, parentheses, tabs and equal signs).
If the ith item is missing, zero is returned. An error message is displayed,
and zero is returned if the variable s is not a string.
pre parse(s, i)
This function scans the string s and returns an integer value according to
the type of the ith item:
0 missing item
1 integer
2 float
3 string missing (unable to interpret as int or float)
Example 2.20 illustrates the use of the FISH I/O functions:
endif
endloop
;
if pre_parse(bb(3), 4) # 3 then
oo = out(’ Not a string’)
exit
endif
;
; FISH I/O TEST -----------------
status = open(filename, IO_WRITE, IO_FISH)
funny_int = 1234567
funny_float = 1.2345e6
aa(1) = ’---> All tests passed OK’
aa(2) = funny_int
aa(3) = funny_float
;
status = write(aa,3)
status = close
status = open(filename, IO_READ, IO_FISH)
status = read(bb, 3)
status = close
;
; now check results...
if type(bb(1)) # 3 then
oo = out(’ Bad FISH string read/write’)
exit
endif
if bb(2) # funny_int then
oo = out(’ Bad FISH integer read/write’)
exit
endif
if bb(3) # funny_float then
oo = out(’ Bad FISH float read/write’)
exit
endif
oo = out(bb(1)) ; (should be a good message)
command
sys del junk.dat
endcommand
end
;
io
FISH contains the option to allow data to be exchanged between two or more Itasca codes running
as separate processes, using socket connections (as used for TCP/IP transmission over the Internet).
At present, socket I/O connections can be made between FLAC and PFC 2D or PFC 3D. FLAC 3D also
allows socket connections at this time. It is possible to pass data between two or more instances
of the same code (e.g., two instances of FLAC), but the main use is anticipated to be coupling
of dissimilar codes such as FLAC and PFC 2D. An example of such a coupling is provided in
Section 2.7.1.
The data contained in FISH arrays may be passed in either direction between two codes. The data
are transmitted in binary with no loss of precision. Up to six data channels may be open at any
one time; these may exist between two codes, or may connect several codes simultaneously. The
following FISH intrinsics are provided in FLAC . The word process denotes the instance of the code
that is currently running. All functions return a value of 10 if the ID number is invalid.
sclose(ID)
Channel ID is closed.
Note: The corresponding FISH intrinsics in PFC 2D / PFC 3D 5.0 is
socket.close(ID). See PFC documentation for details.
sopen(mode, ID)
The integer mode takes the value 0 or 1. A value of 1 causes the data channel
of number ID to be initiated, with the process acting as a server. Another
process can link to the server, with the same ID, by invoking sopen, with
mode = 0, which denotes the process as a client. The ID number must
be in the range 0 to 5 inclusive, giving a total of six possible channels
of communication. The server sopen function must be issued before the
client sopen function, for a given ID. While waiting for a connection, the
server process is unresponsive. The sopen function returns 0 when a good
connection has been made, and nonzero if an error has been detected.
Note: The corresponding FISH intrinsics in PFC 2D / PFC 3D 5.0 is
socket.open(mode, ID). See PFC documentation for details.
Data have been passed both ways between the two code instances. A more useful example is given
in Section 2.7.1.
This example demonstrates the use of the socket I/O functions to transfer data between two codes
executing separately. A deformable punch (modeled with FLAC) is driven into an assembly of
particles (modeled with PFC 2D 5.0). Initially, a series of walls is created in PFC 2D, with each
wall corresponding to a single surface segment of a FLAC zone. As the FLAC zones deform in
large-strain, gridpoint velocities are transferred to PFC 2D, so that the walls move in exactly the
same way as the boundary segments of the FLAC grid. The resulting wall forces, due to particles
interacting with the walls, are transferred to FLAC as applied gridpoint forces. In this way, a fully
coupled simulation is performed.
Three data files are used in the simulation. The file in Example 2.23 must first be executed by PFC 2D
to create an assembly of particles within a container, and bring the assembly to equilibrium under
gravity. The files in Examples 2.24 and 2.25 may then be used by FLAC and PFC 2D, respectively,
to execute the simulation. In order for the system to operate correctly, both codes should be started
as separate processes. Then, Example 2.24 should be called first from FLAC as the server, which
waits (indefinitely) for a connection from another process. The file Example 2.25 may then be
called from PFC 2D (operating as a client process) to establish contact. Initially, FLAC sends a
series of wall coordinates to PFC 2D, and both codes set up arrays of pointers to eliminate searching
during cycling. Once the setup process is complete, cycling begins in both codes, with forces being
sent by PFC 2D to FLAC, and velocities being sent by FLAC to PFC 2D. Cycling in both codes is
synchronized, using a timestep of unity, so that the same displacements are calculated in each step
in each code.
cyc 15000
ball prop fric 1.0
set timestep scale
cyc 15000
save pex0.p2sav
Example 2.24 FLAC initialization and run file for coupled FLAC/PFC 2D example
;fname: fi_02_24.dat
grid 20 10
gen -0.5 1.5 -0.5 2.0 0.5 2.0 0.5 1.5
model elas
prop dens 1000 shear 2.5e7 bulk 5e7
fix x y j=11
def setLimits ; Array limits ... must be changed for bigger problem
nbuff = 1000 ; general buffer size
nList = 200 ; max number of walls generated
igpf = igp
jgpf = jgp
end
setLimits
def iniComms ; initialize communications, & send wall data
array buff(nbuff) numrec(1)
array i1List(nList) j1List(nList) i2List(nList) j2List(nList)
array appnt(igpf,jgpf)
oo = sopen(1,1) ; channel 1 ... server
numWalls = 0
loop j (1,jzones) ; left side
numWalls = numWalls + 1
i1List(numWalls) = 1
j1List(numWalls) = j
i2List(numWalls) = 1
j2List(numWalls) = j+1
endLoop
loop i (1,izones) ; bottom
numWalls = numWalls + 1
i1List(numWalls) = i+1
j1List(numWalls) = 1
i2List(numWalls) = i
j2List(numWalls) = 1
endLoop
loop j (1,jzones) ; right side
numWalls = numWalls + 1
i1List(numWalls) = igp
j1List(numWalls) = j+1
i2List(numWalls) = igp
j2List(numWalls) = j
endLoop
ibuf = 0
loop nn (1,numWalls)
setIJ
ibuf = ibuf + 1
buff(ibuf) = x(i1,j1)
ibuf = ibuf + 1
buff(ibuf) = y(i1,j1)
ibuf = ibuf + 1
buff(ibuf) = x(i2,j2)
ibuf = ibuf + 1
buff(ibuf) = y(i2,j2)
command
apply xforce=1e-10 yforce=1e-10 i=i1 j=j1
endCommand
endLoop
numrec(1) = numWalls
oo = out(’ Sending ’+string(numWalls)+’ wall segments to PFC ...’)
oo = swrite(numrec,1,1)
oo = swrite(buff,ibuf,1)
ap = app_pnt ; set up easy access to apply-list pointers
loop while ap # 0
ii = imem(ap+$kapi1)
jj = imem(ap+$kapj1)
appnt(ii,jj) = ap
ap = imem(ap)
endLoop
end
def setIJ ; set i,j values, given index nn
i1 = i1List(nn)
j1 = j1List(nn)
i2 = i2List(nn)
j2 = j2List(nn)
end
def endComms ; Close communications
oo = sclose(1)
end
def sendVelocities
ibuf = 0
loop nn (1,numWalls)
setIJ
buff(ibuf+1) = xvel(i1,j1)
buff(ibuf+2) = yvel(i1,j1)
buff(ibuf+3) = xvel(i2,j2)
buff(ibuf+4) = yvel(i2,j2)
ibuf = ibuf + 4
endLoop
ibuf = numWalls * 4
oo = swrite(buff,ibuf,1)
getForces
end
def getForces
loop nn (1,numWalls) ; reset applied forces
setIJ
ap1 = appnt(i1,j1)
ap2 = appnt(i2,j2)
fmem(ap1+$kapv3) = 0.0
fmem(ap1+$kapv4) = 0.0
fmem(ap2+$kapv3) = 0.0
fmem(ap2+$kapv4) = 0.0
endLoop
ibuf = numWalls * 3
oo = sread(buff,ibuf,1)
ibuf = 0
loop nn (1,numWalls)
setIJ
FxW = buff(ibuf+1)
FyW = buff(ibuf+2)
MomW = buff(ibuf+3)
denom = FyW * (x(i1,j1)-x(i2,j2)) - FxW * (y(i1,j1)-y(i2,j2))
if denom # 0.0
getSeg
command
wall create id=@IDmake vertices (@x1,@y1) (@x2,@y2)
wall property kn=1e8 ks=1e8 range id @IDmake by wall
endCommand
section
loop foreach local wp wall.list
if wall.id(wp) = IDmake
exit section
endif
endLoop
io.out(’ ** error in finding wall pointer’)
Exit
EndSection
Wpoint(nn) = wp ; save wall pointer
IDmake = IDmake + 1
endLoop
end
define getSeg
global ibuf = ibuf + 1
global x1 = buff(ibuf)
ibuf = ibuf + 1
global y1 = buff(ibuf)
ibuf = ibuf + 1
global x2 = buff(ibuf)
ibuf = ibuf + 1
global y2 = buff(ibuf)
end
define endComms
socket.close(1)
end
define getVelocities
global ibuf = numWalls * 4
socket.read(buff,ibuf,1)
local ibuf = 0
loop local nn (1,numWalls)
ibuf = ibuf + 1
local xv1 = buff(ibuf)
ibuf = ibuf + 1
local yv1 = buff(ibuf)
ibuf = ibuf + 1
local xv2 = buff(ibuf)
ibuf = ibuf + 1
local yv2 = buff(ibuf)
local wp = Wpoint(nn)
Figure 2.1 shows the initial particle assembly and the 40 walls that form the deformable punch,
corresponding to three sides of a 200-zone FLAC grid. Figure 2.2 shows the final state from PFC 2D,
in which the active surfaces of the punch exhibit large deformation in response to the forces exerted
by the particles. Figure 2.3 shows the corresponding state from FLAC, in which the same surface
deformation is evident, as well as contours of major principal stresses. Note that FLAC will halt
with a “bad geometry” error if the simulation is continued much further.
C
pGrb00 0 u0 i
l
lFrnnc
l
lFihc
l l
lFhnc
Figure 2.1 Initial particle assembly and walls that form deformable punch
C
pGrb00 0 u0 i
l
lFrnnc
l
lFihc
l l
lFhfnc
LEGEND
6-Nov-15 13:49
step 1300
1.400
-6.134E-01 <x< 5.974E-01
5.106E-01 <y< 1.721E+00
Boundary plot
0 2E -1 1.200
0 2E -1
0.600
The physical process is quite straightforward, but there are some important points to note:
1. Large-strain mode should be used in FLAC, because PFC 2D cannot operate
in any other way, and it is necessary for wall movement to keep track with
zone-boundary movement.
2. In a simulation that is closely coupled, data should be exchanged at every
cycle, at the appropriate points in the calculation cycles for both codes.
3. The timestep in both codes should be identical. This is achieved here by
running FLAC in static mode, and PFC 2D with timestep scaling. Thus, the
timestep is unity for both processes*. Alternatively, both codes could be run
in dynamic mode with the same timestep.
Some manipulation of forces sent from PFC 2D must be done, because each wall produces forces
and moments relative to its center of rotation. FLAC gridpoint forces are derived from this data by
using equations of moment equilibrium, to determine the line of action of the resultant force. To
avoid giving many APPLY commands at each step, the data in an APPLY list are modified directly
by a FISH function, using a single APPLY list that is set up at the beginning of the run.
* The kinematic constraints for the walls take precedence, and the timestep may end up being smaller
than unity. To make sure the kinematic constraints in this example do not modify the timestep, the
following line is added in Example 2.24: wall tolerance ctolerance 1e-3. This effectively increases
the wall cell extents and relieves the kinematic constraints.
The example is contrived and simplified, but it illustrates that closely coupled interaction may be
achieved between a continuum region and a region that contains discrete bodies.
New constitutive models may be written in the FISH language. Once compiled successfully, a new
model behaves just like a built-in model as far as the user is concerned (i.e., it can be installed and
removed by using the MODEL command). A user-defined model can also use properties that are
defined and given names by the model’s author; these names act just like built-in properties (i.e.,
they can be set with the PROPERTY command, printed with the PRINT command and plotted with
the PLOT command).
User-written models execute more slowly than built-in models. After optimization (described in
Section 2.9), a FISH model will typically run somewhere between one-quarter and one-third the
speed of a built-in model. However, quite often, a user-written model needs only to be installed in
a small part of the FLAC grid, since the particular behavior that it is designed to reproduce may
only occur locally (e.g., cracking around a tunnel); the material elsewhere can be represented by a
standard model. In any case, the increased runtimes of the special model may be compensated for
by decreased human time and effort, since less work may be done trying to force-fit an inappropriate
model.
A user-written constitutive model is simply a FISH function containing some special statements
and references to special variables that correspond to local entities within a single zone. The user-
defined model (referred to as “UDM”) is called by FLAC four times per zone (once per triangular
subzone) for every solution step. It is the task of the UDM to supply a new set of stress components,
given strain increments and the old set of stress components. However, certain other tasks must be
performed, and certain conventions observed. These are described in the following sections.
When a new model has been developed, it should be exercised thoroughly on a one-zone grid with
all gridpoints fixed. Given strain paths should be applied to the zone, and histories made of the
stress response. The proposed model may be “debugged” very effectively in this way.
The following statement must appear at the beginning of a FISH function for it to be recognized as
a user-defined model (UDM).
CONSTITUTIVE MODEL <n>
where n is an optional identification number. The preceding statement is incompatible with the
WHILE STEPPING and COMMAND statements, which must not appear in the same function. Once
a function containing the statement CONSTITUTIVE MODEL has been successfully compiled, the
FLAC command MODEL can be used to install the UDM in the grid, just like a built-in model (see
Example 2.26).
The optional ID number that follows the word CONSTITUTIVE MODEL must be a positive integer,
and must not conflict with the ID numbers of any of FLAC ’s internal models (see Section 2.5.3
for a list of built-in model numbers, under variable name model). If the ID number is omitted or
is inappropriate, then an ID number of zero is taken. The given number is not necessary, but it
is useful when using a FISH function to perform some operation that requires a knowledge of the
model installed in a particular zone (the grid variable model will return the given ID number). Note
that the ID number can only be set by means of the CONSTITUTIVE MODEL statement; it cannot be
assigned anywhere else. The code fragment in Example 2.27 illustrates the use of an ID number.
The name for a UDM should not conflict with that of any of the built-in models (see Section 1.2 in
the Command Reference, under the MODEL command). Section 2.2.2 should also be consulted
to avoid conflicts with other predefined names.
Named variables may be used by the UDM, as with any other FISH function, but since regular
FISH variables are of global scope, they cannot be used to store things that are different for each
zone. However, variables that are local to each zone may be defined by declaring them in advance
with the f prop statement,
f prop var1 var2 var3 ...
This statement is followed by a list of names separated by space(s). Several such statements may
be given, but they should precede any executable code. The names must be unique, and must not
conflict with FISH statements or any built-in property name (see Section 2.5.4). Any length of
name is allowed, but only the first 12 characters are displayed on plots or print headings.
During compilation of the FISH function, the number of user-defined local variables is counted and
used by FLAC to allocate that number of extra words of memory per zone. The names mentioned
in the f prop statement(s) are completely equivalent to FLAC properties; they may be set with the
PROPERTY command, printed with the PRINT command, and plotted with the PLOT command (as
contours). The variables are of type float; they are normally used to store material properties, but
they can be used to store any state variables at the individual zone level. The names of the local
variables are used just like any other scalar variable within the function that defines them (i.e., they
are referred to without zone indices).
Local “property” variables are also available in other FISH functions (apart from the defining
function), but there they act like other property variables (i.e., zone subscripts are needed).
Some program fragments in Example 2.28 illustrate the use of local property variables:
In this example, the newly defined properties are used (as scalars) in the calculation of zone stresses
(discussed later). The same names are used in another function as indexed grid variables.
In general, note that indexed zone or gridpoint variables must not be used in a UDM function; the
function is already being called for a particular zone, so indices are irrelevant (and forbidden).
During stepping, the UDM function is called four times per zone per timestep (once for each
triangular subzone). When the function is called, the following local variables (all of type float)
are available within the function as scalars. The variables are undefined in functions that are not
constitutive model functions.
zart area or triangular subzone (input)
zde11 e11 strain increment (input)
zde12 e12 strain increment (input)
zde22 e22 strain increment (input)
zde33 e33 strain increment (input)
zdpp increment in pore pressure (output)
zdrot incremental zone rotation in SET large mode only (input)
zporos porosity of zone (in CONFIG gwflow mode only) (input)
zs11 σ11 effective stress (input and output)
zs12 σ12 effective stress (input and output)
zs22 σ22 effective stress (input and output)
zs33 σ33 effective stress (input and output)
zsub indicator for subzone averaging (input)
ztea thermal a-zone strain increment (input)
zteb thermal b-zone strain increment (input)
ztec thermal c-zone strain increment (input)
zted thermal d-zone strain increment (input)
ztsa thermal a-zone stress increment (output)
ztsb thermal b-zone stress increment (output)
ztsc thermal c-zone stress increment (output)
ztsd thermal d-zone stress increment (output)
zvisc dynamic viscosity inhibit flag (output)
In addition to the action during stepping (mentioned above), the UDM function must perform other
tasks. The scalar variable mode is defined whenever the function is called by FLAC. The UDM
should test the value of mode (an integer) and perform the appropriate tasks according to its value,
as follows.
mode
1 This mode is set when the function is called prior to stepping; the UDM
may perform any initialization or checking of material properties, or it may
do nothing. In mode 1, the function is called only once per zone per STEP
command.
2 Mode 2 corresponds to the main task of the function, as described in Sec-
tion 2.8.4 (i.e., new stresses should be computed from old stresses and strain
increments). In this mode, the function is called up to 4 times per zone (once
for each subzone).
3 The UDM must return values for the scalars cm max and sm max: these should
be set to the maximum confined modulus and the shear modulus, respectively,
as estimated for this zone. The value of cm max is used by FLAC to compute
a stable timestep; it is essential that a value is returned for this variable. For
an elastic model, this variable is given by K + (4G/3). Both variables are
used by the absorbing boundary logic in dynamic mode (estimates of tangent
moduli should be provided). The UDM is called once per zone for mode 3; it
is called at the beginning of stepping and every ten steps in large-strain mode.
It may also be called more often if FLAC determines that it needs to recompute
the internal timestep.
4 A set of 4 thermally induced stresses must be computed from a given set of 4
fictitious strains. This calculation is only required if thermal calculations are
being done. Normally, the action consists of multiplying each strain by the
current tangent bulk modulus, to give stresses. The UDM is called once per
zone for mode 4.
Note that mode 3 is called before mode 1 for constitutive models; also, zone stresses are undefined
in mode 1, mode 3 and mode 4.
As an example of a simple constitutive model written in FISH, the function in Example 2.29 performs
an operation identical to the built-in isotropic elastic model of FLAC.
The “properties” m e1, m e2 and m g2 are evaluated in the initialization section, to save time
during stepping. We may store the preceding FISH code in a file (e.g., “ELAS.FIS”) and call it
from a FLAC data file, as shown in Example 2.30:
The results should be identical to those using FLAC ’s built-in elastic law. However, the execution
time will be slower, as explained in Section 2.8.1. The speed may be increased considerably by
using the command OPT m elas (see Section 2.9).
Some “property” variables (declared with the f prop statement) may be used to record or accumulate
state variables for the constitutive model. For example, plastic strain may be accumulated for use in
a strain-hardening law. However, properties are stored for the whole quadrilateral zone, whereas the
user-written function is called for each triangular subzone (either 2 or 4, depending on geometry).
The computed “property” (e.g., plastic strain) should be the average for the 2 or 4 subzones.
Parameter zsub (which is of type float) allows the function to know when to accumulate values and
when to store them, according to the following prescription.
zsub = 0 The function should accumulate the desired quantity in some unique
global variable. Since the function is called sequentially for all
subzones in the quadrilateral, there is no danger that the global
variable will conflict with its use by other zones and other models.
However, the global variable must not be overwritten by other (non-
constitutive) functions.
zsub > 0 The function should now divide the accumulated quantity by zsub
and store it in the appropriate property location. Since zsub will
have the value 2.0 or 4.0, the stored property will be the average
quantity. The variable used for accumulation must now be set to
zero. This is very important; otherwise, the next zone calculation
will be in error, since its accumulator will not start from zero.
The elastic model given as an example in Section 2.8.5 may be modified to illustrate the use
of parameter zsub to save average state variables. For example, suppose we want to save the
accumulated volume strain for each zone. We introduce a new property name m dvol, and a new
global variable m vol, which should not be used elsewhere. The following modified part of the
FISH code in Example 2.31 will store the average volumetric strain in the property variable m dvol.
After running FLAC, the correctness may be verified by printing out both the newly defined volume
strain and FLAC ’s built-in volume strain measure:
print m dvol vsi
The FRIEND statement may be used in a user-defined model (UDM) to allow other functions (without
the CONSTITUTIVE MODEL declaration) to have access to the local zone variables of the calling
function. The format is
friend func1 func2 ...
where func1 and func2 are names of FISH functions. Within such functions (when called from the
UDM), the intrinsic zone variables (e.g., zde11, mode, etc.) and property names are treated exactly
as they are in the calling UDM. The FRIEND declaration is only necessary if zone properties and
variables are to be accessed by the associated function. Three restrictions apply:
a) Each function may only be declared as the friend of one UDM; otherwise,
there would be a conflict in zone variable names. However, each UDM may
declare any number of friends.
b) The function referred to by the FRIEND statement must be defined after the orig-
inating UDM; otherwise, the compiler cannot know that the property names
(used in the FRIEND function) are not just global variables.
c) If a FRIEND function is optimized, the associated UDM function must also be
optimized.
Example 2.32 demonstrates and validates the FRIEND logic:
pr xdisp
; ---> Note that results should be symmetric, but of opposite sign.
Section 3 contains FISH versions of a number of FLAC ’s built-in models; the FISH programs are
also available on file to FLAC users. It is useful to consult these programs to get some ideas before
writing a new constitutive model. The following suggestions may also be useful.
1. After defining a constitutive function, type PRINT fish to make sure that
(a) local properties do not appear in the list; and
(b) misspelled state variables (e.g., zed22) do not appear.
2. If a constitutive program uses many global variables, it may be worthwhile to
include a $ sign as the first character in their names, since the command PRINT
fish will not print these variables (and will cause confusion by listing many
names that may be irrelevant to the user). The “$” variables may be listed
separately with the command PRINT $fish.
3. If a user-defined property is given a value for mode 2 operation of a constitutive
function, then the conditions will correspond to the last-computed subzone
(usually D) unless steps are taken to store the average value (see Section 2.8.6).
4. If you have some user-written models that you use frequently, they can be
pre-compiled and stored in a save file; this will eliminate the time delay for
compilation of complicated models. The following file fragments illustrate
the technique.
call elas.fis ;compile required models
call mohr.fis
call my model.fis
save defmod.sav ;now save compiled versions
The compiled models can now be retrieved rapidly (from some other data file):
res defmod.sav
grid 20 20
model my model
.
.
The execution speed of user-written FISH functions can be increased in certain cases by using the
OPT command; a speed increase of four to eight times is typical. However, there are a number
of restrictions that limit the usefulness of the command, and may actually lead to errors if the
optimized code is used incorrectly. The optimizer was designed primarily to improve the speed of
user-written constitutive models; there is less advantage in using it for general functions.
In a normal FISH function, every reference to a variable necessitates a call to a “librarian” that
determines where the information is to be found. Since there are many types and variations of
variables (e.g., grid variables, intrinsic functions, local properties, etc.), the process of retrieval is
complicated and time-consuming. The optimizer attempts to go through this process beforehand
and store a machine address for each variable (or an index that gives rapid access to a machine
address). It also determines the types (integer, float or string) of variables and propagates them
through the arithmetic expressions in order to determine the types of the results in advance; this
eliminates another step at runtime.
The result of invoking the optimizer on a function is to create a new list of instructions (the “o-
code”), which consists of basic operations (arithmetic and jumps) using machine addresses. Any
references or operations that cannot be translated into simple instructions cause a temporary jump
from the o-code to the regular FISH code (the “p-code”) when the function is executed. The printout
“% optimized” indicates the proportion of instructions that were translated into o-code. Some
operations cannot be translated, in which case the optimization of the whole function fails, and an
error message is displayed.
The use of optimized and non-optimized functions during execution is automatic and transparent
to the user; if an optimized version of a function exists, then it is used instead of the non-optimized
version.
2.9.3 Restrictions
The following rules must be observed when writing a function that can be successfully optimized.
1. The function may call other functions, but such functions must have already
been defined prior to optimization. The called functions may or may not have
been optimized themselves.
2. COMMAND sections may not be executed while an optimized function is active.
For example, an error will occur if an optimized function calls a non-optimized
function that contains a COMMAND section. An optimized function may not
contain the COMMAND statement.
3. No mixed-mode arithmetic is allowed (i.e., all variables (and functions) in
an arithmetic expression must be of the same type (all integer or all floating-
point)). Furthermore, the destination type must be the same as the source type
in an assignment statement. If type conversion is required, then the functions
int( ), float( ) and string( ) should be used. The specification statements INT,
FLOAT and STRING should be used at the beginning of the function to predefine
the types of all variables that will be used. The exponentiation operation is
exempted from the mixed-mode restriction. For example, 3.4ˆ2 is allowed.
4. The following scalar variables (see Section 2.5.2) will not work in optimized
code: clock, imem, fmem, urand and grand. All other variables and functions
work correctly.
2.9.4 Suggestions
The following suggestions are designed to improve the speed of an optimized function, but it is not
necessary to follow them in order to achieve a working program.
1. The use of property variables within a constitutive model function should be
minimized; if possible, work with regular FISH variables, which are accessed
more rapidly.
2. The following variables or functions involve extra computational overheads:
tables, all grid-indexed variables (e.g., sxx(i,j), ex 3(i,j),
shear mod(i,j), etc.) and intrinsic functions (e.g., sin(x), sqrt(x)).
Also included in the list are user-defined constitutive properties used outside
of the constitutive model in which they are defined.
3. If possible, replace exponentiation (ˆ) with repeated multiplications (e.g.,
xdifˆ2 should be replaced with xdif*xdif).
2.9.5 Warning
Variable types (integer, float or string) are built into the optimized code at the time of optimization.
If the variable types subsequently change, then the results of arithmetic operations will be wrong.
For example, integer arithmetic might be used to multiply two floating-point numbers. It is the
responsibility of the user to ensure that variable types do not change.