Update September 13, 2021: The issue described in this post has been resolved as of OpenSeesPy version 3.3.0.1.
Python is one of the best things to happen with OpenSees. Unfortunately, the break from Tcl has not been squeaky clean.
A very sticky transition point has been element recorders. When we wrote the internal setResponse
functions to identify which element, section, material, or fiber response to record, we put C-style int argc, char **argv
arguments in the method signature. So, setResponse
sees everything as a string. This is not entirely Tcl’s fault, but the “everything is a string” mantra definitely affected the implementation decision.
To demonstrate, let’s suppose you wanted to record the stress response history of the fiber closest to coordinates (10,5) on section 3 of element 1. In Tcl, you’d write something like this:
set y 10.0
set z 5.0
recorder Element -ele 1 section 3 fiber $y $z stress
For brevity, I omitted the -file
and -time
options.
After variable substitution in Tcl, element 1 sees an array of strings in setResponse
:
argv[0] = "section"
argv[1] = "3"
argv[2] = "fiber"
argv[3] = "10.0"
argv[4] = "5.0"
argv[5] = "stress"
Then, the element uses atoi(argv[1])
to get the section number and passes the remaining information on to setResponse
of the requested section. The section calls atof
on the coordinates then searches for the fiber closest to those coordinates to figure out which material object to call next.
With Python, you’d be inclined to write the recorder command like this:
y = 10.0
z = 5.0
ops.recorder('Element', '-ele', 1, 'section', 3, 'fiber', y, z, 'stress')
After variable substitution in Python, element 1 will receive the following argv
assignments in setResponse
:
argv[0] = "section"
argv[1] = 3
argv[2] = "fiber"
argv[3] = 10.0
argv[4] = 5.0
argv[5] = "stress"
Looks reasonable. But look closely and you’ll see we’re dumping integers and floats (with no quotes) into character strings. In fact, if you print to screen (using opserr <<
) the argv
values that are passed to the element setResponse
, you’ll see Invalid String Input!
for argv[1]
, argv[3]
, and argv[4]
.
To make matters worse, neither atoi
nor atof
throws an exception, i.e., there is no error when the element calls atoi(argv[1])
. As a result, your recorder command will appear to work, but then not record anything–you’ll get an empty output file.
Fixing this issue will not be easy. Hard coding on the front end what should or should not be a string is not a good idea. Neither is trying to code a workaround in all the setResponse
methods.
What you can do in the meantime is cast the arguments using Python’s str
function, forcing the OpenSees interpreter to generate strings for everything it passes to setResponse
:
y = 10.0
z = 5.0
ops.recorder('Element', '-ele', 1, 'section', str(3), 'fiber', str(y), str(z), 'stress')
You need to string cast only the arguments that drill down beyond the element setResponse
, not the element tags (notice str(1)
is not used for the element tag after '-ele'
above). Basically, any numeric argument that comes after 'section'
, 'material'
, or 'fiber'
in an element recorder needs to be cast as a string. Node recorders are not affected.