Beispiel #1
0
def create_f_vector(x, y, simplices, c=2):
    triangles = mesh.all_triangles(simplices, x, y)
    f = np.zeros(x.shape)
    areas = calc_areas(triangles)
    for n, simp in enumerate(simplices):
        f[simp] += -c*areas[n]/3
    return f
Beispiel #2
0
def assemble_global_matrix(x, y, simplices, func_elem, d=2, elem_dict=dict()):
    """
    Assembles the global matrix K for a FEM system.
    Arguments:
    - x, y: (n,) array of vertex positions
    - simplices: (n, 3) array of vertex numbers that make up each element
    - func_elem: function that calculates the element matrix for a given
                 element, must have signature: func_elem(tri, area, **kwargs)
    - d: dimensionality of the output space (ie, scalar field or vector field)
    - elem_dict: keyword arguments passed to func_elem

    Returns:
    - K: (nd, nd) Global matrix for the FEM system
    """
    N = x.size
    K = np.zeros((N * d, N * d))
    triangles = mesh.all_triangles(simplices, x, y)
    areas = calc_areas(triangles)
    indices = element_to_global(simplices, d)

    for tri, area, ind in zip(triangles, areas, indices):
        el = func_elem(tri, area, **elem_dict)
        if not np.allclose(el, el.T):
            print('Element matrix not symmetric')
            print(el)

        x, y = ind
        K[x, y] += el
    return K
Beispiel #3
0
def assemble_global_matrix(x, y, simplices):
    K = np.zeros((x.size, x.size))

    triangles = mesh.all_triangles(simplices, x, y)
    elements = create_element_matrix(triangles)
    indices = get_global_indices(simplices)
    for el, ind in zip(elements, indices):
        x, y = ind
        K[x, y] += el
    return K
Beispiel #4
0
def ex_with_external():
    x, y, simplices = load_mat('data.mat')

    # Clamp the left side
    mask1 = x == np.amin(x)
    vals1 = 0

    # Get the element matric keyword arguments
    elem_dict = dict(D=STEEL_D)

    # We apply a downwards nodal force on the right side
    bottom_force = -5e7
    mask2 = x == np.amax(x)
    A_n = calc_hat_area(y[mask2])
    vals2 = np.zeros((A_n.size, 2))
    vals2[:, 1] = A_n * bottom_force

    x_displacement, y_displacement = fem.FEM(x,
                                             y,
                                             simplices,
                                             create_element_matrix,
                                             func_source,
                                             mask1,
                                             vals1,
                                             mask2,
                                             vals2,
                                             elem_dict=elem_dict)
    x_new = x + x_displacement
    y_new = y + y_displacement
    fig, ax = plt.subplots()
    ax.triplot(x, y, simplices, color='r')
    ax.triplot(x_new, y_new, simplices, color='b')

    triangles_before = mesh.all_triangles(simplices, x, y)
    triangles_after = mesh.all_triangles(simplices, x_new, y_new)
    area_before = np.sum(fem.calc_areas(triangles_before))
    area_after = np.sum(fem.calc_areas(triangles_after))
    print(area_before)
    print(area_after)
    plt.show()
Beispiel #5
0
def ex_squares():
    poissons = [0.1, 0.3, 0.5]
    max_areas = [0.5, 0.1, 0.01]
    contour = [np.array([[0, 0], [6, 0], [6, 2], [0, 2]])]
    save_file = 'squares'
    poissons2, max_areas2 = np.meshgrid(poissons, max_areas)

    num = 100
    min_load = 0
    max_load = -5e7
    areas = np.zeros((num, *poissons2.shape))
    loads = np.linspace(min_load, max_load, num=num)
    bar = Bar('simulating', max=areas.size)
    for i in range(poissons2.shape[0]):
        for j in range(poissons2.shape[1]):
            x, y, simplices = mesh.generate_and_import_mesh(
                contour, max_area=max_areas2[i, j])
            elem_dict = dict(D=D(nu=poissons2[i, j]))
            mask1 = x == np.amin(x)
            vals1 = 0
            # We apply a downwards nodal force on the right side
            mask2 = x == np.amax(x)
            A_n = calc_hat_area(y[mask2])
            vals2 = np.zeros((A_n.size, 2))
            vals2[:, 1] = A_n

            K = fem.assemble_global_matrix(x,
                                           y,
                                           simplices,
                                           create_element_matrix,
                                           elem_dict=elem_dict)
            f = fem.create_f_vector(x, y, simplices, func_source)
            K, f = fem.add_point_boundary(K, f, mask1, vals1)
            # bar = Bar('Simulating', max=num)
            for n, load in enumerate(loads):
                vals = vals2 * load
                f = fem.create_f_vector(x, y, simplices, func_source)
                f = fem.add_to_source(f, mask2, vals)
                u = np.linalg.solve(K, f)
                x_disp, y_disp = fem.unpack_u(u)
                x_new = x + x_disp
                y_new = y + y_disp
                triangles = mesh.all_triangles(simplices, x_new, y_new)
                area = np.sum(fem.calc_areas(triangles))
                areas[n, i, j] = area
                np.save(save_file, areas)
                bar.next()
    bar.finish()
Beispiel #6
0
def ex_resolution(max_max_area=2, min_max_area=0.01, num=50):
    contour = [np.array([[0, 0], [6, 0], [6, 2], [0, 2]])]
    end_area = np.zeros(num)
    end_positions = np.zeros((num, 2))
    bottom_traction = -5e7
    max_areas = np.logspace(np.log2(max_max_area),
                            np.log2(min_max_area),
                            num=num,
                            base=2)
    savefile = 'areas'
    savefile2 = 'positions'
    bar = Bar('Simulate', max=num)
    for i, area in enumerate(max_areas):
        x, y, simplices = mesh.generate_and_import_mesh(contour, max_area=area)
        bottom_right_index = (x == np.amax(x)) * (y == np.amin(y))
        mask1 = x == np.amin(x)
        vals1 = 0

        # Get the element matric keyword arguments
        elem_dict = dict(D=STEEL_D)

        # We apply a downwards nodal force on the right side
        mask2 = x == np.amax(x)
        A_n = calc_hat_area(y[mask2])
        vals2 = np.zeros((A_n.size, 2))
        vals2[:, 1] = A_n * bottom_traction

        x_displacement, y_displacement = fem.FEM(x,
                                                 y,
                                                 simplices,
                                                 create_element_matrix,
                                                 func_source,
                                                 mask1,
                                                 vals1,
                                                 mask2,
                                                 vals2,
                                                 elem_dict=elem_dict)
        x_new = x + x_displacement
        y_new = y + y_displacement
        triangles_after = mesh.all_triangles(simplices, x_new, y_new)
        end_area[i] = np.sum(fem.calc_areas(triangles_after))
        end_positions[i, :] = [
            x_new[bottom_right_index], y_new[bottom_right_index]
        ]
        np.save(savefile, end_area)
        np.save(savefile2, end_positions)
        bar.next()
    bar.finish()
Beispiel #7
0
def calc_cv_areas(x, y, simplices):
    """
    Calculates the nodal "area" - the area of each control volume, for a median
    dual vertex centred control volume.
    """

    # For this type of control volume, each triangular element is split up into
    # three parts of equal size. The size of the control volume is then the
    # total area of all triangles the vertex is a part of, divided by 3.
    m = np.zeros(x.shape)
    triangles = mesh.all_triangles(simplices, x, y)
    areas = mesh.calc_areas(triangles)
    for i in range(x.size):
        neighbours = mesh.find_neighbouring_simplices(simplices, i)
        m[i] = np.sum(areas[neighbours]) / 3
    return m
Beispiel #8
0
def ex_load(min_load=0, max_load=-5e7, num=100, max_area=0.01):
    contour = [np.array([[0, 0], [6, 0], [6, 2], [0, 2]])]
    x, y, simplices = mesh.generate_and_import_mesh(contour, max_area=max_area)

    start_area = 12
    savefile = 'loads'
    areas = np.zeros(num)
    loads = np.linspace(min_load, max_load, num=num)

    mask1 = x == np.amin(x)
    vals1 = 0

    # Get the element matric keyword arguments
    elem_dict = dict(D=STEEL_D)

    # We apply a downwards nodal force on the right side
    mask2 = x == np.amax(x)
    A_n = calc_hat_area(y[mask2])
    vals2 = np.zeros((A_n.size, 2))
    vals2[:, 1] = A_n

    K = fem.assemble_global_matrix(x,
                                   y,
                                   simplices,
                                   create_element_matrix,
                                   elem_dict=elem_dict)
    f = fem.create_f_vector(x, y, simplices, func_source)
    K, f = fem.add_point_boundary(K, f, mask1, vals1)
    # bar = Bar('Simulating', max=num)
    for i, load in enumerate(loads):
        vals = vals2 * load
        f = fem.create_f_vector(x, y, simplices, func_source)
        f = fem.add_to_source(f, mask2, vals)
        u = np.linalg.solve(K, f)
        x_disp, y_disp = fem.unpack_u(u)
        x_new = x + x_disp
        y_new = y + y_disp
        triangles = mesh.all_triangles(simplices, x_new, y_new)
        area = np.sum(fem.calc_areas(triangles))
        areas[i] = area
        np.save(savefile, [loads, areas])
Beispiel #9
0
def create_f_vector(x, y, simplices, func_source, d=2, source_dict=dict()):
    """
    Creates the source vector f for the FEM system
    Arguments:
    - x, y: (n,) array of vertex positions
    - simplices: (n, 3) array of vertex numbers that make up each element
    - func_source: function that calculates the element matrix for a given
                   element, must have signature:
                   func_source(tri, **kwargs)
    - d: dimensionality of the output space (ie, scalar field or vector field)
    - source_dict: keyword arguments passed to func_source

    Returns:
    - f: (nd, ) Source term vector for the system
    """
    triangles = mesh.all_triangles(simplices, x, y)
    f = np.zeros(d * x.size)
    for tri, simplex in zip(triangles, simplices):
        ind = get_global_indices(simplex, d)
        f_tri = func_source(tri, **source_dict)
        f[ind] += f_tri
    return f
Beispiel #10
0
def ex_show_loads():
    contour = [np.array([[0, 0], [6, 0], [6, 2], [0, 2]])]
    x, y, simplices = mesh.generate_and_import_mesh(contour, max_area=0.01)

    load = -5e7

    mask1 = x == np.amin(x)
    vals1 = 0

    # Get the element matric keyword arguments
    elem_dict = dict(D=STEEL_D)

    # We apply a downwards nodal force on the right side
    mask2 = x == np.amax(x)
    A_n = calc_hat_area(y[mask2])
    vals2 = np.zeros((A_n.size, 2))
    vals2[:, 1] = A_n

    K = fem.assemble_global_matrix(x,
                                   y,
                                   simplices,
                                   create_element_matrix,
                                   elem_dict=elem_dict)
    f = fem.create_f_vector(x, y, simplices, func_source)
    K, f = fem.add_point_boundary(K, f, mask1, vals1)
    vals = vals2 * load
    f = fem.add_to_source(f, mask2, vals)
    u = np.linalg.solve(K, f)
    x_disp, y_disp = fem.unpack_u(u)
    x_new = x + x_disp
    y_new = y + y_disp
    fig, ax = plt.subplots()
    ax.triplot(x, y, simplices)
    ax.triplot(x_new, y_new, simplices)
    triangles = mesh.all_triangles(simplices, x_new, y_new)
    area = np.sum(fem.calc_areas(triangles))
    print(area)
    plt.show()
Beispiel #11
0
def ex_debug():
    X = np.array((0, 1, 0.5))
    Y = np.array((0, 0, np.sqrt(3) / 2))
    T = np.array((0, 1, 2)).reshape((1, 3))
    I = np.array((X.sum(), Y.sum())) / 3
    tris = mesh.all_triangles(T, X, Y)
    area = mesh.calc_areas(tris)
    a = 1.1
    i = a * I
    x = a * X
    y = a * Y

    v = I - i
    x = x + v[0]
    y = y + v[1]
    i = i + v

    X2 = X - I[1]
    Y2 = Y - I[1]
    # lims = np.array(((np.amin(x), np.amax(x)), (np.amin(y), np.amax(y))))

    E, nu = 1e3, 0.3
    rho = 10

    b = np.zeros(2)
    t = b
    mask = np.zeros(3) == 1
    bmask = ~mask
    cvs = None

    De0inv, m, f_ext, ft = proj.calc_intial_stuff(X, Y, T, b, rho, mask, t)
    m = m.reshape((3, 1))
    lambda_, mu = proj.calc_lame_parameters(E, nu)
    fe = proj.calc_all_fe(x, y, T, cvs, De0inv, lambda_, mu)

    k = 1 / 2 * (a * a + 1) * (lambda_ + mu)

    T0 = 1
    K = 5e-3
    dt = K * np.sqrt(rho / E)
    N = np.ceil(T0 / dt).astype(int)
    v = np.zeros((3, 2))
    p = np.array((x, y)).T
    points = np.zeros((N, 3, 2))
    E_kin = np.zeros(N)
    E_pot = np.zeros(N)
    E_str = np.zeros(N)
    E_kin[0] = proj.calc_kin_energy(m, v)
    E_pot[0] = calc_pot_energy_tri(np.array((x[0], y[0])), np.zeros(2), k)
    E_pot[0] = proj.calc_pot_energy(m, y)
    E_str[0] = proj.calc_strain_energy(x, y, T, De0inv, lambda_, mu, area)
    points[0] = p
    fes = np.zeros((N, 3, 2))
    bar = Bar('Simulating', max=N)
    bar.next()
    for n in range(1, N):
        x, y = points[n - 1].T
        fe = proj.calc_all_fe(x, y, T, cvs, De0inv, lambda_, mu)
        f_total = fe
        fes[n] = f_total
        points[n], v = proj.calc_next_time_step(points[n - 1], v, m, f_total,
                                                dt, bmask)
        E_kin[n] = proj.calc_kin_energy(m, v)
        x, y = points[n].T
        E_str[n] = proj.calc_strain_energy(x, y, T, De0inv, lambda_, mu, area)
        E_pot[n] = calc_pot_energy_tri(np.array((x[0], y[0])), np.zeros(2), k)
        E_pot[n] = proj.calc_pot_energy(m, y)
        bar.next()
    bar.finish()

    N_frames = 500
    if N < N_frames:
        frame_skip = 1
    else:
        frame_skip = np.floor(N / N_frames).astype(int)
    x_all = points[:, :, 0].flatten()
    y_all = points[:, :, 1].flatten()
    xmax = np.amax(x_all)
    xmin = np.amin(x_all)
    ymin = np.amin(y_all)
    ymax = np.amax(y_all)
    limits = np.array(((xmin, xmax), (ymin, ymax)))
    outfile = 'triangle4.mp4'
    dpi = 200

    fps = 60
    padding = 0.2
    xlims, ylims = limits
    padding = np.array((-padding, padding))
    xlims = xlims + padding
    ylims = ylims + padding
    Times = np.cumsum(dt * np.ones(N))
    fig, (ax, ax2) = plt.subplots(nrows=2,
                                  gridspec_kw={'height_ratios': [2, 1]})
    ax2.plot(Times, E_kin + E_pot + E_str, label='$E_{total}$')
    ax2.plot(Times, E_pot, label='$E_{pot}$')
    ax2.plot(Times, E_kin, label='$E_{kin}$')
    ax2.plot(Times, E_str, label='$E_{str}$')
    ax2.legend()
    plt.show()
Beispiel #12
0
def simulate(x,
             y,
             simplices,
             cvs,
             dt=1,
             N=10,
             lambda_=1,
             mu=1,
             b=np.zeros(2),
             t=np.array((0, -1)),
             rho=1,
             t_mask=None,
             boundary_mask=None,
             y0=None,
             T_stopt=None):
    """
    Simulates the system

    Inputs:
    - x, y: (n,) arrays of nodal positions
    - simlices: (m,3) connectivity matrix
    - cvs: n-list of control volumes
    - dt: float, time step
    - N: total steps in the simulation. The initial position is the first step,
         so only N-1 steps are simulated
    - lambda_, mu: Lame parameters
    - b: body force density
    - t: traction
    - rho: mass density of the system
    - t_mask: (n,) boolean array of vertices to apply traction to. if None,
              apply traction to right boundary
    - boundary_mask: (n,) boolean array of nodes to update, ie NOT the clamped
                     boundary. If None, clamp left edge, ie. False on left edge
    
    Outputs:
    - points_t: (N,n,2) array of vertex positions for each step.
    """
    if boundary_mask is None:
        boundary_mask = x != np.amin(x)
    if t_mask is None:
        t_mask = x == np.amax(x)
    n_p = -1
    points = np.array((x, y)).T
    areas = mesh.calc_areas(mesh.all_triangles(simplices, x, y))
    v = np.zeros(points.shape)
    points_t = np.zeros((N, *points.shape))
    E_kin = np.zeros(N)
    E_pot = np.zeros(N)
    E_str = np.zeros(N)
    momentum = np.zeros((N, 2))

    De0inv, m, f_ext, ft = calc_intial_stuff(x, y, simplices, b, rho, t_mask,
                                             t)
    m = m.reshape((m.size, 1))
    points_t[0] = points
    h = y0 if y0 is not None else 0
    E_kin[0] = calc_kin_energy(m, v)
    E_pot[0] = calc_pot_energy(m, y, h)
    E_str[0] = calc_strain_energy(x, y, simplices, De0inv, lambda_, mu, areas)
    momentum[0] = calc_momentum(m, v)
    bar = Bar('simulating', max=N)
    bar.next()
    for n in range(1, N):
        if y0 is not None:
            points_t[n - 1], v = floor_(points_t[n - 1], y0=y0, v=v)
        T = dt * n
        if T_stopt <= T and T_stopt is not None:
            # print('stop T')
            ft = 0
        x, y = points_t[n - 1].T
        fe = calc_all_fe(x,
                         y,
                         simplices,
                         cvs,
                         De0inv,
                         lambda_,
                         mu,
                         n=True if n == n_p else False)
        f_total = ft + fe + f_ext
        points_t[n], v = calc_next_time_step(points_t[n - 1], v, m, f_total,
                                             dt, boundary_mask)
        momentum[n] = calc_momentum(m, v)
        E_kin[n] = calc_kin_energy(m, v)
        x, y = points_t[n].T
        E_pot[n] = calc_pot_energy(m, y, h)
        E_str[n] = calc_strain_energy(x, y, simplices, De0inv, lambda_, mu,
                                      areas)
        bar.next()
    bar.finish()
    return points_t, E_pot, E_kin, E_str, momentum
Beispiel #13
0
def create_control_volumes(x, y, simplices):
    def _calc_for_control(ox, oy, dx, dy):
        # Calculate stuff for create_control_volumes
        l = np.sqrt((dx-ox)**2 + (dy-oy)**2)
        ex = (dx - ox)/l
        ey = (dy - oy)/l
        nx = -ey
        ny = ex
        mx = (dx + ox)/2
        my = (dy + oy)/2
        return l, ex, ey, nx, ny, mx, my
    
    N = x.size
    triangles = mesh.all_triangles(simplices, x, y)
    incenters = calc_incenters(triangles)
    cx, cy = incenters.T
    points = np.array((x,y))
    hull = spatial.ConvexHull(points.T)
    hull_vertices = hull.vertices
    boundary_mask = np.zeros(N)
    boundary_mask[hull_vertices] = 1
    cvs = []

    for i in range(N):
        indices = mesh.find_neighbouring_simplices(simplices, i)
        K = indices.size
        I =    []
        N =    []
        OX =   []
        OY =   []
        DX =   []
        DY =   []
        L  =   []
        EX =   []
        EY =   []
        NX =   []
        NY =   []
        MX =   []
        MY =   []
        code = []

        if boundary_mask[i]:
            a = simplices[indices[0], 0]
            b = simplices[indices[0], 1]
            c = simplices[indices[0], 2]
            ii, jj, kk = find_vertex_order(i, a, b, c)
            ox = x[i]
            oy = y[i]
            dx, dy = project_to_edge(ox, oy, x[jj], y[jj], cx[indices[0]],
                                     cy[indices[0]])
            print(f'ox: {ox:.2f}, oy: {oy:.2f}')
            print(f'dx: {dx:.2f}, dy: {dy:.2f}')
            l, ex, ey, nx, ny, mx, my = _calc_for_control(ox, oy, dx, dy)

            I.append(i)
            N.append(-1)
            OX.append(ox)
            OY.append(oy)
            DX.append(dx)
            DY.append(dy)
            L.append(l)
            EX.append(ex)
            EY.append(ey)
            NX.append(-nx)
            NY.append(-ny)
            MX.append(mx)
            MY.append(my)
            code.append(2)

            ox = dx
            oy = dy
            dx = cx[indices[0]]
            dy = cy[indices[0]]
            l, ex, ey, nx, ny, mx, my = _calc_for_control(ox, oy, dx, dy)

            I.append(i)
            N.append(jj)
            OX.append(ox)
            OY.append(oy)
            DX.append(dx)
            DY.append(dy)
            L.append(l)
            EX.append(ex)
            EY.append(ey)
            NX.append(-nx)
            NY.append(-ny)
            MX.append(mx)
            MY.append(my)
            code.append(1)

        lastK = K-1 if boundary_mask[i] else K

        for j in range(lastK):
            a = simplices[indices[j], 0]
            b = simplices[indices[j], 1]
            c = simplices[indices[j], 2]
            ii, jj, kk = find_vertex_order(i, a, b, c)

            # Origin vertex index
            o = indices[j]

            # Destination vertex index
            d = indices[(j+1)%K]
            # print(f'j: {j}, j mod: {(j+1)%K}')
            ox = cx[o]
            oy = cy[o]
            dx = cx[d]
            dy = cy[d]
            l, ex, ey, nx, ny, mx, my = _calc_for_control(ox, oy, dx, dy)
            I.append(i)
            N.append(kk)
            OX.append(ox)
            OY.append(oy)
            DX.append(dx)
            DY.append(dy)
            L.append(l)
            EX.append(ex)
            EY.append(ey)
            NX.append(-nx)
            NY.append(-ny)
            MX.append(mx)
            MY.append(my)
            code.append(0)

        
        if boundary_mask[i]:
            # index -1 corresponds to "K" in matlab code
            a = simplices[indices[-1], 0]
            b = simplices[indices[-1], 1]
            c = simplices[indices[-1], 2]
            ii, jj, kk = find_vertex_order(i, a, b, c)

            ox = cx[indices[-1]]
            oy = cy[indices[-1]]
            dx, dy = project_to_edge(x[kk], y[kk], x[i], y[i], ox, oy)
            l, ex, ey, nx, ny, mx, my = _calc_for_control(ox, oy, dx, dy)
            I.append(i)
            N.append(kk)
            OX.append(ox)
            OY.append(oy)
            DX.append(dx)
            DY.append(dy)
            L.append(l)
            EX.append(ex)
            EY.append(ey)
            NX.append(-nx)
            NY.append(-ny)
            MX.append(mx)
            MY.append(my)
            code.append(1)

            ox = dx
            ox = dy
            dx = x[i]
            dy = y[i]
            l, ex, ey, nx, ny, mx, my = _calc_for_control(ox, oy, dx, dy)
            I.append(i)
            N.append(-1)
            OX.append(ox)
            OY.append(oy)
            DX.append(dx)
            DY.append(dy)
            L.append(l)
            EX.append(ex)
            EY.append(ey)
            NX.append(-nx)
            NY.append(-ny)
            MX.append(mx)
            MY.append(my)
            code.append(2)
        cv = {'I':np.array(I),
              'N':np.array(N),
              'ox':np.array(OX),
              'oy':np.array(OY),
              'dx':np.array(DX),
              'dy':np.array(DY),
              'l':np.array(L),
              'ex':np.array(EX),
              'ey':np.array(EY),
              'nx':np.array(NX),
              'ny':np.array(NY),
              'mx':np.array(MX),
              'my':np.array(MY),
              'code':np.array(code)}
        cvs.append(cv)
    return cvs
Beispiel #14
0
def calc_phi_on_centroids(x, y, simplices, phi):
    triangles = mesh.all_triangles(simplices, x, y)
    phi_tri = phi[simplices]
    phi_tri_avg = np.mean(phi_tri, axis=1)
    return triangles, phi_tri_avg