Tables 7-6 through 7-13 of the AISC Steel Manual contain values for C, the effective number of bolts that resist shear in eccentrically loaded bolt groups.
For example, in a bolt group with three vertical rows of 4 bolts spaced s=3 inch with srow=3 inch row spacing and a load at =30 degrees from vertical at a horizontal eccentricity of eX=12 inch from the bolt group centroid, the coefficient is C=3.82, i.e., the available strength of this bolt group is 3.82 times (not 12 times) the available strength of one bolt.
The tabulated values of C are based on the Instantaneous Center of Rotation (IC) Method. The Steel Manual also describes the Elastic Method to compute C, but that approach can be too conservative. The IC Method assumes all bolts in the group rotate about an instantaneous center of rotation under the action of the eccentric load.
OpenSees can handle the IC method pretty easily using zero length elements and a rigid diaphragm constraint; however, we have to define the model in 3D to accommodate the instantaneous center of rotation.
import openseespy.opensees as ops import math ops.wipe() ops.model('basic','-ndm',3,'-ndf',6)
In addition to the kinematic assumption, the IC Method assumes a nonlinear force-deformation relationship for each bolt, , where is the bolt ultimate strength, which we can assume is 1 kip in order to compute the C coefficients. The maximum bolt deformation is assumed to be 0.34 inch.
OpenSees does not have a uniaxialMaterial or hystereticBackbone model that fits this function, but it’s easy enough the create the force-deformation relationship as a MultiLinear material.
Rult = 1.0 # Bolt strength dmax = 0.34 # Maximum bolt deformation N = 10 # Number of points uf =  # Array of deformation and force values for i in range(1,N+1): d = i*dmax/N uf.append(d) R = Rult*(1-math.exp(-10*d))**0.55 uf.append(R) ops.uniaxialMaterial('MultiLinear',1,*uf)
Next, the force-deformation response needs to be applied in arbitrary directions for each bolt. We cannot use a regular zeroLength element because its materials act in orthogonal directions, uncoupled. Instead, we can use a CoupledZeroLength element which applies a uniaxial force-deformation relationship based on the strain magnitude and direction, i.e., “along the hypotenuse”.
For each bolt, we create two collocated nodes–one completely fixed (tag < 0) and the other (tag > 0) with out of plane DOFs constrained–then we define a CoupledZeroLength element. Finally, we define an additional node (tag = 0) at the point of loading (with out of plane DOFs constrained) and enforce a rigid diaphragm constraint with the loaded node as primary and the bolt nodes (tag > 0) as secondary.
s = 3 # bolt spacing ex = 12 # load eccentricity n = 4 # bolts per row Nrows = 3 # number of rows srow = 3 # row spacing theta = 30*math.pi/180 # load direction ndTags =  for r in range(Nrows): for i in range(n): ndTag = r*n + i + 1 ndTags.append(ndTag) ops.node(-ndTag,-r*srow,i*s,0) ops.fix(-ndTag,1,1,1,1,1,1) ops.node(ndTag,-r*srow,i*s,0) ops.fix(ndTag,0,0,1,1,1,0) ops.element('CoupledZeroLength',ndTag,-ndTag,ndTag,1,2,1) ops.node(0,ex-srow*(Nrows-1)/2,s*(n-1)/2,0) ops.fix(0,0,0,1,1,1,0) ops.rigidDiaphragm(3,0,*ndTags)
This gives the 3D model shown below, plotted with
We then apply a load at the primary node and perform nonlinear static analysis until the deformation of any bolt reaches the 0.34 inch limit prescribed in the IC method.
ops.timeSeries('Linear',1) ops.pattern('Plain',1,1) ops.load(0,-math.sin(theta),-math.cos(theta),0,0,0,0) ops.test('NormDispIncr',1e-6,10,0) ops.algorithm('KrylovNewton') ops.system('UmfPack') ops.constraints('Transformation') Pmax = Rult*Nrows*n # Maximum possible load dP = 0.01 Nsteps = int(Pmax/dP) ops.integrator('LoadControl',dP) ops.analysis('Static') for i in range(Nsteps): ok = ops.analyze(1) if ok < 0: break stopAnalysis = False for j in ops.getEleTags(): eps = ops.eleResponse(j,'material','strain') if abs(eps) >= dmax: stopAnalysis = True break if stopAnalysis: break C = ops.getLoadFactor(1)/Rult
I resorted to the KrylovNewton algorithm because it seems the CoupledZeroLength element does not return a consistent tangent, which led to convergence problems when I ran the analysis with the default Newton-Raphson algorithm.
Anyway, the coefficient computed by the OpenSees analysis is C=3.82, right on with the value tabulated in Table 7-10 of the Steel Manual!
The displaced shape of the model (magnification=5) at the end of the analysis is shown below, where we can see the bolt group has moved about an instantaneous center of rotation located somewhere around (X,Y)=(-4,6).
It would be a snap to put this code in nested loops to generate all of Tables 7-6 through 7-13 and to explore specific cases not shown in the tables.
And add this to the list of things you didn’t know could be done with OpenSees.