OpenSees Cloud
OpenSees AMI
It's Not Load Control
Original Post - 07 Feb 2021 - Michael H. Scott
Visit Structural Analysis Is Simple on Substack.
The static integrators in OpenSees, including displacement control, arc length, and minimum unbalanced displacement norm (MUDN), are based on an incremental-iterative framework. After an initial load increment, each integrator imposes a constraint on the change in load factor at subsequent equilibrium iterations within a pseudo-time step.
Displacement control calculates the change in load factor necessary to keep the displacement at a specified DOF constant during iteration. Arc length and MUDN impose other constraints to determine the change in load factor. Load control also fits within this framework, but as a trivial case where the constraint is that the change in load factor is zero.
Although the documentation and many examples would lead you to believe load control controls the load factor, \(\lambda\), you are actually controlling the pseudo-time, t. Here’s the source code to prove it.
You see variables currentLambda
and deltaLambda
sprinkled throughout the
LoadControl class. But look at lines 127 through 130. These statements
are getting, updating, and applying time in the AnalysisModel.
Sure, the time and load factor are the same for the common case of a linear time series where \(\lambda(t)=t\). But, really, load control should be called pseudo-time control.
The point of all this is that imposed displacements via load control is often confused with the displacement control integrator. I mean, what’s clear about the fact that you have to define loads to use displacement control and you have to use load control to impose displacements?
Displacement control is best used when you have a spatial distribution of reference loads that you want to keep in proportion during a pushover analysis, i.e., you want to maintain a vertical distribution of lateral loads. You can control only one degree of freedom (DOF) with displacement control.
ops.timeSeries('Linear',2)
ops.pattern('Plain',2,2)
ops.load(3,1.0,0.0,0.0)
ops.load(2,0.5,0.0,0.0)
dU = 0.2
ops.integrator('DisplacementControl',3,1,dU)
To impose displacements at more than one DOF, you need to use sp constraints inside a load pattern. Imposing displacements at multiple locations is not very common in 2D analysis–you’re more likely to see it when imposing bidirectional displacements to 3D models–but it’s shown here anyway for the sake of demonstration.
The 2.1336 ratio of imposed displacements is based on linear response of the frame for the load pattern used in displacement control.
ops.timeSeries('Linear',2)
dU = 0.2
ops.pattern('Plain',2,2)
ops.sp(3,1,dU)
ops.sp(2,1,dU/2.1336) # Based on linear analysis
ops.integrator('LoadControl',1.0) # Using dt=1.0
Like reference loads, there is no unique way to define imposed reference
displacements. So, we could have used 1.0 and 1.0/2.1336 for the sp
command along with dU
as the time step in the load control integrator.
As expected, the results are quite different because the imposed displacements do not remain proportional to the displacement control load pattern after yield.
These two approaches give the same result if you control only one DOF.