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.