OpenSees Cloud
OpenSees AMI
Recorders not Recording?
Original Post - 14 Aug 2020 - Michael H. Scott
Visit Structural Analysis Is Simple on Substack.
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.