The patch test is one of the standard “sanity checks” of finite element implementations. To pass a patch test, an irregular mesh of elements must be able to reproduce a constant stress field. Interior nodes of the mesh should also displace according to a linear displacement field.
OpenSees is not known so much for its solid finite elements, but many such elements are available in the framework. So, let’s see if the four node quad can pass the patch test.
Below is an irregular mesh of four plane stress quad elements with unit thickness. Simple boundary conditions are applied on one side while nodal loads are applied on the other side, putting the model in tension.
The OpenSees code to create this model is shown below.
ops.wipe() ops.model('basic','-ndm',2,'-ndf',2) ops.node(1,0,0); ops.fix(1,1,1) ops.node(2,h,0) ops.node(3,2*h,0) ops.node(4,0,h); ops.fix(4,1,0) ops.node(5,2*h,h) ops.node(6,0,2*h); ops.fix(6,1,0) ops.node(7,h,2*h) ops.node(8,2*h,2*h) # Interior node X = 0.9*h; Y = 1.2*h ops.node(9,X,Y) ops.nDMaterial('ElasticIsotropic',1,E,v) stressType = 'PlaneStress' ops.element('quad',1,1,2,9,4,1.0,stressType,1) ops.element('quad',2,2,3,5,9,1.0,stressType,1) ops.element('quad',3,4,9,7,6,1.0,stressType,1) ops.element('quad',4,9,5,8,7,1.0,stressType,1) ops.timeSeries('Constant',1) ops.pattern('Plain',1,1) ops.load(3,0.25*F,0) ops.load(5,0.5*F,0) ops.load(8,0.25*F,0) ops.analysis('Static') ops.analyze(1) # Check displacement print(ops.nodeDisp(9,1)) u = ops.nodeDisp(5,1) print(X/(2*h)*u) # Check stress for ele in ops.getEleTags(): print(ops.eleResponse(ele,'stress'))
Static analysis shows the normal stress is equal at each Gauss point in each element. Furthermore, the horizontal displacement of the interior node is consistent with a linear interpolation of displacement.
This patch test can and should be repeated for other stress states, and there are higher order patch tests for elements with higher order shape functions.
OpenSees, however, is known more for its frame finite elements, which also should pass a patch test. But what does a patch test mean for frame elements? Yes, we can do pure tension, but pure bending would be more interesting.
For example, a cantilever with a concentrated moment at its free end will have constant internal bending moment and a linear rotation field along its length.
To perform this beam patch test, we use an irregular mesh of two displacement-based beam-column elements.
Below is the OpenSees code for this model.
ops.wipe() ops.model('basic','-ndm',2,'-ndf',3) ops.node(1,0,0); ops.fix(1,1,1,1) ops.node(3,2*h,0) # Interior node X = 0.9*h ops.node(2,X,0) ops.geomTransf('Linear',1) ops.section('Elastic',1,E,A,I) Np = 2 ops.beamIntegration('Legendre',1,1,Np) ops.element('dispBeamColumn',1,1,2,1,1) ops.element('dispBeamColumn',2,2,3,1,1) ops.timeSeries('Constant',1) ops.pattern('Plain',1,1) ops.load(3,0,0,M) ops.analysis('Static') ops.analyze(1) # Check rotation print(ops.nodeDisp(2,3)) u = ops.nodeDisp(3,3) print(X/(2*h)*u) # Check bending moment for ele in ops.getEleTags(): for ip in range(Np): print(ops.eleResponse(ele,'section',ip+1,'force'))
After the analysis, the bending moment at each Gauss point is equal to the applied moment, as expected. In addition, the rotation at the interior node matches linear interpolation of the rotation at the free end of the cantilever. We should also do a patch test for the transverse displacement field.
I intentionally did not show output in this post. You can take my word for it that the elements pass the patch test. But really, I want you to try the patch tests yourself and make sense of the results.