OpenSees Cloud
OpenSees AMI
Is OpenSees Pythonic?
Original Post - 16 May 2024 - Michael H. Scott
Visit Structural Analysis Is Simple on Substack.
OpenSeesPy is sometimes criticized for not being “Pythonic”. But what does “Pythonic” even mean? And does achieving “Pythonic” status matter?
From what I gathered in this blog post, Pythonic code uses the idioms, i.e., the specific syntax and constructs, of Python. That definition is circular–an example from the post better demonstrates the meaning.
If you are familiar with C++, your first try at doing something with the elements of a Python list might look like a C/C++ for loop rewritten as a while loop:
i = 0
while i < n:
doSomething(xlist[i])
i += 1
No idioms, just straight up iteration and indexing.
A better attempt at list processing would use Python’s range
idiom for
iteration:
for i in range(n):
doSomething(xlist[i])
But the fully Pythonic approach takes advantage of Python’s list traversal idiom that handles both iteration and indexing:
for x in xlist:
doSomething(x)
Along the same lines, a familiar OpenSees construct is iterating over the tags of the elements in your model. The un-Pythonic way to do something with each element tag is:
elelist = ops.getEleTags()
Nele = len(elelist)
for i in range(Nele):
doSomething(elelist[i])
While the Pythonic approach is:
for ele in ops.getEleTags():
doSomething(ele)
For what it’s worth, since long before OpenSeesPy was a twinkle in
anyone’s eye, the getEleTags
function has been available in OpenSees Tcl
and you could do the following:
foreach ele [getEleTags] {
doSomething $ele
}
So, one could argue OpenSees has always been “Pythonic”, or “Tcl-rific”.
But anyway, by using idioms that Python supports, your OpenSeesPy script should run more quickly than if you did not use the idioms. But when you’re only using Python as a front end or API to nonlinear solution algorithms, element formulations, and constitutive models, how fast the idioms–or non-idioms–of your script execute should be the least of your worries. So, meh.
The legitimate reason people label OpenSees “un-Pythonic” is the lack of
keyword arguments, or kwargs
, as inputs.
For example, the Pythonic approach to defining a Steel01
material would
accept keywords for every input, and the inputs can be in any order:
ops.uniaxialMaterial('Steel01',E=29000,Fy=60,b=0.01,tag=8)
Instead, OpenSeesPy commands rely on positional arguments where you must supply a specific order of inputs with no keywords:
ops.uniaxialMaterial('Steel01',8,60,29000,0,01)
The decision to use positional arguments for OpenSeesPy made the syntax as similar as possible to the Tcl version of OpenSees. Still 100% the right decision.
To accommodate kwargs
, you can define a layer on top of OpenSeesPy and
also keep track of OpenSees model parameters in Python objects. This
extra layer is the basis for O3seespy
,
but it takes third party effort
to keep this package in sync with the main OpenSeesPy.
Regardless of its source, the bigger issue is what does the label “un-Pythonic” imply about OpenSees? Nothing, really.
As stated in this blog post, the “un-Pythonic” label may cause one to overlook the core functionality and power of frameworks like OpenSees.
… the condemnation of software as ‘unpythonic’ may be somewhat unfair and may obscure other positive aspects of the software. A less powerful framework that is easy to pick up for a Python programmer may be considered more Pythonic than a far more powerful system that takes more of a time investment to learn.
Sure, one could attempt to out-Python OpenSees by writing a Pythonic nonlinear FEA package. But the code would be bloated, the performance would be terrible, and it would take years of toil and trouble to match OpenSees functionality. Do you want to rewrite Concrete23 in pure Python? I didn’t think so.
With OpenSees, and other frameworks that get the job done, it’s about what you can do, not how you can do it. The syntax and idioms on the front end, and whether or not the framework is Pythonic, doesn’t really matter.