def test_dofset_reset(self): dof = DofSet() self.assertTrue(dof.count() == 0) dof.reset(True) self.assertTrue(dof.count() == 6) dof.reset() self.assertTrue(dof.count() == 0)
def __init__(self, n1, n2, material=None, properties=None): BaseBeam.__init__(self) self.nodeCount = 2 self.name = 'Structural 3D Truss' self.dofSet = DofSet(Dx=True, Dy=True, Dz=True) self.nodes = (n1, n2) self.material = material self.loads = List() if properties is None: properties = Properties() self.properties = properties
def test_dofset_inplace(self): dof1 = DofSet(Dx=True,Dy=True,Dz=True) dof2 = DofSet(Dx=True,Dy=True,Dz=True,Rx=True,Ry=True,Rz=True) dof1 &= dof2 self.assertTrue(dof1.count() == 3) self.assertTrue(dof1.is3DSolid()) self.assertTrue(not dof1.is3DShell()) self.assertTrue(dof1.elementDimension() == 3) dof1 = DofSet(Dx=True,Dy=True,Dz=True) dof2 = DofSet(Dx=True,Dy=True,Dz=True,Rx=True,Ry=True,Rz=True) dof1 |= dof2 self.assertTrue(dof1.count() == 6) self.assertTrue(not dof1.is3DSolid()) self.assertTrue(dof1.is3DShell()) self.assertTrue(dof1.elementDimension() == 3)
def test_dofset_2d(self): dof1 = DofSet(Dx=True,Dy=True,Dz=False) self.assertTrue(dof1.count() == 2) self.assertTrue(not dof1.is3DSolid()) self.assertTrue(not dof1.is3DShell()) self.assertTrue(dof1.elementDimension() == 2) dof2 = DofSet(Dx=True,Dy=True,Dz=True,Rx=True,Ry=True,Rz=True) dofa = dof1 & dof2 self.assertTrue(dofa.count() == 2) self.assertTrue(not dofa.is3DSolid()) self.assertTrue(not dofa.is3DShell()) self.assertTrue(dofa.elementDimension() == 2) dofo = dof1 | dof2 self.assertTrue(dofo.count() == 6) self.assertTrue(not dofo.is3DSolid()) self.assertTrue(dofo.is3DShell()) self.assertTrue(dofo.elementDimension() == 3)
def __init__(self, n1, n2, material = None, properties = None): BaseBeam.__init__(self) self.nodeCount = 2 self.name = 'Structural 3D Truss' self.dofSet = DofSet(Dx=True, Dy=True, Dz=True) self.nodes = (n1,n2) self.material = material self.loads = List() if properties is None: properties = Properties() self.properties = properties
def test_dofset_2d(self): dof1 = DofSet(Dx=True, Dy=True, Dz=False) self.assertTrue(dof1.count() == 2) self.assertTrue(not dof1.is3DSolid()) self.assertTrue(not dof1.is3DShell()) self.assertTrue(dof1.elementDimension() == 2) dof2 = DofSet(Dx=True, Dy=True, Dz=True, Rx=True, Ry=True, Rz=True) dofa = dof1 & dof2 self.assertTrue(dofa.count() == 2) self.assertTrue(not dofa.is3DSolid()) self.assertTrue(not dofa.is3DShell()) self.assertTrue(dofa.elementDimension() == 2) dofo = dof1 | dof2 self.assertTrue(dofo.count() == 6) self.assertTrue(not dofo.is3DSolid()) self.assertTrue(dofo.is3DShell()) self.assertTrue(dofo.elementDimension() == 3)
def test_dofset_dofPos(self): dof = DofSet(Dx=True,Dy=True,Dz=True) self.assertTrue(dof.dofPos(3) == 2) dof = DofSet(Dx=False,Dy=False,Dz=False,Rx=True,Ry=True,Rz=True) self.assertTrue(dof.dofPos(3) == 5)
def test_dofset_dofPos(self): dof = DofSet(Dx=True, Dy=True, Dz=True) self.assertTrue(dof.dofPos(3) == 2) dof = DofSet(Dx=False, Dy=False, Dz=False, Rx=True, Ry=True, Rz=True) self.assertTrue(dof.dofPos(3) == 5)
def test_dofset_inplace(self): dof1 = DofSet(Dx=True, Dy=True, Dz=True) dof2 = DofSet(Dx=True, Dy=True, Dz=True, Rx=True, Ry=True, Rz=True) dof1 &= dof2 self.assertTrue(dof1.count() == 3) self.assertTrue(dof1.is3DSolid()) self.assertTrue(not dof1.is3DShell()) self.assertTrue(dof1.elementDimension() == 3) dof1 = DofSet(Dx=True, Dy=True, Dz=True) dof2 = DofSet(Dx=True, Dy=True, Dz=True, Rx=True, Ry=True, Rz=True) dof1 |= dof2 self.assertTrue(dof1.count() == 6) self.assertTrue(not dof1.is3DSolid()) self.assertTrue(dof1.is3DShell()) self.assertTrue(dof1.elementDimension() == 3)
class Beam(BaseBeam): ''' 2-Node Structural 3D Beam ''' def __init__(self, n1, n2, material = None, properties = None): BaseBeam.__init__(self) self.nodeCount = 2 self.name = 'Structural 3D Beam' self.dofSet = DofSet() self.dofSet.reset(True) self.nodes = (n1,n2) self.material = material self.loads = List() if properties is None: properties = Properties() self.properties = properties def validate(self): ''' Validate beam ''' if self.length() < TINY: raise FEError('Beam length is to small') # check material for name in ('E', 'G', 'nu'): if name not in self.material: raise FEError('Material definition not complete') if self.material[name] < TINY: raise FEError("Material parameter '%s' not correct" % name) # check profile data for name in ('Area', 'Iyy', 'Izz'): if name not in self.properties: raise FEError('Properties definition not complete') if self.properties[name] < TINY: raise FEError("Properties parameter '%s' not correct" % name) def calcTe(self): ''' Eval element tranformation matrix ''' # Eval coordinate transformation matrix Tl = self.calcT() # Build the final transformation matrix - a 12x12 matrix T = np.zeros((12,12), dtype=float) for i in range(4): for j in range(3): for k in range(3): T[j+3*i,k+3*i] = Tl[j,k] return T def calcM(self, lumped = False): ''' Eval elemenent consistent mass matrix in global coordinates ''' mat = self.material prop = self.properties L = self.length() LL = L*L rho = mat["Density"] Ax, Iy, Iz = prop["Area"], prop["Iyy"], prop["Izz"] J = prop.get("Ixx", 0.) if J == 0.: # Simplification only valid for circular sections J = Iy + Iz M = np.zeros((12,12), dtype=float) t = rho*Ax*L ry = rho*Iy rz = rho*Iz po = rho*J*L if lumped: t *= .5 ry *= .5 rz *= .5 po *= .5 M[0][0] = M[1][1] = M[2][2] = M[6][6] = M[7][7] = M[8][8] = t M[3][3] = M[9][9] = po + ry + rz M[4][4] = M[10][10] = po + ry + rz M[5][5] = M[11][11] = po + ry + rz M[3][4] = M[4][3] = M[9][10] = M[10][9] = po + ry + rz M[3][5] = M[5][3] = M[9][11] = M[11][9] = po + ry + rz M[4][5] = M[5][4] = M[10][11] = M[11][10] = po + ry + rz else: M[0][0] = M[6][6] = t/3. M[1][1] = M[7][7] = 13.*t/35. + 6.*rz/(5.*L) M[2][2] = M[8][8] = 13.*t/35. + 6.*ry/(5.*L) M[3][3] = M[9][9] = po/3. M[4][4] = M[10][10] = t*LL/105. + 2.*L*ry/15. M[5][5] = M[11][11] = t*LL/105. + 2.*L*rz/15. M[4][2] = M[2][4] = -11.*t*L/210. - ry/10. M[5][1] = M[1][5] = 11.*t*L/210. + rz/10. M[6][0] = M[0][6] = t/6. M[7][5] = M[5][7] = 13.*t*L/420. - rz/10. M[8][4] = M[4][8] = -13.*t*L/420. + ry/10. M[9][3] = M[3][9] = po/6. M[10][2] = M[2][10] = 13.*t*L/420. - ry/10. M[11][1] = M[1][11] = -13.*t*L/420. + rz/10. M[10][8] = M[8][10] = 11.*t*L/210. + ry/10. M[11][7] = M[7][11] = -11.*t*L/210. - rz/10. M[7][1] = M[1][7] = 9.*t/70. - 6.*rz/(5.*L) M[8][2] = M[2][8] = 9.*t/70. - 6.*ry/(5.*L) M[10][4] = M[4][10] = -LL*t/140. - ry*L/30. M[11][5] = M[5][11] = -LL*t/140. - rz*L/30. T = self.calcTe() # Evaluate Transformation M = T^T * M * T return np.dot(T.T,np.dot(M,T)) def calcKe(self): ''' Eval element stiffness matrix in local coordinates. Includes transverse shear deformation. If no shear deflection constant (ShearZ and ShearY) is set, the displacement considers bending deformation only. Shear deflection constants for other cross-sections can be found in structural handbooks. ''' Ke = np.zeros((12,12), dtype=float) mat = self.material prop = self.properties L = self.length() L2 = L*L L3 = L2*L E, G = mat["E"], mat["G"] Ax, Iy, Iz = prop["Area"], prop["Iyy"], prop["Izz"] J = prop.get("Ixx", 0.) if J == 0.: # Simplification only valid for circular sections J = Iy + Iz Asy = prop.get("ShearY", 0.) Asz = prop.get("ShearZ", 0.) if Asy != 0. and Asz != 0.: Ksy = 12.*E*Iz / (G*Asy*L2) Ksz = 12.*E*Iy / (G*Asz*L2) else: Ksy = Ksz = 0. Ke[0,0] = Ke[6,6] = E*Ax / L Ke[1,1] = Ke[7,7] = 12.*E*Iz / ( L3*(1.+Ksy) ) Ke[2,2] = Ke[8,8] = 12.*E*Iy / ( L3*(1.+Ksz) ) Ke[3,3] = Ke[9,9] = G*J / L Ke[4,4] = Ke[10,10] = (4.+Ksz)*E*Iy / ( L*(1.+Ksz) ) Ke[5,5] = Ke[11,11] = (4.+Ksy)*E*Iz / ( L*(1.+Ksy) ) Ke[4,2] = Ke[2,4] = -6.*E*Iy / ( L2*(1.+Ksz) ) Ke[5,1] = Ke[1,5] = 6.*E*Iz / ( L2*(1.+Ksy) ) Ke[6,0] = Ke[0,6] = -Ke[0,0] Ke[11,7] = Ke[7,11] = Ke[7,5] = Ke[5,7] = -Ke[5,1] Ke[10,8] = Ke[8,10] = Ke[8,4] = Ke[4,8] = -Ke[4,2] Ke[9,3] = Ke[3,9] = -Ke[3,3] Ke[10,2] = Ke[2,10] = Ke[4,2] Ke[11,1] = Ke[1,11] = Ke[5,1] Ke[7,1] = Ke[1,7] = -Ke[1,1] Ke[8,2] = Ke[2,8] = -Ke[2,2] Ke[10,4] = Ke[4,10] = (2.-Ksz)*E*Iy / ( L*(1.+Ksz) ) Ke[11,5] = Ke[5,11] = (2.-Ksy)*E*Iz / ( L*(1.+Ksy) ) return Ke def calcK(self): ''' Eval element stiffness matrix in global coordinates. ''' K = self.calcKe() T = self.calcTe() # Evaluate Transformation K = T^T * K * T return np.dot(T.T,np.dot(K,T)) def calcStress(self): raise NotImplementedError() def calcNodalForces(self, res = None): ''' Calculate corresponding global forces at nodes from line loads ''' res = self.calcLocalNodalForces(res) Tl = self.calcT() # Build the final transformation matrix - a 12x12 matrix T = np.zeros((12,12), dtype=float) for i in range(4): for j in range(3): for k in range(3): T[j+3*i,k+3*i] = Tl[j,k] res[:] = np.dot(T.T, res) return res def calcLocalNodalForces(self, res = None): ''' Calculate corresponding end forces at nodes from line loads in local directions ''' if res is None: res = np.zeros((self.dofSet.count() * self.nodeCount,), dtype = float) L = self.length() LL = L * L load = self.calcLocalLoad() load *= L # Nx res[0] += .5*load[0] res[6] += .5*load[0] # Vy res[1] += .5*load[1] res[7] += .5*load[1] # VZ res[2] += .5*load[2] res[8] += .5*load[2] # My res[4] -= load[2] * L / 12. res[10] += load[2] * L / 12. # Mz res[5] -= load[1] * L / 12. res[11] += load[1] * L / 12. return res def calcSectionForces(self, dx = 1e9, sections=None): # find number of divisions try: nx = int(self.length()/dx) nx = min(max(1, nx), 100) except ZeroDivisionError: nx = 1 dx = self.length() if sections is not None: nx = sections - 1 dx = self.length() / (sections - 1) res = np.zeros((nx + 1, 7), dtype=float) L = self.length() # Eval local coordinate transformation matrix Tl = self.calcT() # Build the final transformation matrix - a 12x12 matrix T = np.zeros((12,12), dtype=float) for i in range(4): for j in range(3): for k in range(3): T[j+3*i,k+3*i] = Tl[j,k] # nodes flipped? n1, n2 = self.nodes if n1.cz > n2.cz: fac = -1. else: fac = 1. # Transform displacements and rotations from global to local coordinates d = np.zeros((12,), dtype=float) d[:6] = n1.results d[6:] = n2.results if not n1.coordSys is None: Tcs = n1.coordSys d[:3] = np.dot(Tcs.T, d[:3]) d[3:6] = np.dot(Tcs.T, d[3:6]) if not n2.coordSys is None: Tcs = n2.coordSys d[6:9] = np.dot(Tcs.T, d[6:9]) d[9:12] = np.dot(Tcs.T, d[9:12]) u = np.dot(T, d) # Stiffness matrix in local coordinates Ke = self.calcKe() # Calculate forces in local directions forces = np.dot(Ke, u) # Correct forces from eqivalent forces line loads iforces = self.calcLocalNodalForces() forces -= iforces res[0,0] = 0. res[0,1:] = forces[:6] res[nx,0] = 1. res[nx,1:] = -forces[6:] if nx > 1: # accumulate interior span loads load = self.calcLocalLoad() _dx = dx x = _dx for i in range(nx): res[i + 1,0] = x/L # Position res[i + 1,1] = res[i, 1] + load[0]*_dx # Axial force, Nx res[i + 1,2] = res[i, 2] + load[1]*_dx # Sheare force Vy res[i + 1,3] = res[i, 3] + load[2]*_dx # Sheare force Vz res[i + 1,4] = res[i, 4] # Torsion, Txx # correct last step if needed if (i + 1)*_dx > L: _dx = L - i*_dx x += _dx # trapezoidal integration of shear force for bending momemnt _dx = dx for i in range(nx): res[i + 1,5] = res[i,5] + .5*(res[i + 1, 3] + res[i, 3])*_dx # Myy res[i + 1,6] = res[i,6] + fac*.5*(res[i + 1, 2] + res[i, 2])*_dx # Mzz # correct last step if needed if (i + 1)*_dx > L: _dx = L - i*_dx return res
class Truss(BaseBeam): ''' 2-Node Structural 3D Truss ''' def __init__(self, n1, n2, material = None, properties = None): BaseBeam.__init__(self) self.nodeCount = 2 self.name = 'Structural 3D Truss' self.dofSet = DofSet(Dx=True, Dy=True, Dz=True) self.nodes = (n1,n2) self.material = material self.loads = List() if properties is None: properties = Properties() self.properties = properties def __str__(self): return '()' def __repr__(self): return '%s%s' % (self.__class__.__name__, str(self)) def validate(self): ''' Validate truss ''' if self.length() < TINY: raise FEError('Truss length is to small') # check material for name in ('E',): if name not in self.material: raise FEError('Material definition not complete') if self.material[name] < TINY: raise FEError("Material parameter '%s' not correct" % name) # check profile data for name in ('Area',): if name not in self.properties: raise FEError('Properties definition not complete') if self.properties[name] < TINY: raise FEError("Properties parameter '%s' not correct" % name) def calcLocalNodalForces(self, res = None): ''' Calculate corresponding end forces at nodes from line loads in local directions ''' if res is None: res = np.zeros((self.dofSet.count() * self.nodeCount,), dtype = float) load = self.calcLocalLoad() # Nx res[0] += .5*load[0] res[3] += .5*load[0] # Vy res[1] += .5*load[1] res[4] += .5*load[1] # VZ res[2] += .5*load[2] res[5] += .5*load[2] return res def calcNodalForces(self, res = None): ''' Calculate corresponding global forces at nodes from line loads ''' # calculate local forces res = self.calcLocalNodalForces(res) # Transforamtion matrix T = self.calcT() res[0:3] = np.dot(T.T, res[0:3]) res[3:6] = np.dot(T.T, res[3:6]) return res def calcM(self, lumped = False): ''' Eval elemenent consistent mass matrix in global coordinates ''' mat = self.material prop = self.properties Ax, rho = prop["Area"], mat['Density'] n1, n2 = self.nodes L = self.length() # Eval direction cosines in order to transform stiffness matrix # from local coords (link direction) to global coordinates cxx = ( n2.cx - n1.cx ) / L cxy = ( n2.cy - n1.cy ) / L cxz = ( n2.cz - n1.cz ) / L ll = sqrt(cxx**2 + cxy**2) if ll != 0.: cyx = -cxy/ll cyy = cxx/ll cyz = 0. else: cyx = 0. cyy = 1. cyz = 0. czx = cxy*cyz - cxz*cyy; czy = -cxx*cyz + cxz*cyx; czz = cxx*cyy - cxy*cyx; # Transforamtion matrix T = np.zeros((6,6), dtype=float) T[0,0] = cxx; T[0,1] = cxy; T[0,2] = cxz T[1,0] = cyx; T[1,1] = cyy; T[1,2] = cyz T[2,0] = czx; T[2,1] = czy; T[2,2] = czz T[3,3] = cxx; T[3,4] = cxy; T[3,5] = cxz T[4,3] = cyx; T[4,4] = cyy; T[4,5] = cyz T[5,3] = czx; T[5,4] = czy; T[5,5] = czz # Mass matrix M = np.zeros((6,6), dtype=float) if lumped: M[0,0] = 1.; M[5,5] = 1. M *= rho * Ax * L / 2. else: M[0,0] = 2.; M[1,1] = 2.; M[2,2] = 2. M[3,3] = 2.; M[4,4] = 2.; M[5,5] = 2. M[3,0] = 1.; M[4,1] = 1.; M[5,2] = 1. M[0,3] = 1.; M[1,4] = 1.; M[2,5] = 1. M *= rho * Ax * L / 6. # Evaluate Transformation M = T^T * M * T return np.dot(T.T,np.dot(M,T)) def calcKe(self): ''' Eval element stiffness matrix in local coordinates ''' mat = self.material prop = self.properties L = self.length() Ax, E = prop["Area"], mat["E"] # Stiffness matrix Ke = np.zeros((2,2), dtype=float) AxEoverL = Ax*E/L Ke[0,0] = AxEoverL; Ke[0,1] = -AxEoverL Ke[1,0] =-AxEoverL; Ke[1,1] = AxEoverL return Ke def calcK(self): ''' Eval element stiffness matrix in global coordinates. ''' L = self.length() n1, n2 = self.nodes # Transforamtion matrix T = np.zeros((2,6), dtype=float) # Eval direction cosines in order to transform stiffness matrix # from local coords (link direction) to global coordinates cx = ( n2.cx - n1.cx ) / L cy = ( n2.cy - n1.cy ) / L cz = ( n2.cz - n1.cz ) / L T[0,0] = cx; T[0,1] = cy; T[0,2] = cz T[1,3] = cx; T[1,4] = cy; T[1,5] = cz # Stiffness matrix K = self.calcKe() # Evaluate Transformation K = T^T * K * T return np.dot(T.T,np.dot(K,T)) def calcStress(self): raise NotImplementedError() def calcSectionForces(self, dx = 1e9): ''' Eval beam section forces in nodes ''' res = np.zeros((2,7), dtype=float) prop = self.properties mat = self.material n1, n2 = self.nodes L = self.length() E = mat['E'] G = mat['G'] Ax = prop['Area'] # Eval coordinate transformation matrix T = self.calcT() # Transform displacements from global to local coordinates u1 = np.dot(T, n1.results[:3]) u2 = np.dot(T, n2.results[:3]) # Axial force, Nx res[0,0] = 0. res[0,1] = (-Ax*E/L)*(u2[0] - u1[0]) res[1,0] = 1. res[1,1] = -res[0,1] return res
class Beam(BaseBeam): ''' 2-Node Structural 3D Beam ''' def __init__(self, n1, n2, material=None, properties=None): BaseBeam.__init__(self) self.nodeCount = 2 self.name = 'Structural 3D Beam' self.dofSet = DofSet() self.dofSet.reset(True) self.nodes = (n1, n2) self.material = material self.loads = List() if properties is None: properties = Properties() self.properties = properties def validate(self): ''' Validate beam ''' if self.length() < TINY: raise FEError('Beam length is to small') # check material for name in ('E', 'G', 'nu'): if name not in self.material: raise FEError('Material definition not complete') if self.material[name] < TINY: raise FEError("Material parameter '%s' not correct" % name) # check profile data for name in ('Area', 'Iyy', 'Izz'): if name not in self.properties: raise FEError('Properties definition not complete') if self.properties[name] < TINY: raise FEError("Properties parameter '%s' not correct" % name) def calcTe(self): ''' Eval element tranformation matrix ''' # Eval coordinate transformation matrix Tl = self.calcT() # Build the final transformation matrix - a 12x12 matrix T = np.zeros((12, 12), dtype=float) for i in range(4): for j in range(3): for k in range(3): T[j + 3 * i, k + 3 * i] = Tl[j, k] return T def calcM(self, lumped=False): ''' Eval elemenent consistent mass matrix in global coordinates ''' mat = self.material prop = self.properties L = self.length() LL = L * L rho = mat["Density"] Ax, Iy, Iz = prop["Area"], prop["Iyy"], prop["Izz"] J = prop.get("Ixx", 0.) if J == 0.: # Simplification only valid for circular sections J = Iy + Iz M = np.zeros((12, 12), dtype=float) t = rho * Ax * L ry = rho * Iy rz = rho * Iz po = rho * J * L if lumped: t *= .5 ry *= .5 rz *= .5 po *= .5 M[0][0] = M[1][1] = M[2][2] = M[6][6] = M[7][7] = M[8][8] = t M[3][3] = M[9][9] = po + ry + rz M[4][4] = M[10][10] = po + ry + rz M[5][5] = M[11][11] = po + ry + rz M[3][4] = M[4][3] = M[9][10] = M[10][9] = po + ry + rz M[3][5] = M[5][3] = M[9][11] = M[11][9] = po + ry + rz M[4][5] = M[5][4] = M[10][11] = M[11][10] = po + ry + rz else: M[0][0] = M[6][6] = t / 3. M[1][1] = M[7][7] = 13. * t / 35. + 6. * rz / (5. * L) M[2][2] = M[8][8] = 13. * t / 35. + 6. * ry / (5. * L) M[3][3] = M[9][9] = po / 3. M[4][4] = M[10][10] = t * LL / 105. + 2. * L * ry / 15. M[5][5] = M[11][11] = t * LL / 105. + 2. * L * rz / 15. M[4][2] = M[2][4] = -11. * t * L / 210. - ry / 10. M[5][1] = M[1][5] = 11. * t * L / 210. + rz / 10. M[6][0] = M[0][6] = t / 6. M[7][5] = M[5][7] = 13. * t * L / 420. - rz / 10. M[8][4] = M[4][8] = -13. * t * L / 420. + ry / 10. M[9][3] = M[3][9] = po / 6. M[10][2] = M[2][10] = 13. * t * L / 420. - ry / 10. M[11][1] = M[1][11] = -13. * t * L / 420. + rz / 10. M[10][8] = M[8][10] = 11. * t * L / 210. + ry / 10. M[11][7] = M[7][11] = -11. * t * L / 210. - rz / 10. M[7][1] = M[1][7] = 9. * t / 70. - 6. * rz / (5. * L) M[8][2] = M[2][8] = 9. * t / 70. - 6. * ry / (5. * L) M[10][4] = M[4][10] = -LL * t / 140. - ry * L / 30. M[11][5] = M[5][11] = -LL * t / 140. - rz * L / 30. T = self.calcTe() # Evaluate Transformation M = T^T * M * T return np.dot(T.T, np.dot(M, T)) def calcKe(self): ''' Eval element stiffness matrix in local coordinates. Includes transverse shear deformation. If no shear deflection constant (ShearZ and ShearY) is set, the displacement considers bending deformation only. Shear deflection constants for other cross-sections can be found in structural handbooks. ''' Ke = np.zeros((12, 12), dtype=float) mat = self.material prop = self.properties L = self.length() L2 = L * L L3 = L2 * L E, G = mat["E"], mat["G"] Ax, Iy, Iz = prop["Area"], prop["Iyy"], prop["Izz"] J = prop.get("Ixx", 0.) if J == 0.: # Simplification only valid for circular sections J = Iy + Iz Asy = prop.get("ShearY", 0.) Asz = prop.get("ShearZ", 0.) if Asy != 0. and Asz != 0.: Ksy = 12. * E * Iz / (G * Asy * L2) Ksz = 12. * E * Iy / (G * Asz * L2) else: Ksy = Ksz = 0. Ke[0, 0] = Ke[6, 6] = E * Ax / L Ke[1, 1] = Ke[7, 7] = 12. * E * Iz / (L3 * (1. + Ksy)) Ke[2, 2] = Ke[8, 8] = 12. * E * Iy / (L3 * (1. + Ksz)) Ke[3, 3] = Ke[9, 9] = G * J / L Ke[4, 4] = Ke[10, 10] = (4. + Ksz) * E * Iy / (L * (1. + Ksz)) Ke[5, 5] = Ke[11, 11] = (4. + Ksy) * E * Iz / (L * (1. + Ksy)) Ke[4, 2] = Ke[2, 4] = -6. * E * Iy / (L2 * (1. + Ksz)) Ke[5, 1] = Ke[1, 5] = 6. * E * Iz / (L2 * (1. + Ksy)) Ke[6, 0] = Ke[0, 6] = -Ke[0, 0] Ke[11, 7] = Ke[7, 11] = Ke[7, 5] = Ke[5, 7] = -Ke[5, 1] Ke[10, 8] = Ke[8, 10] = Ke[8, 4] = Ke[4, 8] = -Ke[4, 2] Ke[9, 3] = Ke[3, 9] = -Ke[3, 3] Ke[10, 2] = Ke[2, 10] = Ke[4, 2] Ke[11, 1] = Ke[1, 11] = Ke[5, 1] Ke[7, 1] = Ke[1, 7] = -Ke[1, 1] Ke[8, 2] = Ke[2, 8] = -Ke[2, 2] Ke[10, 4] = Ke[4, 10] = (2. - Ksz) * E * Iy / (L * (1. + Ksz)) Ke[11, 5] = Ke[5, 11] = (2. - Ksy) * E * Iz / (L * (1. + Ksy)) return Ke def calcK(self): ''' Eval element stiffness matrix in global coordinates. ''' K = self.calcKe() T = self.calcTe() # Evaluate Transformation K = T^T * K * T return np.dot(T.T, np.dot(K, T)) def calcStress(self): raise NotImplementedError() def calcNodalForces(self, res=None): ''' Calculate corresponding global forces at nodes from line loads ''' res = self.calcLocalNodalForces(res) Tl = self.calcT() # Build the final transformation matrix - a 12x12 matrix T = np.zeros((12, 12), dtype=float) for i in range(4): for j in range(3): for k in range(3): T[j + 3 * i, k + 3 * i] = Tl[j, k] res[:] = np.dot(T.T, res) return res def calcLocalNodalForces(self, res=None): ''' Calculate corresponding end forces at nodes from line loads in local directions ''' if res is None: res = np.zeros((self.dofSet.count() * self.nodeCount, ), dtype=float) L = self.length() LL = L * L load = self.calcLocalLoad() load *= L # Nx res[0] += .5 * load[0] res[6] += .5 * load[0] # Vy res[1] += .5 * load[1] res[7] += .5 * load[1] # VZ res[2] += .5 * load[2] res[8] += .5 * load[2] # My res[4] -= load[2] * L / 12. res[10] += load[2] * L / 12. # Mz res[5] -= load[1] * L / 12. res[11] += load[1] * L / 12. return res def calcSectionForces(self, dx=1e9): # find number of divisions try: nx = int(self.length() / dx) nx = min(max(1, nx), 100) except ZeroDivisionError: nx = 1 dx = self.length() res = np.zeros((nx + 1, 7), dtype=float) L = self.length() # Eval local coordinate transformation matrix Tl = self.calcT() # Build the final transformation matrix - a 12x12 matrix T = np.zeros((12, 12), dtype=float) for i in range(4): for j in range(3): for k in range(3): T[j + 3 * i, k + 3 * i] = Tl[j, k] # nodes flipped? n1, n2 = self.nodes if n1.cz > n2.cz: fac = -1. else: fac = 1. # Transform displacements and rotations from global to local coordinates d = np.zeros((12, ), dtype=float) d[:6] = n1.results d[6:] = n2.results if not n1.coordSys is None: Tcs = n1.coordSys d[:3] = np.dot(Tcs.T, d[:3]) d[3:6] = np.dot(Tcs.T, d[3:6]) if not n2.coordSys is None: Tcs = n2.coordSys d[6:9] = np.dot(Tcs.T, d[6:9]) d[9:12] = np.dot(Tcs.T, d[9:12]) u = np.dot(T, d) # Stiffness matrix in local coordinates Ke = self.calcKe() # Calculate forces in local directions forces = np.dot(Ke, u) # Correct forces from eqivalent forces line loads iforces = self.calcLocalNodalForces() forces -= iforces res[0, 0] = 0. res[0, 1:] = forces[:6] res[nx, 0] = 1. res[nx, 1:] = -forces[6:] if nx > 1: # accumulate interior span loads load = self.calcLocalLoad() _dx = dx x = _dx for i in range(nx): res[i + 1, 0] = x / L # Position res[i + 1, 1] = res[i, 1] + load[0] * _dx # Axial force, Nx res[i + 1, 2] = res[i, 2] + load[1] * _dx # Sheare force Vy res[i + 1, 3] = res[i, 3] + load[2] * _dx # Sheare force Vz res[i + 1, 4] = res[i, 4] # Torsion, Txx # correct last step if needed if (i + 1) * _dx > L: _dx = L - i * _dx x += _dx # trapezoidal integration of shear force for bending momemnt _dx = dx for i in range(nx): res[i + 1, 5] = res[i, 5] + .5 * (res[i + 1, 3] + res[i, 3]) * _dx # Myy res[i + 1, 6] = res[i, 6] + fac * .5 * (res[i + 1, 2] + res[i, 2]) * _dx # Mzz # correct last step if needed if (i + 1) * _dx > L: _dx = L - i * _dx return res
class Truss(BaseBeam): ''' 2-Node Structural 3D Truss ''' def __init__(self, n1, n2, material=None, properties=None): BaseBeam.__init__(self) self.nodeCount = 2 self.name = 'Structural 3D Truss' self.dofSet = DofSet(Dx=True, Dy=True, Dz=True) self.nodes = (n1, n2) self.material = material self.loads = List() if properties is None: properties = Properties() self.properties = properties def __str__(self): return '()' def __repr__(self): return '%s%s' % (self.__class__.__name__, str(self)) def validate(self): ''' Validate truss ''' if self.length() < TINY: raise FEError('Truss length is to small') # check material for name in ('E', ): if name not in self.material: raise FEError('Material definition not complete') if self.material[name] < TINY: raise FEError("Material parameter '%s' not correct" % name) # check profile data for name in ('Area', ): if name not in self.properties: raise FEError('Properties definition not complete') if self.properties[name] < TINY: raise FEError("Properties parameter '%s' not correct" % name) def calcLocalNodalForces(self, res=None): ''' Calculate corresponding end forces at nodes from line loads in local directions ''' if res is None: res = np.zeros((self.dofSet.count() * self.nodeCount, ), dtype=float) load = self.calcLocalLoad() # Nx res[0] += .5 * load[0] res[3] += .5 * load[0] # Vy res[1] += .5 * load[1] res[4] += .5 * load[1] # VZ res[2] += .5 * load[2] res[5] += .5 * load[2] return res def calcNodalForces(self, res=None): ''' Calculate corresponding global forces at nodes from line loads ''' # calculate local forces res = self.calcLocalNodalForces(res) # Transforamtion matrix T = self.calcT() res[0:3] = np.dot(T.T, res[0:3]) res[3:6] = np.dot(T.T, res[3:6]) return res def calcM(self, lumped=False): ''' Eval elemenent consistent mass matrix in global coordinates ''' mat = self.material prop = self.properties Ax, rho = prop["Area"], mat['Density'] n1, n2 = self.nodes L = self.length() # Eval direction cosines in order to transform stiffness matrix # from local coords (link direction) to global coordinates cxx = (n2.cx - n1.cx) / L cxy = (n2.cy - n1.cy) / L cxz = (n2.cz - n1.cz) / L ll = sqrt(cxx**2 + cxy**2) if ll != 0.: cyx = -cxy / ll cyy = cxx / ll cyz = 0. else: cyx = 0. cyy = 1. cyz = 0. czx = cxy * cyz - cxz * cyy czy = -cxx * cyz + cxz * cyx czz = cxx * cyy - cxy * cyx # Transforamtion matrix T = np.zeros((6, 6), dtype=float) T[0, 0] = cxx T[0, 1] = cxy T[0, 2] = cxz T[1, 0] = cyx T[1, 1] = cyy T[1, 2] = cyz T[2, 0] = czx T[2, 1] = czy T[2, 2] = czz T[3, 3] = cxx T[3, 4] = cxy T[3, 5] = cxz T[4, 3] = cyx T[4, 4] = cyy T[4, 5] = cyz T[5, 3] = czx T[5, 4] = czy T[5, 5] = czz # Mass matrix M = np.zeros((6, 6), dtype=float) if lumped: M[0, 0] = 1. M[5, 5] = 1. M *= rho * Ax * L / 2. else: M[0, 0] = 2. M[1, 1] = 2. M[2, 2] = 2. M[3, 3] = 2. M[4, 4] = 2. M[5, 5] = 2. M[3, 0] = 1. M[4, 1] = 1. M[5, 2] = 1. M[0, 3] = 1. M[1, 4] = 1. M[2, 5] = 1. M *= rho * Ax * L / 6. # Evaluate Transformation M = T^T * M * T return np.dot(T.T, np.dot(M, T)) def calcKe(self): ''' Eval element stiffness matrix in local coordinates ''' mat = self.material prop = self.properties L = self.length() Ax, E = prop["Area"], mat["E"] # Stiffness matrix Ke = np.zeros((2, 2), dtype=float) AxEoverL = Ax * E / L Ke[0, 0] = AxEoverL Ke[0, 1] = -AxEoverL Ke[1, 0] = -AxEoverL Ke[1, 1] = AxEoverL return Ke def calcK(self): ''' Eval element stiffness matrix in global coordinates. ''' L = self.length() n1, n2 = self.nodes # Transforamtion matrix T = np.zeros((2, 6), dtype=float) # Eval direction cosines in order to transform stiffness matrix # from local coords (link direction) to global coordinates cx = (n2.cx - n1.cx) / L cy = (n2.cy - n1.cy) / L cz = (n2.cz - n1.cz) / L T[0, 0] = cx T[0, 1] = cy T[0, 2] = cz T[1, 3] = cx T[1, 4] = cy T[1, 5] = cz # Stiffness matrix K = self.calcKe() # Evaluate Transformation K = T^T * K * T return np.dot(T.T, np.dot(K, T)) def calcStress(self): raise NotImplementedError() def calcSectionForces(self, dx=1e9): ''' Eval beam section forces in nodes ''' res = np.zeros((2, 7), dtype=float) prop = self.properties mat = self.material n1, n2 = self.nodes L = self.length() E = mat['E'] G = mat['G'] Ax = prop['Area'] # Eval coordinate transformation matrix T = self.calcT() # Transform displacements from global to local coordinates u1 = np.dot(T, n1.results[:3]) u2 = np.dot(T, n2.results[:3]) # Axial force, Nx res[0, 0] = 0. res[0, 1] = (-Ax * E / L) * (u2[0] - u1[0]) res[1, 0] = 1. res[1, 1] = -res[0, 1] return res