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)
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 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