OpenSees Cloud
OpenSees AMI
How to Use Block2D
Original Post - 09 Jan 2023 - Michael H. Scott
Visit Structural Analysis Is Simple on Substack.
Although the mesh commands outlined in previous posts
(here
and here)
are more
powerful, the block2D
and block3D
commands remain useful, especially if you want to
use quadrilateral or brick elements.
Ed C++ Love based the OpenSees block commands on the meshing functionality in FEAP.
So, if you are familiar with FEAP, these commands should look familiar. This post will
focus on block2D
.
The basic idea is to define corner points for the block, then the command creates nodes and elements from the user-specified number of elements in the block. The command takes element specific arguments that are passed into each element the block creates. For example, to create a 10x6 mesh of quad elements with node and element tags each starting at 1, use the following code.
import openseespy.opensees as ops
L = 20
b = 10
t = 1.0
E = 29000.0
v = 0.3
ops.wipe()
ops.model('basic','-ndm',2,'-ndf',2)
ops.nDMaterial('ElasticIsotropic',1,E,v)
eleArgs = [t,'PlaneStress',1]
points = [1,0,0,
2,L,0,
3,L,b,
4,0,b]
ops.block2D(10,6,1,1,'quad',*eleArgs,*points)
This produces the model shown below (plotted using
opsvis
) with 77 nodes and 60 elements.
You can also define curved meshes by supplying edge points 5-8 and/or point 9 in the
block2D
command. The
wiki page
has a good explanation of points 5 through 9.
# Points 5 and 6 to curve edge 1-2 and 2-3, respectively
points = [1,0,0,
2,L,0,
3,L,b,
4,0,b,
5,L/2,-2,
6,L+4,b-4]
ops.block2D(10,6,1,1,'quad',*eleArgs,*points)
# Point 9 to curve the interior mesh
points = [1,0,0,
2,L,0,
3,L,b,
4,0,b,
9,L/2-2,b-6]
ops.block2D(10,6,1,1,'quad',*eleArgs,*points)
The above meshes show general features, but usually you will define regular meshes in
layers using multiple calls to the block2D
command. In this case, you need to ensure
you pass starting node and element tags that will not cause a conflict. Instead of a
magic number,
pass in one more than the largest tag already defined.
points = [1,0,0,
2,L,0,
3,L,b/2,
4,0,b/2]
ops.block2D(10,2,1,1,'quad',*eleArgs,*points)
eleStart = max(ops.getEleTags()) + 1
nodeStart = max(ops.getNodeTags()) + 1
points = [1,0,b/2,
2,L,b/2,
3,L,b,
4,0,b]
ops.block2D(10,2,nodeStart,eleStart,'quad',*eleArgs,*points)
# fix along X=0
ops.fixX(0,1,1)
Notice in the above mesh there are collocated nodes along the mesh boundary. Without further model adjustments, applying a load, e.g., at the free end, leads to an incompatible solution.
The nodes along the block interface must be joined. Unlike FEAP, there is no tie
command in OpenSees–you have to do it yourself.
Below is generic Python code to perform the tie operation with a tolerance on the internodal distance. The first if-statement in the inner loop ensures we don’t tie a node to itself and that we don’t tie nodes to each other twice.
import numpy as np
for ndI in ops.getNodeTags():
XYZI = np.array(ops.nodeCoord(ndI))
for ndJ in ops.getNodeTags():
if ndI <= ndJ:
continue
XYZJ = np.array(ops.nodeCoord(ndJ))
if np.linalg.norm(XYZJ-XYZI) < 1e-8:
ops.equalDOF(ndI,ndJ,1,2)
Note that this code will work for any model, but adjust the equalDOF
command accordingly.
For this model, the resulting solution with tied blocks is shown below. Much better!
I’ll do a separate post on block3D
.