OpenSees Cloud
OpenSees AMI
Parameter Updates in the Loop
Original Post - 15 Aug 2021 - Michael H. Scott
Visit Structural Analysis Is Simple on Substack.
Besides visualization and writing output to files, there’s some pretty useful things you can do during an OpenSees analysis. One of those things is updating model parameters.
Before getting into parameter updating, it is worth showing that OpenSees analyses can be run one step at a time. Many examples online show a dynamic analysis, e.g., with 4000 time steps at 0.01 sec, as one analyze command.
Nsteps = 4000
dt = 0.01
ops.analyze(Nsteps, dt)
This approach is perfectly fine, but you can’t do anything during those 4000 steps. So, to get the most out of your analyses and to do stuff as the analysis proceeds, you can equivalently analyze one step at a time, inside a loop.
for i in range(Nsteps):
ops.analyze(1, dt)
With this as our starting point, let’s look at parameter updating inside the loop.
Consider a two DOF rigid shear frame model. I like to use simple models so that we don’t get mired in the details of the Concrete23 constitutive model and/or the cadillacBeamColumn element formulation.
Let’s suppose, for whatever reason, that the stiffness of the first story reduces linearly to 80% of its initial value during an earthquake of duration T. Not realistic, but it’s a simple demonstration.
\[{\displaystyle k(t) = k_o\left(1-0.2\frac{t}{T}\right)}\]After defining the model, we use the parameter command to identify the stiffness of the first story as a parameter. Each parameter has a tag, which we use to update the parameter inside the analysis loop.
m = 2
k = 100
g = 386.4
import openseespy.opensees as ops
ops.wipe()
ops.model('basic','-ndm',1,'-ndf',1)
ops.node(1,0); ops.fix(1,1)
ops.node(2,0); ops.mass(2,m)
ops.node(3,0); ops.mass(3,m/2)
ops.uniaxialMaterial('Elastic',1,k)
ops.element('zeroLength',1,1,2,'-mat',1,'-dir',1)
ops.element('zeroLength',2,2,3,'-mat',1,'-dir',1)
ops.parameter(1,'element',1,'E')
ops.timeSeries('Path',1,'-dt',0.02,'-filePath','tabasFN.txt','-factor',g)
ops.pattern('UniformExcitation',1,1,'-accel',1)
ops.analysis('Transient')
T = 50
dt = 0.01
Nsteps = int(T/dt)
for i in range(Nsteps):
ops.analyze(1,dt)
ops.reactions()
t = ops.getTime()
ops.updateParameter(1,k*(1-0.2*t/T))
Note that each element makes a copy of its material(s). So, even though we’ve defined one elastic uniaxial material, the parameter will be associated with only the copy contained in element 1, i.e., the first story.
Also note that we are passing 'E'
to the parameter, not 'k'
. The
parameter string is based on what the model object, in this case the
elastic uniaxial material, understands–not the variable name you used
in your script.
The internal methods to identify and update parameters are similar to the methods for creating recorders. So, to see what parameters are valid for your element and material models, search for setParameter in the OpenSees source code just like you would take a look at setResponse to figure out what can be recorded from your elements and materials.
From the analysis, we can see the difference in roof displacement and base shear response history, with and without parameter updates.
Again, this was a very simple example–neither realistic nor meant to imply important engineering results.
As you go out to larger time scales like years, you can examine how your analysis results change as parameters within your model change, e.g., due to corrosion or whatever.
Suppose that the stiffness of the first story reduces to 80% of its initial value after 40 years. We want to know how an earthquake ground motion would affect the structure in each year of this degrading process.
This is a very simple change in parameter updates–in a loop over years, reset the model and update the parameter, then re-run the analysis. This example uses the same ground motion and intensity over all years, but it’s an easy modification to change the intensity or the ground motion.
#
# Using model defined above
#
Nyears = 40
for year in range(Nyears+1):
ops.reset()
ops.updateParameter(1,k*(1-0.2*year/Nyears))
Umax = 0
Vmax = 0
for i in range(Nsteps):
ops.analyze(1,dt)
ops.reactions()
U = ops.nodeDisp(3,1)
if abs(U) > Umax:
Umax = abs(U)
V = ops.nodeReaction(1,1)
if abs(V) > Vmax:
Vmax = abs(V)
I don’t like envelope recorders, so I get the maximum responses manually in the inner analysis loop. The maximum roof displacement and base shear over 40 years are shown below.
Even though it’s a linear-elastic model, there is not a direct relationship between the decrease in story stiffness and the peak response quantities. While this outcome was expected, very little additional engineering insight should be inferred from this example.