Пример #1
0
def mem_static_x_vector(mem, refn, vdc, type='bpt', atol=1e-10, maxiter=100):
    '''
    Static deflection of membrane calculated via fixed-point iteration.
    '''
    def pes(v, x, g_eff):
        return -e_0 / 2 * v**2 / (g_eff + x)**2

    if isinstance(mem, abstract.SquareCmutMembrane):
        amesh = mesh.square(mem.length_x, mem.length_y, refn)
    else:
        amesh = mesh.circle(mem.radius, refn)

    K = mem_k_matrix(mem, refn, type=type)
    g_eff = mem.gap + mem.isolation / mem.permittivity
    F = mem_f_vector(mem, refn, 1)
    Kinv = linalg.inv(K)
    nnodes = K.shape[0]
    x0 = np.zeros(nnodes)

    for i in range(maxiter):
        x0_new = Kinv.dot(F * pes(vdc, x0, g_eff))

        if np.max(np.abs(x0_new - x0)) < atol:
            is_collapsed = False
            return x0_new, is_collapsed

        x0 = x0_new

    is_collapsed = True
    return x0, is_collapsed
Пример #2
0
def mem_static_x_vector(mem, refn, vdc, k, x0, atol=1e-10, maxiter=100):
    '''
    '''
    def pes(v, x, g_eff):
        return -e_0 / 2 * v**2 / (g_eff + x)**2

    if isinstance(mem, abstract.SquareCmutMembrane):
        amesh = mesh.square(mem.length_x, mem.length_y, refn)
    else:
        amesh = mesh.circle(mem.radius, refn)

    K = fem.mem_k_matrix(mem, refn)
    g_eff = mem.gap + mem.isolation / mem.permittivity
    F = fem.mem_f_vector(mem, refn, 1)
    Kinv = np.linalg.inv(K)
    nnodes = K.shape[0]
    x = np.zeros(nnodes)

    for i in range(maxiter):
        p = F * pes(vdc, x, g_eff)
        p[x < x0] += -k * (x[x < x0] - x0) * F[x < x0]

        xnew = Kinv.dot(p)

        if np.max(np.abs(xnew - x)) < atol:
            is_collapsed = False
            return xnew, is_collapsed

        x = xnew

    is_collapsed = True
    return x, p, is_collapsed
Пример #3
0
def mem_f_vector(mem, refn, p):
    '''
    Pressure load vector based on equal distribution of pressure to element
    nodes.
    '''
    if isinstance(mem, abstract.SquareCmutMembrane):
        amesh = mesh.square(mem.length_x, mem.length_y, refn)
    else:
        amesh = mesh.circle(mem.radius, refn)

    nodes = amesh.vertices
    triangles = amesh.triangles
    triangle_areas = amesh.g / 2
    ob = amesh.on_boundary

    f = np.zeros(len(nodes))
    for tt in range(len(triangles)):
        tri = triangles[tt, :]
        ap = triangle_areas[tt]
        bfac = 1 * np.sum(~ob[tri])

        f[tri] += 1 / bfac * p * ap

    f[ob] = 0

    return f
Пример #4
0
def mem_dlm_matrix(mem, refn):
    '''
    Mass matrix based on equal distribution of element mass to nodes
    (diagonally-lumped).
    '''
    if isinstance(mem, abstract.SquareCmutMembrane):
        amesh = mesh.square(mem.length_x, mem.length_y, refn)
    else:
        amesh = mesh.circle(mem.radius, refn)

    # get mesh information
    nodes = amesh.vertices
    triangles = amesh.triangles
    triangle_areas = amesh.g / 2

    mass = sum([x * y for x, y in zip(mem.density, mem.thickness)])

    # construct M matrix by adding contribution from each element
    M = np.zeros((len(nodes), len(nodes)))
    for tt in range(len(triangles)):
        tri = triangles[tt, :]
        ap = triangle_areas[tt]
        M[tri, tri] += 1 / 3 * mass * ap

    ob = amesh.on_boundary
    M[ob, :] = 0
    M[:, ob] = 0
    M[ob, ob] = 1

    return M
Пример #5
0
def mem_z_matrix(mem, refn, k, *args, **kwargs):
    '''
    Impedance matrix in FullFormat for a membrane.
    '''
    if isinstance(mem, abstract.SquareCmutMembrane):
        amesh = mesh.square(mem.length_x, mem.length_y, refn)
    else:
        amesh = mesh.circle(mem.radius, refn)

    return np.array(ZFullMatrix(amesh, k, *args, **kwargs).data)
Пример #6
0
def calc_es_correction(array, refn):

    fcorr = []

    for elem in array.elements:
        for mem in elem.membranes:
            if isinstance(mem, abstract.SquareCmutMembrane):
                square = True
                amesh = mesh.square(mem.length_x, mem.length_y, refn=refn)
            elif isinstance(mem, abstract.CircularCmutMembrane):
                square = False
                amesh = mesh.circle(mem.radius, refn=refn)

            K = fem.mem_k_matrix(amesh, mem.y_modulus, mem.thickness, mem.p_ratio)
            Kinv = np.linalg.inv(K)
            g = mem.gap
            g_eff = mem.gap + mem.isolation / mem.permittivity

            # F = fem.mem_f_vector(amesh, 1)
            F = np.array(fem.f_from_abstract(array, refn).todense())
            # u = Kinv.dot(-F)
            # unorm = u / np.max(np.abs(u))

            for i, pat in enumerate(mem.patches):
                if square:
                    avg = fem.square_patch_avg_vector(amesh.vertices, amesh.triangles, amesh.on_boundary,
                        mem.length_x, mem.length_y, pat.position[0] - mem.position[0], 
                        pat.position[1] - mem.position[1], pat.length_x, pat.length_y)
                else:
                    avg = fem.circular_patch_avg_vector(amesh.vertices, amesh.triangles, amesh.on_boundary,
                        mem.radius, pat.position[0] - mem.position[0], pat.position[1] - mem.position[1], 
                        pat.radius_min, pat.radius_max, pat.theta_min, pat.theta_max)

                u = Kinv.dot(-F[:,i])
                unorm = u / np.max(np.abs(u))

                d = np.linspace(0, 1, 11)
                fc = []
                uavg = []
                fpp = []
                for di in d:
                    ubar = (unorm * g * di).dot(avg) / pat.area
                    uavg.append(ubar)

                    fc.append((-e_0 / 2 / (unorm * g * di + g_eff)**2).dot(avg) / pat.area)
                    fpp.append(-e_0 / 2 / (ubar + g_eff)**2)

                fcorr.append((d, unorm, uavg, fc, fpp))
                # fcorr.append(interp1d(uavg, fc, kind='cubic', bounds_error=False, fill_value=(fc[-1], fc[0])))

    return fcorr
Пример #7
0
def mem_f_vector_arb_load(mem, refn, load_func):
    '''
    Pressure load vector based on an arbitrary load.
    '''
    if isinstance(mem, abstract.SquareCmutMembrane):
        amesh = mesh.square(mem.length_x, mem.length_y, refn)
    else:
        amesh = mesh.circle(mem.radius, refn)

    nodes = amesh.vertices
    triangles = amesh.triangles
    triangle_areas = amesh.triangle_areas
    ob = amesh.on_boundary

    f = np.zeros(len(nodes))
    for tt in range(len(triangles)):
        tri = triangles[tt, :]
        xi, yi = nodes[tri[0], :2]
        xj, yj = nodes[tri[1], :2]
        xk, yk = nodes[tri[2], :2]

        def load_func_psi_eta(psi, eta):
            x = (xj - xi) * psi + (xk - xi) * eta + xi
            y = (yj - yi) * psi + (yk - yi) * eta + yi
            return load_func(x, y)

        integ, _ = dblquad(load_func_psi_eta,
                           0,
                           1,
                           0,
                           lambda x: 1 - x,
                           epsrel=1e-1,
                           epsabs=1e-1)
        frac = integ / (1 / 2)  # fraction of triangle covered by load
        da = triangle_areas[tt]
        bfac = 1 * np.sum(~ob[tri])

        f[tri] += 1 / bfac * frac * da

    ob = amesh.on_boundary
    f[ob] = 0

    return f
Пример #8
0
def mem_eig(mem, refn):
    '''
    Returns the eigenfrequency (in Hz) and eigenmodes of a membrane.
    '''
    if isinstance(mem, abstract.SquareCmutMembrane):
        amesh = mesh.square(mem.length_x, mem.length_y, refn)
    else:
        amesh = mesh.circle(mem.radius, refn)
    ob = amesh.on_boundary

    M = mem_m_matrix(mem, refn, mu=0.5)
    K = mem_k_matrix(mem, refn)
    w, v = linalg.eig(linalg.inv(M).dot(K)[np.ix_(~ob, ~ob)])

    idx = np.argsort(np.sqrt(np.abs(w)))
    eigf = np.sqrt(np.abs(w))[idx] / (2 * np.pi)
    eigv = v[:, idx]

    return eigf, eigv
Пример #9
0
def mem_cm_matrix(mem, refn):
    '''
    Mass matrix based on kinetic energy and linear shape functions
    (consistent).
    '''
    if isinstance(mem, abstract.SquareCmutMembrane):
        amesh = mesh.square(mem.length_x, mem.length_y, refn)
    else:
        amesh = mesh.circle(mem.radius, refn)

    # get mesh information
    nodes = amesh.vertices
    triangles = amesh.triangles
    triangle_areas = amesh.triangle_areas

    mass = sum([x * y for x, y in zip(mem.density, mem.thickness)])

    # construct M matrix by adding contribution from each element
    M = np.zeros((len(nodes), len(nodes)))
    for tt in range(len(triangles)):
        tri = triangles[tt, :]
        xi, yi = nodes[tri[0], :2]
        xj, yj = nodes[tri[1], :2]
        xk, yk = nodes[tri[2], :2]

        # da = ((xj - x5) * (yk - yi) - (xk - x5) * (yj - yi))
        da = triangle_areas[tt]
        Mt = np.array([[1, 1 / 2, 1 / 2], [1 / 2, 1, 1 / 2], [1 / 2, 1 / 2, 1]
                       ]) / 12
        M[np.ix_(tri, tri)] += 2 * Mt * mass * da

    ob = amesh.on_boundary
    M[ob, :] = 0
    M[:, ob] = 0
    M[ob, ob] = 1

    return M
Пример #10
0
def mem_k_matrix_bpt(mem, refn):
    '''
    Stiffness matrix based on 3-dof (rotation-free) basic plate triangle (BPT) elements.
    Refer to E. Onate and F. Zarate, Int. J. Numer. Meth. Engng. 47, 557-603 (2000).
    '''
    def L(x1, y1, x2, y2):
        # calculates edge length
        return np.sqrt((x2 - x1)**2 + (y2 - y1)**2)

    def T_matrix(x1, y1, x2, y2):
        # transformation matrix for an edge
        z = [0, 0, 1]
        r = [x2 - x1, y2 - y1]
        n = np.cross(z, r)
        norm = np.linalg.norm(n)
        n = n / norm
        nx, ny, _ = n
        return np.array([[-nx, 0], [0, -ny], [-ny, -nx]])

    if isinstance(mem, abstract.SquareCmutMembrane):
        amesh = mesh.square(mem.length_x, mem.length_y, refn)
    else:
        amesh = mesh.circle(mem.radius, refn)

    # get mesh information
    nodes = amesh.vertices
    triangles = amesh.triangles
    triangle_edges = amesh.triangle_edges
    triangle_areas = amesh.triangle_areas
    ntriangles = len(triangles)
    ob = amesh.on_boundary

    # determine list of neighbors for each triangle
    # None indicates neighbor doesn't exist for that edge (boundary edge)
    triangle_neighbors = []
    for tt in range(ntriangles):
        neighbors = []
        for te in triangle_edges[tt, :]:
            mask = np.any(triangle_edges == te, axis=1)
            args = np.nonzero(mask)[0]
            if len(args) > 1:
                neighbors.append(args[args != tt][0])
            else:
                neighbors.append(None)
        triangle_neighbors.append(neighbors)
    amesh.triangle_neighbors = triangle_neighbors

    # construct constitutive matrix for material
    h = mem.thickness[0]  # no support for composite membranes yet
    E = mem.y_modulus[0]
    eta = mem.p_ratio[0]

    D = np.zeros((3, 3))
    D[0, 0] = 1
    D[0, 1] = eta
    D[1, 0] = eta
    D[1, 1] = 1
    D[2, 2] = (1 - eta) / 2
    D = D * E * h**3 / (12 * (1 - eta**2))

    # calculate Jacobian and gradient operator for each triangle
    gradops = []
    for tt in range(ntriangles):
        tri = triangles[tt, :]
        xi, yi = nodes[tri[0], :2]
        xj, yj = nodes[tri[1], :2]
        xk, yk = nodes[tri[2], :2]

        J = np.array([[xj - xi, xk - xi], [yj - yi, yk - yi]])
        gradop = np.linalg.inv(J.T).dot([[-1, 1, 0], [-1, 0, 1]])
        # gradop = np.linalg.inv(J.T).dot([[1, 0, -1],[0, 1, -1]])

        gradops.append(gradop)

    # construct K matrix
    K = np.zeros((len(nodes), len(nodes)))
    for p in range(ntriangles):
        trip = triangles[p, :]
        ap = triangle_areas[p]

        xi, yi = nodes[trip[0], :2]
        xj, yj = nodes[trip[1], :2]
        xk, yk = nodes[trip[2], :2]

        neighbors = triangle_neighbors[p]
        gradp = gradops[p]
        # list triangle edges, ordered so that z cross-product will produce outward normal
        edges = [(xk, yk, xj, yj), (xi, yi, xk, yk), (xj, yj, xi, yi)]

        # begin putting together indexes needed later for matrix assignment
        ii, jj, kk = trip
        Kidx = [ii, jj, kk]
        Kpidx = [0, 1, 2]

        # construct B matrix for control element
        Bp = np.zeros((3, 6))
        for j, n in enumerate(neighbors):
            if n is None:
                continue

            # determine index of the node in the neighbor opposite edge
            iin, jjn, kkn = triangles[n, :]
            uidx = [
                x for x in np.unique([ii, jj, kk, iin, jjn, kkn])
                if x not in [ii, jj, kk]
            ][0]
            # update indexes
            Kidx.append(uidx)
            Kpidx.append(3 + j)

            l = L(*edges[j])
            T = T_matrix(*edges[j])
            gradn = gradops[n]

            pterm = l / 2 * T.dot(gradp)
            Bp[:, :3] += pterm

            nterm = l / 2 * T.dot(gradn)
            idx = [Kpidx[Kidx.index(x)] for x in [iin, jjn, kkn]]
            Bp[:, idx] += nterm
        Bp = Bp / ap

        # construct local K matrix for control element
        Kp = (Bp.T).dot(D).dot(Bp) * ap

        # add matrix values to global K matrix
        K[np.ix_(Kidx, Kidx)] += Kp[np.ix_(Kpidx, Kpidx)]

    K[ob, :] = 0
    K[:, ob] = 0
    K[ob, ob] = 1

    return K
Пример #11
0
def mem_k_matrix_hybrid(mem, refn):
    '''
    *Experimental* Stiffness matrix based on HPB for interior triangles and
    BPT for boundary triangles.
    '''
    def norm(r1, r2):
        # calculates edge length
        return np.sqrt((r2[0] - r1[0])**2 + (r2[1] - r1[1])**2)

    def LL(x1, y1, x2, y2):
        # calculates edge length
        return np.sqrt((x2 - x1)**2 + (y2 - y1)**2)

    def T_matrix(x1, y1, x2, y2):
        # transformation matrix for an edge
        z = [0, 0, 1]
        r = [x2 - x1, y2 - y1]
        n = np.cross(z, r)
        norm = np.linalg.norm(n)
        n = n / norm
        nx, ny, _ = n
        return np.array([[-nx, 0], [0, -ny], [-ny, -nx]])

    if isinstance(mem, abstract.SquareCmutMembrane):
        amesh = mesh.square(mem.length_x, mem.length_y, refn)
    else:
        amesh = mesh.circle(mem.radius, refn)

    # get mesh information
    nodes = amesh.vertices
    triangles = amesh.triangles
    triangle_edges = amesh.triangle_edges
    triangle_areas = amesh.triangle_areas
    ntriangles = len(triangles)
    ob = amesh.on_boundary

    # determine list of neighbors and neighbor nodes for each triangle
    # None indicates neighbor doesn't exist for that edge (boundary edge)
    triangle_neighbors = []
    neighbors_node = []

    for tt in range(ntriangles):

        neighbors = []
        neighbor_node = []
        for te in triangle_edges[tt, :]:

            mask = np.any(triangle_edges == te, axis=1)
            args = np.nonzero(mask)[0]

            if len(args) > 1:

                n = args[args != tt][0]
                neighbors.append(n)

                # determine index of the node in the neighbor opposite edge
                iin, jjn, kkn = triangles[n, :]
                ii, jj, kk = triangles[tt, :]
                uidx = [
                    x for x in np.unique([ii, jj, kk, iin, jjn, kkn])
                    if x not in [ii, jj, kk]
                ][0]
                neighbor_node.append(uidx)

            else:
                neighbors.append(None)
                neighbor_node.append(None)

        triangle_neighbors.append(neighbors)
        neighbors_node.append(neighbor_node)

    amesh.triangle_neighbors = triangle_neighbors
    amesh.neighbors_node = neighbors_node

    # construct constitutive matrix for material
    h = mem.thickness[0]  # no support for composite membranes yet
    E = mem.y_modulus[0]
    eta = mem.p_ratio[0]

    D = np.zeros((3, 3))
    D[0, 0] = 1
    D[0, 1] = eta
    D[1, 0] = eta
    D[1, 1] = 1
    D[2, 2] = (1 - eta)
    D = D * E * h**3 / (12 * (1 - eta**2))

    D2 = np.zeros((3, 3))
    D2[0, 0] = 1
    D2[0, 1] = eta
    D2[1, 0] = eta
    D2[1, 1] = 1
    D2[2, 2] = (1 - eta) / 2
    D2 = D2 * E * h**3 / (12 * (1 - eta**2))

    # calculate Jacobian and gradient operator for each triangle
    gradops = []
    for tt in range(ntriangles):
        tri = triangles[tt, :]
        xi, yi = nodes[tri[0], :2]
        xj, yj = nodes[tri[1], :2]
        xk, yk = nodes[tri[2], :2]

        J = np.array([[xj - xi, xk - xi], [yj - yi, yk - yi]])
        gradop = np.linalg.inv(J.T).dot([[-1, 1, 0], [-1, 0, 1]])

        gradops.append(gradop)

    # construct K matrix
    K = np.zeros((len(nodes), len(nodes)))

    for p in range(ntriangles):

        trip = triangles[p, :]
        ap = triangle_areas[p]

        # assign primary triangle nodes based on order of edges
        x5, y5 = nodes[trip[0], :2]
        x6, y6 = nodes[trip[1], :2]
        x4, y4 = nodes[trip[2], :2]

        r4 = np.array([x4, y4])
        r5 = np.array([x5, y5])
        r6 = np.array([x6, y6])

        # assign neighboring triangle nodes and add fictitious nodes if necessary
        neighbors = neighbors_node[p]

        if None in neighbors or ob[trip[0]] or ob[trip[1]] or ob[trip[2]]:
            # if True:

            xi, yi = nodes[trip[0], :2]
            xj, yj = nodes[trip[1], :2]
            xk, yk = nodes[trip[2], :2]

            neighbors = triangle_neighbors[p]
            gradp = gradops[p]
            # list triangle edges, ordered so that z cross-product will produce outward normal
            edges = [(xk, yk, xj, yj), (xi, yi, xk, yk), (xj, yj, xi, yi)]

            # begin putting together indexes needed later for matrix assignment
            ii, jj, kk = trip
            Kidx = [ii, jj, kk]
            Kpidx = [0, 1, 2]

            # construct B matrix for control element
            Bp = np.zeros((3, 6))
            for j, n in enumerate(neighbors):
                if n is None:
                    continue

                # determine index of the node in the neighbor opposite edge
                iin, jjn, kkn = triangles[n, :]
                uidx = [
                    x for x in np.unique([ii, jj, kk, iin, jjn, kkn])
                    if x not in [ii, jj, kk]
                ][0]
                # update indexes
                Kidx.append(uidx)
                Kpidx.append(3 + j)

                l = LL(*edges[j])
                T = T_matrix(*edges[j])
                gradn = gradops[n]

                pterm = l / 2 * T.dot(gradp)
                Bp[:, :3] += pterm

                nterm = l / 2 * T.dot(gradn)
                idx = [Kpidx[Kidx.index(x)] for x in [iin, jjn, kkn]]
                Bp[:, idx] += nterm
            Bp = Bp / ap

            # construct local K matrix for control element
            Kp = (Bp.T).dot(D).dot(Bp) * ap

            # add matrix values to global K matrix
            K[np.ix_(Kidx, Kidx)] += Kp[np.ix_(Kpidx, Kpidx)]

        else:
            x1, y1 = nodes[neighbors[0], :2]
            x2, y2 = nodes[neighbors[1], :2]
            x3, y3 = nodes[neighbors[2], :2]

            r1 = np.array([x1, y1])
            r2 = np.array([x2, y2])
            r3 = np.array([x3, y3])

            # construct C matrix
            C = np.zeros((6, 3))
            C[0, :] = [x1**2 / 2, y1**2 / 2, x1 * y1]
            C[1, :] = [x2**2 / 2, y2**2 / 2, x2 * y2]
            C[2, :] = [x3**2 / 2, y3**2 / 2, x3 * y3]
            C[3, :] = [x4**2 / 2, y4**2 / 2, x4 * y4]
            C[4, :] = [x5**2 / 2, y5**2 / 2, x5 * y5]
            C[5, :] = [x6**2 / 2, y6**2 / 2, x6 * y6]

            # construct L matrix

            # calculate vars based on geometry of first sub-element
            L1 = norm(r6, r4)
            b1a1 = (r1 - r4).dot(r6 - r4) / L1
            b1a2 = (r1 - r6).dot(r4 - r6) / L1
            b1b1 = (r5 - r4).dot(r6 - r4) / L1
            b1b2 = (r5 - r6).dot(r4 - r6) / L1
            h1a = np.sqrt(norm(r1, r4)**2 - b1a1**2)
            h1b = np.sqrt(norm(r5, r6)**2 - b1b2**2)

            # calculate vars based on geometry of second sub-element
            L2 = norm(r5, r4)
            b2a1 = (r2 - r5).dot(r4 - r5) / L2
            b2a2 = (r2 - r4).dot(r5 - r4) / L2
            b2b1 = (r6 - r5).dot(r4 - r5) / L2
            b2b2 = (r6 - r4).dot(r5 - r4) / L2
            h2a = np.sqrt(norm(r2, r5)**2 - b2a1**2)
            h2b = np.sqrt(norm(r6, r4)**2 - b2b2**2)

            # calculate vars based on geometry of third sub-element
            L3 = norm(r5, r6)
            b3a1 = (r3 - r6).dot(r5 - r6) / L3
            b3a2 = (r3 - r5).dot(r6 - r5) / L3
            b3b1 = (r4 - r6).dot(r5 - r6) / L3
            b3b2 = (r4 - r5).dot(r6 - r5) / L3
            h3a = np.sqrt(norm(r3, r6)**2 - b3a1**2)
            h3b = np.sqrt(norm(r4, r5)**2 - b3b2**2)

            L = np.zeros((3, 6))
            L[0, 0] = 1 / h1a
            L[1, 1] = 1 / h2a
            L[2, 2] = 1 / h3a
            L[0, 3] = -b1a2 / (L1 * h1a) + -b1b2 / (L1 * h1b)
            L[0, 4] = 1 / h1b
            L[0, 5] = -b1a1 / (L1 * h1a) + -b1b1 / (L1 * h1b)
            L[1, 3] = -b2b1 / (L2 * h2b) + -b2a1 / (L2 * h2a)
            L[1, 4] = -b2b2 / (L2 * h2b) + -b2a2 / (L2 * h2a)
            L[1, 5] = 1 / h2b
            L[2, 3] = 1 / h3b
            L[2, 4] = -b3a1 / (L3 * h3a) + -b3b1 / (L3 * h3b)
            L[2, 5] = -b3b2 / (L3 * h3b) + -b3a2 / (L3 * h3a)

            # calculate G matrix
            G = L @ C
            Ginv = np.linalg.inv(G)

            # create I_D matrix
            I_D = np.zeros((3, 3))
            I_D[0, 0] = 1
            I_D[1, 1] = 1
            I_D[2, 2] = 2

            # K_be = ap * (L.T).dot(Ginv.T).dot(I_D).dot(D).dot(Ginv).dot(L)
            K_be = ap * L.T @ Ginv.T @ I_D @ D @ Ginv @ L

            # begin putting together indexes needed later for matrix assignment
            K_idx = [
                neighbors[0], neighbors[1], neighbors[2], trip[2], trip[0],
                trip[1]
            ]
            K_be_idx = [0, 1, 2, 3, 4, 5]

            # add matrix values to global K matrix
            K[np.ix_(K_idx, K_idx)] += K_be[np.ix_(K_be_idx, K_be_idx)]

    K[ob, :] = 0
    K[:, ob] = 0
    K[ob, ob] = 1

    return K
Пример #12
0
def mem_k_matrix_hpb(mem, refn, retmesh=False):
    '''
    Stiffness matrix based on 3-dof hinged plate bending (HPB) elements.
    Refer to R. Phaal and C. R. Calladine, Int. J. Numer. Meth. Engng.,
    vol. 35, no. 5, pp. 955–977, (1992).
    '''
    def norm(r1, r2):
        # calculates edge length
        return np.sqrt((r2[0] - r1[0])**2 + (r2[1] - r1[1])**2)

    if isinstance(mem, abstract.SquareCmutMembrane):
        amesh = mesh.square(mem.length_x, mem.length_y, refn)
    else:
        amesh = mesh.circle(mem.radius, refn)

    # get mesh information
    nodes = amesh.vertices
    triangles = amesh.triangles
    triangle_edges = amesh.triangle_edges
    triangle_areas = amesh.triangle_areas
    ntriangles = len(triangles)
    ob = amesh.on_boundary

    # determine list of neighbors and neighbor nodes for each triangle
    # None indicates neighbor doesn't exist for that edge (boundary edge)
    triangle_neighbors = []
    neighbors_node = []

    for tt in range(ntriangles):

        neighbors = []
        neighbor_node = []
        for te in triangle_edges[tt, :]:

            mask = np.any(triangle_edges == te, axis=1)
            args = np.nonzero(mask)[0]

            if len(args) > 1:

                n = args[args != tt][0]
                neighbors.append(n)

                # determine index of the node in the neighbor opposite edge
                iin, jjn, kkn = triangles[n, :]
                ii, jj, kk = triangles[tt, :]
                uidx = [
                    x for x in np.unique([ii, jj, kk, iin, jjn, kkn])
                    if x not in [ii, jj, kk]
                ][0]
                neighbor_node.append(uidx)

            else:
                neighbors.append(None)
                neighbor_node.append(None)

        triangle_neighbors.append(neighbors)
        neighbors_node.append(neighbor_node)

    amesh.triangle_neighbors = triangle_neighbors
    amesh.neighbors_node = neighbors_node

    # construct constitutive matrix for material
    h = mem.thickness[0]  # no support for composite membranes yet
    E = mem.y_modulus[0]
    eta = mem.p_ratio[0]

    D = np.zeros((3, 3))
    D[0, 0] = 1
    D[0, 1] = eta
    D[1, 0] = eta
    D[1, 1] = 1
    D[2, 2] = (1 - eta)
    D = D * E * h**3 / (12 * (1 - eta**2))

    # construct K matrix
    K = np.zeros((len(nodes), len(nodes)))
    for p in range(ntriangles):

        trip = triangles[p, :]
        ap = triangle_areas[p]

        # assign primary triangle nodes based on order of edges
        x5, y5 = nodes[trip[0], :2]
        x6, y6 = nodes[trip[1], :2]
        x4, y4 = nodes[trip[2], :2]

        r4 = np.array([x4, y4])
        r5 = np.array([x5, y5])
        r6 = np.array([x6, y6])

        # assign neighboring triangle nodes and add fictitious nodes if necessary
        neighbors = neighbors_node[p]

        if neighbors[0] is None:
            x = r5
            xo = r6
            n = (r4 - r6) / norm(r4, r6)
            x1, y1 = -x + 2 * xo + 2 * (x - xo).dot(n) * n
        else:
            x1, y1 = nodes[neighbors[0], :2]

        if neighbors[1] is None:
            x = r6
            xo = r4
            n = (r5 - r4) / norm(r5, r4)
            x2, y2 = -x + 2 * xo + 2 * (x - xo).dot(n) * n
        else:
            x2, y2 = nodes[neighbors[1], :2]

        if neighbors[2] is None:
            x = r4
            xo = r5
            n = (r6 - r5) / norm(r6, r5)
            x3, y3 = -x + 2 * xo + 2 * (x - xo).dot(n) * n
        else:
            x3, y3 = nodes[neighbors[2], :2]

        r1 = np.array([x1, y1])
        r2 = np.array([x2, y2])
        r3 = np.array([x3, y3])

        # construct C matrix
        C = np.zeros((6, 3))
        C[0, :] = [x1**2 / 2, y1**2 / 2, x1 * y1]
        C[1, :] = [x2**2 / 2, y2**2 / 2, x2 * y2]
        C[2, :] = [x3**2 / 2, y3**2 / 2, x3 * y3]
        C[3, :] = [x4**2 / 2, y4**2 / 2, x4 * y4]
        C[4, :] = [x5**2 / 2, y5**2 / 2, x5 * y5]
        C[5, :] = [x6**2 / 2, y6**2 / 2, x6 * y6]

        # construct L matrix

        # calculate vars based on geometry of first sub-element
        L1 = norm(r6, r4)
        b1a1 = (r1 - r4).dot(r6 - r4) / L1
        b1a2 = (r1 - r6).dot(r4 - r6) / L1
        b1b1 = (r5 - r4).dot(r6 - r4) / L1
        b1b2 = (r5 - r6).dot(r4 - r6) / L1
        h1a = np.sqrt(norm(r1, r4)**2 - b1a1**2)
        h1b = np.sqrt(norm(r5, r6)**2 - b1b2**2)

        # calculate vars based on geometry of second sub-element
        L2 = norm(r5, r4)
        b2a1 = (r2 - r5).dot(r4 - r5) / L2
        b2a2 = (r2 - r4).dot(r5 - r4) / L2
        b2b1 = (r6 - r5).dot(r4 - r5) / L2
        b2b2 = (r6 - r4).dot(r5 - r4) / L2
        h2a = np.sqrt(norm(r2, r5)**2 - b2a1**2)
        h2b = np.sqrt(norm(r6, r4)**2 - b2b2**2)

        # calculate vars based on geometry of third sub-element
        L3 = norm(r5, r6)
        b3a1 = (r3 - r6).dot(r5 - r6) / L3
        b3a2 = (r3 - r5).dot(r6 - r5) / L3
        b3b1 = (r4 - r6).dot(r5 - r6) / L3
        b3b2 = (r4 - r5).dot(r6 - r5) / L3
        h3a = np.sqrt(norm(r3, r6)**2 - b3a1**2)
        h3b = np.sqrt(norm(r4, r5)**2 - b3b2**2)

        L = np.zeros((3, 6))
        L[0, 0] = 1 / h1a
        L[1, 1] = 1 / h2a
        L[2, 2] = 1 / h3a
        L[0, 3] = -b1a2 / (L1 * h1a) + -b1b2 / (L1 * h1b)
        L[0, 4] = 1 / h1b
        L[0, 5] = -b1a1 / (L1 * h1a) + -b1b1 / (L1 * h1b)
        L[1, 3] = -b2b1 / (L2 * h2b) + -b2a1 / (L2 * h2a)
        L[1, 4] = -b2b2 / (L2 * h2b) + -b2a2 / (L2 * h2a)
        L[1, 5] = 1 / h2b
        L[2, 3] = 1 / h3b
        L[2, 4] = -b3a1 / (L3 * h3a) + -b3b1 / (L3 * h3b)
        L[2, 5] = -b3b2 / (L3 * h3b) + -b3a2 / (L3 * h3a)

        # calculate G matrix
        G = L @ C
        Ginv = np.linalg.inv(G)

        # create I_D matrix
        I_D = np.zeros((3, 3))
        I_D[0, 0] = 1
        I_D[1, 1] = 1
        I_D[2, 2] = 2

        # K_be = ap * (L.T).dot(Ginv.T).dot(I_D).dot(D).dot(Ginv).dot(L)
        K_be = ap * L.T @ Ginv.T @ I_D @ D @ Ginv @ L

        # begin putting together indexes needed later for matrix assignment
        K_idx = [trip[2], trip[0], trip[1]]
        K_be_idx = [3, 4, 5]

        # apply BCs
        if neighbors[0] is None:
            # fictitious node index = 0
            # mirrored node index = 4
            # boundary nodes index = 3, 5
            # non-boundary nodes index = 1, 2

            # modify row for mirrored node
            K_be[4,
                 4] = (K_be[0, 0] + K_be[0, 4] + K_be[4, 0] + K_be[4, 4])  #/ 2
            K_be[4, 1] = (K_be[4, 1] + K_be[0, 1])  #/ 2
            K_be[4, 2] = (K_be[4, 2] + K_be[0, 2])  #/ 2
            # K_be[4, 1] /= 2
            # K_be[1, 4] /= 2
            # K_be[4, 2] /= 2
            # K_be[2, 4] /= 2
            # K_be[4, 3] = (K_be[4, 3] + K_be[0, 3]) / 2
            # K_be[4, 5] = (K_be[4, 5] + K_be[0, 5]) / 2

            # modify row of first non-boundary node
            K_be[1, 4] = (K_be[1, 4] + K_be[1, 0])  #/ 2

            # modify row of second non-boundary node
            K_be[2, 4] = (K_be[2, 4] + K_be[2, 0])  #/ 2

            # modify row of first boundary node
            # K_be[3, 3] = K_be[3, 3] / 2
            # K_be[3, 4] = (K_be[3, 4] + K_be[3, 0]) / 2

            # modify row of second boundary node
            # K_be[5, 5] = K_be[5, 5] / 2
            # K_be[5, 4] = (K_be[5, 4] + K_be[5, 0]) / 2
        else:

            K_idx.append(neighbors[0])
            K_be_idx.append(0)

        if neighbors[1] is None:
            # fictitious node index = 1
            # mirrored node index = 5
            # boundary nodes index = 3, 4
            # non-boundary nodes index = 0, 2

            # modify row for mirrored node
            K_be[5,
                 5] = (K_be[1, 1] + K_be[1, 5] + K_be[5, 1] + K_be[5, 5])  #/ 2
            K_be[5, 0] = (K_be[5, 0] + K_be[1, 0])  #/ 2
            K_be[5, 2] = (K_be[5, 2] + K_be[1, 2])  #/ 2
            # K_be[5, 0] /= 2
            # K_be[0, 5] /= 2
            # K_be[5, 2] /= 2
            # K_be[2, 5] /= 2
            # K_be[5, 3] = (K_be[5, 3] + K_be[1, 3]) / 2
            # K_be[5, 4] = (K_be[5, 4] + K_be[1, 4]) / 2

            # modify row of first non-boundary node
            K_be[0, 5] = (K_be[0, 5] + K_be[0, 1])  #/ 2

            # modify row of second non-boundary node
            K_be[2, 5] = (K_be[2, 5] + K_be[2, 1])  #/ 2

            # modify row of first boundary node
            # K_be[3, 3] = K_be[3, 3] / 2
            # K_be[3, 5] = (K_be[3, 5] + K_be[3, 1]) / 2

            # modify row of second boundary node
            # K_be[4, 4] = K_be[4, 4] / 2
            # K_be[4, 5] = (K_be[4, 5] + K_be[4, 1]) / 2
        else:

            K_idx.append(neighbors[1])
            K_be_idx.append(1)

        if neighbors[2] is None:
            # fictitious node index = 2
            # mirrored node index = 3
            # boundary nodes index = 4, 5
            # non-boundary nodes index = 0, 1

            # modify row for mirrored node
            K_be[3,
                 3] = (K_be[2, 2] + K_be[2, 3] + K_be[3, 2] + K_be[3, 3])  #/ 2
            K_be[3, 0] = (K_be[3, 0] + K_be[2, 0])  #/ 2
            K_be[3, 1] = (K_be[3, 1] + K_be[2, 1])  #/ 2
            # K_be[3, 0] /= 2
            # K_be[0, 3] /= 2
            # K_be[3, 1] /= 2
            # K_be[1, 3] /= 2
            # K_be[3, 4] = (K_be[3, 4] + K_be[2, 4]) / 2
            # K_be[3, 5] = (K_be[3, 5] + K_be[2, 5]) / 2

            # modify row of first non-boundary node
            K_be[0, 3] = (K_be[0, 3] + K_be[0, 2])  #/ 2

            # modify row of second non-boundary node
            K_be[1, 3] = (K_be[1, 3] + K_be[1, 2])  #/ 2

            # modify row of first boundary node
            # K_be[4, 4] = K_be[4, 4] / 2
            # K_be[4, 3] = (K_be[4, 3] + K_be[4, 2]) / 2

            # modify row of second boundary node
            # K_be[5, 5] = K_be[5, 5] / 2
            # K_be[5, 3] = (K_be[5, 3] + K_be[5, 2]) / 2
        else:

            K_idx.append(neighbors[2])
            K_be_idx.append(2)

        # add matrix values to global K matrix
        K[np.ix_(K_idx, K_idx)] += K_be[np.ix_(K_be_idx, K_be_idx)]

    K[ob, :] = 0
    K[:, ob] = 0
    K[ob, ob] = 1

    return K
Пример #13
0
def mem_patch_avg_matrix(mem, refn):
    '''
    Averaging vector for a patch.
    '''
    if isinstance(mem, abstract.SquareCmutMembrane):
        amesh = mesh.square(mem.length_x,
                            mem.length_y,
                            refn,
                            center=mem.position)
    else:
        amesh = mesh.circle(mem.radius, refn, center=mem.position)

    nodes = amesh.vertices
    triangles = amesh.triangles
    triangle_areas = amesh.triangle_areas
    # ob = amesh.on_boundary

    avg = []
    for pat in mem.patches:
        if isinstance(mem, abstract.SquareCmutMembrane):

            px, py, pz = pat.position
            plx = pat.length_x
            ply = pat.length_y

            def load_func(x, y):
                # use 2 * eps to account for potential round-off error
                if x - (px - plx / 2) >= -2 * eps:
                    if x - (px + plx / 2) <= 2 * eps:
                        if y - (py - ply / 2) >= -2 * eps:
                            if y - (py + ply / 2) <= 2 * eps:
                                return 1
                return 0
        else:
            px, py, pz = pat.position
            prmin = pat.radius_min
            prmax = pat.radius_max
            pthmin = pat.theta_min
            pthmax = pat.theta_max

            def load_func(x, y):
                r = np.sqrt((x - px)**2 + (y - py)**2)
                th = np.arctan2((y - py), (x - px))
                # pertube theta by 2 * eps to account for potential round-off error
                th1 = th - 2 * eps
                if th1 < -np.pi:
                    th1 += 2 * np.pi  # account for [-pi, pi] wrap-around
                th2 = th + 2 * eps
                if th2 > np.pi:
                    th2 -= 2 * np.pi  # account for [-pi, pi] wrap-around
                if r - prmin >= -2 * eps:
                    if r - prmax <= 2 * eps:
                        # for theta, check both perturbed values
                        if th1 - pthmin >= 0:
                            if th1 - pthmax <= 0:
                                return 1
                        if th2 - pthmin >= 0:
                            if th2 - pthmax <= 0:
                                return 1
                return 0

        avg_pat = np.zeros(len(nodes))
        for tt in range(len(triangles)):
            tri = triangles[tt, :]
            xi, yi = nodes[tri[0], :2]
            xj, yj = nodes[tri[1], :2]
            xk, yk = nodes[tri[2], :2]

            # check if triangle vertices are inside or outside load
            loadi = load_func(xi, yi)
            loadj = load_func(xj, yj)
            loadk = load_func(xk, yk)
            # if load covers entire triangle
            if all([loadi, loadj, loadk]):
                da = triangle_areas[tt]

                avg_pat[tri] += 1 / 3 * 1 * da
            # if load does not cover any part of triangle
            elif not any([loadi, loadj, loadk]):
                continue
            # if load partially covers triangle
            else:

                def load_func_psi_eta(psi, eta):
                    x = (xj - xi) * psi + (xk - xi) * eta + xi
                    y = (yj - yi) * psi + (yk - yi) * eta + yi
                    return load_func(x, y)

                integ, _ = dblquad(load_func_psi_eta,
                                   0,
                                   1,
                                   0,
                                   lambda x: 1 - x,
                                   epsrel=1e-1,
                                   epsabs=1e-1)
                frac = integ / (1 / 2)  # fraction of triangle covered by load
                da = triangle_areas[tt]

                avg_pat[tri] += 1 / 3 * frac * da

        avg.append(avg_pat / pat.area)

    return np.array(avg).T