class BAR2(ContinuumElement): """2-node, isoparametric element Notes ----- Node and element face numbering 0-------1 [0] """ ndi = 1 nshr = 0 name = "BAR2" num_node = 2 num_coord = 1 num_dof_per_node = 1 type = geom.get_elem_type(num_coord, num_node) cp = geom.elem_center_coord(num_coord, num_node) xp = geom.elem_corner_coord(num_coord, num_node) def __init__(self): # integration scheme for this element self.integration = IntegrationProperties(self.num_coord, self.num_node) self.shape = ShapefunctionPrototype(self.num_coord, self.num_node) # element boundary properties self.num_face_nodes = geom.num_face_nodes(self.num_coord, self.num_node) args = (self.num_coord-1, self.num_face_nodes) self.bndry = ShapefunctionPrototype(*args) self.bndry.integration = IntegrationProperties(*args)
class TRIE3(ContinuumElement): """Element function for linear CST element defined in [1] Notes ----- Node and element face numbering 2 |\ [2] | \ [1] | \ 0-----1 [0] References ---------- 1. Taylor & Hughes (1981), p.49 """ ndi = 3 nshr = 1 name = "TRIE3" num_node = 3 num_coord = 2 num_dof_per_node = 2 type = geom.get_elem_type(num_coord, num_node) cp = geom.elem_center_coord(num_coord, num_node) xp = geom.elem_corner_coord(num_coord, num_node) def __init__(self): # integration scheme for this element self.integration = IntegrationProperties(self.num_coord, self.num_node) self.shape = ShapefunctionPrototype(self.num_coord, self.num_node) # element boundary properties self.num_face_nodes = geom.num_face_nodes(self.num_coord, self.num_node) args = (self.num_coord - 1, self.num_face_nodes) self.bndry = ShapefunctionPrototype(*args) self.bndry.integration = IntegrationProperties(*args) self.thickness = 1. @staticmethod def b_matrix(shg, shgbar=None, **kwargs): """Assemble and return the B matrix""" B = zeros((4, 6)) B[0, 0::2] = shg[0, :] B[1, 1::2] = shg[1, :] B[3, 0::2] = shg[1, :] B[3, 1::2] = shg[0, :] return B @classmethod def volume(cls, coords): return geom.elem_volume(cls.num_coord, cls.num_node, coords, cls.thickness)
class QE4FI(ContinuumElement): """4-node, isoparametric, plane strain element. Full integration Notes ----- Node and element face numbering [2] 3-------2 | | [3] | | [1] | | 0-------1 [0] """ ndi = 3 nshr = 1 name = "QE4FI" num_node = 4 num_coord = 2 num_dof_per_node = 2 type = geom.get_elem_type(num_coord, num_node) cp = geom.elem_center_coord(num_coord, num_node) xp = geom.elem_corner_coord(num_coord, num_node) def __init__(self): # integration scheme for this element self.integration = IntegrationProperties(self.num_coord, self.num_node) self.shape = ShapefunctionPrototype(self.num_coord, self.num_node) # element boundary properties self.num_face_nodes = geom.num_face_nodes(self.num_coord, self.num_node) args = (self.num_coord - 1, self.num_face_nodes) self.bndry = ShapefunctionPrototype(*args) self.bndry.integration = IntegrationProperties(*args) self.thickness = 1. def b_matrix(self, shg, shgbar=None, **kwargs): """Assemble and return the B matrix""" B = zeros( (self.ndi + self.nshr, self.num_node * self.num_dof_per_node)) B[0, 0::2] = shg[0, :] B[1, 1::2] = shg[1, :] B[3, 0::2] = shg[1, :] B[3, 1::2] = shg[0, :] return B @classmethod def volume(cls, coords): return geom.elem_volume(cls.num_coord, cls.num_node, coords, cls.thickness)
class TET4(ContinuumElement): """Trilinear tet element""" ndi = 3 nshr = 3 name = "TET4" num_node = 4 num_coord = 3 num_dof_per_node = 3 type = geom.get_elem_type(num_coord, num_node) cp = geom.elem_center_coord(num_coord, num_node) xp = geom.elem_corner_coord(num_coord, num_node) def __init__(self): # integration scheme for this element self.integration = IntegrationProperties(self.num_coord, self.num_node) self.shape = ShapefunctionPrototype(self.num_coord, self.num_node) # element boundary properties self.num_face_nodes = geom.num_face_nodes(self.num_coord, self.num_node) args = (self.num_coord - 1, self.num_face_nodes) self.bndry = ShapefunctionPrototype(*args) self.bndry.integration = IntegrationProperties(*args) self.thickness = 1. @staticmethod def b_matrix(shg, shgbar=None, **kwargs): B = zeros((6, 12)) B[0, 0::3] = shg[0, :] B[1, 1::3] = shg[1, :] B[2, 2::3] = shg[2, :] B[3, 0::3] = shg[1, :] B[3, 1::3] = shg[0, :] B[4, 1::3] = shg[2, :] B[4, 2::3] = shg[1, :] B[5, 0::3] = shg[2, :] B[5, 2::3] = shg[0, :] return B @classmethod def volume(cls, coords): return geom.elem_volume(cls.num_coord, cls.num_node, coords)
class HEX8RI(ContinuumElement): """Trilinear hex element Notes ----- Integration points in parentheses 7---------------6 /|(7) (6)/| / | / | / | / | / | / | / (4)| (5)/ | 4---------------5 | | | | | | 3---------|-----2 | / (3) | (2)/ | / | / | / | / | / | / |/ (0) (1)|/ 0---------------1 """ ndi = 3 nshr = 3 name = "HEX8RI" num_node = 8 num_coord = 3 num_dof_per_node = 3 type = geom.get_elem_type(num_coord, num_node) cp = geom.elem_center_coord(num_coord, num_node) xp = geom.elem_corner_coord(num_coord, num_node) def __init__(self): # integration scheme for this element self.integration = IntegrationProperties(self.num_coord, self.num_node, reduced=True) self.shape = ShapefunctionPrototype(self.num_coord, self.num_node) # element boundary properties self.num_face_nodes = geom.num_face_nodes(self.num_coord, self.num_node) args = (self.num_coord - 1, self.num_face_nodes) self.bndry = ShapefunctionPrototype(*args) self.bndry.integration = IntegrationProperties(*args) def b_matrix(self, shg, shgbar=None, **kwargs): B = zeros((6, 24)) B[0, 0::3] = shg[0, :] B[1, 1::3] = shg[1, :] B[2, 2::3] = shg[2, :] B[3, 0::3] = shg[1, :] B[3, 1::3] = shg[0, :] B[4, 1::3] = shg[2, :] B[4, 2::3] = shg[1, :] B[5, 0::3] = shg[2, :] B[5, 2::3] = shg[0, :] return B @classmethod def volume(cls, coords): return geom.elem_volume(cls.num_coord, cls.num_node, coords)
class QE8FI(ContinuumElement): """8-node, isoparametric, plane strain element Notes ----- Node and element face numbering [2] 3---6---2 | | [3] 7 5 [1] | | 0---4---1 [0] """ ndi = 3 nshr = 1 name = "QE8FI" num_node = 8 num_coord = 2 num_dof_per_node = 2 type = geom.get_elem_type(num_coord, num_node) cp = geom.elem_center_coord(num_coord, num_node) xp = geom.elem_corner_coord(num_coord, num_node) def __init__(self): # integration scheme for this element self.integration = IntegrationProperties(self.num_coord, self.num_node) self.shape = ShapefunctionPrototype(self.num_coord, self.num_node) # element boundary properties self.num_face_nodes = geom.num_face_nodes(self.num_coord, self.num_node) args = (self.num_coord-1, self.num_face_nodes) self.bndry = ShapefunctionPrototype(*args) self.bndry.integration = IntegrationProperties(*args) self.thickness = 1. def b_matrix(self, shg, shgbar=None, **kwargs): """Assemble and return the B matrix""" B = zeros((self.ndi+self.nshr, self.num_node * self.num_dof_per_node)) B[0, 0::2] = shg[0, :] B[1, 1::2] = shg[1, :] B[3, 0::2] = shg[1, :] B[3, 1::2] = shg[0, :] if shgbar is None: return B # mean dilatational formulation for a in range(self.num_node): i = 2 * a j = i + 1 bb1 = (shgbar[0, a] - shg[0, a]) / 2. bb2 = (shgbar[1, a] - shg[1, a]) / 2. B[0, i:i+2] += [bb1, bb2] B[1, i:i+2] += [bb1, bb2] return B @classmethod def volume(cls, coords): return geom.elem_volume(cls.num_coord, cls.num_node, coords, thickness=cls.thickness)
class QE4SRI(ContinuumElementSRI): """4-node, isoparametric, plane strain element Notes ----- Node and element face numbering [2] 3-------2 | | [3] | | [1] | | 0-------1 [0] """ ndi = 3 nshr = 1 name = "QE4SRI" num_node = 4 num_coord = 2 num_dof_per_node = 2 type = geom.get_elem_type(num_coord, num_node) cp = geom.elem_center_coord(num_coord, num_node) xp = geom.elem_corner_coord(num_coord, num_node) def __init__(self): # integration scheme for this element self.integration = IntegrationProperties(self.num_coord, self.num_node) self.shape = ShapefunctionPrototype(self.num_coord, self.num_node) # element boundary properties self.num_face_nodes = geom.num_face_nodes(self.num_coord, self.num_node) args = (self.num_coord - 1, self.num_face_nodes) self.bndry = ShapefunctionPrototype(*args) self.bndry.integration = IntegrationProperties(*args) self.thickness = 1. self.sri_e = QE4RI(**{"hourglass stiffness": 0.}) @staticmethod def iso_contrib(c, num_coord, nshr): # modify base function to have zeros in rows/columns associated with # plane strain I = array([1 for i in range(2)] + [0 for i in range(2)]) a = dot(I, c) / num_coord return outer(a, I) def b_matrix(self, shg, shgbar=None, **kwargs): """Assemble and return the B matrix""" B = zeros( (self.ndi + self.nshr, self.num_node * self.num_dof_per_node)) B[0, 0::2] = shg[0, :] B[1, 1::2] = shg[1, :] B[3, 0::2] = shg[1, :] B[3, 1::2] = shg[0, :] if shgbar is None: return B # mean dilatational formulation for a in range(self.num_node): i = 2 * a j = i + 1 bb1 = (shgbar[0, a] - shg[0, a]) / 2. bb2 = (shgbar[1, a] - shg[1, a]) / 2. B[0, i:i + 2] += [bb1, bb2] B[1, i:i + 2] += [bb1, bb2] return B @classmethod def volume(cls, coords): return geom.elem_volume(cls.num_coord, cls.num_node, coords, cls.thickness)
class QE4IM(ContinuumElementI): """4-node, isoparametric, plane strain element, incompatible modes Notes ----- Node and element face numbering [2] 3-------2 | | [3] | | [1] | | 0-------1 [0] """ ndi = 3 nshr = 1 name = "QE4IM" num_node = 4 num_coord = 2 num_dof_per_node = 2 num_incompat_modes = 2 type = geom.get_elem_type(num_coord, num_node) cp = geom.elem_center_coord(num_coord, num_node) xp = geom.elem_corner_coord(num_coord, num_node) def __init__(self): # integration scheme for this element self.integration = IntegrationProperties(self.num_coord, self.num_node) self.shape = ShapefunctionPrototype(self.num_coord, self.num_node) # element boundary properties self.num_face_nodes = geom.num_face_nodes(self.num_coord, self.num_node) args = (self.num_coord-1, self.num_face_nodes) self.bndry = ShapefunctionPrototype(*args) self.bndry.integration = IntegrationProperties(*args) self.thickness = 1. def b_matrix(self, shg, shgbar=None, lcoords=None, coords=None, det=None, disp=0, **kwargs): """Assemble and return the B matrix""" B = zeros((self.ndi+self.nshr, self.num_node * self.num_dof_per_node)) B[0, 0::2] = shg[0, :] B[1, 1::2] = shg[1, :] B[3, 0::2] = shg[1, :] B[3, 1::2] = shg[0, :] if not disp: return B # Algorithm in # The Finite Element Method: Its Basis and Fundamentals # By Olek C Zienkiewicz, Robert L Taylor, J.Z. Zhu # Jacobian at element centroid dNdxi = self.shape.grad([0., 0.]) dxdxi = dot(dNdxi, coords) J0 = inv(dxdxi) dt0 = determinant(dxdxi) xi, eta = lcoords dNdxi = array([[-2. * xi, 0.], [0., -2. * eta]]) dNdx = dt0 / det * dot(J0, dNdxi) G1 = array([[dNdx[0, 0], 0], [0, dNdx[0, 1]], [0, 0], [dNdx[0, 1], dNdx[0, 0]]]) G2 = array([[dNdx[1, 0], 0], [0, dNdx[1, 1]], [0, 0], [dNdx[1, 1], dNdx[1, 0]]]) G = concatenate((G1, G2), axis=1) return B, G # Algorithm in Taylor's original paper and # The Finite Element Method: Linear Static and Dynamic # Finite Element Analysis # By Thomas J. R. Hughe xi = self.gauss_coords n = self.num_gauss dxdxi = asum([coords[i, 0] * xi[i, 0] for i in range(n)]) dxdeta = asum([coords[i, 0] * xi[i, 1] for i in range(n)]) dydxi = asum([coords[i, 1] * xi[i, 0] for i in range(n)]) dydeta = asum([coords[i, 1] * xi[i, 1] for i in range(n)]) xi, eta = lcoords G1 = array([[-xi * dydeta, 0], [0, xi * dxdeta], [0, 0], [xi * dxdeta, -xi * dydeta]]) G2 = array([[-eta * dydxi, 0.], [0, eta * dxdxi], [0, 0], [eta * dxdxi, -eta * dydxi]]) G = 2. / det * concatenate((G1, G2), axis=1) return B, G @classmethod def volume(cls, coords): return geom.elem_volume(cls.num_coord, cls.num_node, coords, cls.thickness)
class HEX8SRI(ContinuumElementSRI): """Trilinear hex element Notes ----- Integration points in parentheses 7---------------6 /|(7) (6)/| / | / | / | / | / | / | / (4)| (5)/ | 4---------------5 | | | | | | 3---------|-----2 | / (3) | (2)/ | / | / | / | / | / | / |/ (0) (1)|/ 0---------------1 """ ndi = 3 nshr = 3 name = "HEX8SRI" num_node = 8 num_coord = 3 num_dof_per_node = 3 type = geom.get_elem_type(num_coord, num_node) cp = geom.elem_center_coord(num_coord, num_node) xp = geom.elem_corner_coord(num_coord, num_node) def __init__(self): # integration scheme for this element self.integration = IntegrationProperties(self.num_coord, self.num_node) self.shape = ShapefunctionPrototype(self.num_coord, self.num_node) # element boundary properties self.num_face_nodes = geom.num_face_nodes(self.num_coord, self.num_node) args = (self.num_coord - 1, self.num_face_nodes) self.bndry = ShapefunctionPrototype(*args) self.bndry.integration = IntegrationProperties(*args) self.sri_e = HEX8RI(**{"hourglass stiffness": 0.}) @staticmethod def iso_contrib(c): I = array([1 for i in range(3)] + [0 for i in range(3)]) a = dot(I, c) / num_coord return outer(a, I) def b_matrix(self, shg, shgbar=None, **kwargs): B = zeros((6, 24)) B[0, 0::3] = shg[0, :] B[1, 1::3] = shg[1, :] B[2, 2::3] = shg[2, :] B[3, 0::3] = shg[1, :] B[3, 1::3] = shg[0, :] B[4, 1::3] = shg[2, :] B[4, 2::3] = shg[1, :] B[5, 0::3] = shg[2, :] B[5, 2::3] = shg[0, :] return B @classmethod def volume(cls, coords): return geom.elem_volume(cls.num_coord, cls.num_node, coords)