示例#1
0
def form_identify_dof(form):
    r"""Identify the DOF of a form diagram.

    Parameters
    ----------
    form : FormDiagram
        The form diagram.

    Returns
    -------
    k : int
        Dimension of the null space (nullity) of the equilibrium matrix.
        Number of independent states of self-stress.
    m : int
        Size of the left null space of the equilibrium matrix.
        Number of (infenitesimal) mechanisms.
    ind : list
        Indices of the independent edges.

    Notes
    -----
    The equilibrium matrix of the form diagram is

    .. math::

        \mathbf{E}
        =
        \begin{bmatrix}
        \mathbf{C}_{i}^{t}\mathbf{U} \\
        \mathbf{C}_{i}^{t}\mathbf{V}
        \end{bmatrix}


    If ``k == 0`` and ``m == 0``, the system described by the equilibrium matrix
    is statically determined.
    If ``k > 0`` and ``m == 0``, the system is statically indetermined with `k`
    idependent states of stress.
    If ``k == 0`` asnd ``m > 0``, the system is unstable, with `m` independent
    mechanisms.

    The dimension of a vector space (such as the null space) is the number of
    vectors of a basis of that vector space. A set of vectors forms a basis of a
    vector space if they are linearly independent vectors and every vector of the
    space is a linear combination of this set.

    Examples
    --------
    >>>
    """
    k_i = form.key_index()
    xy = form.vertices_attributes('xy')
    fixed = [k_i[key] for key in form.fixed()]
    free = list(set(range(len(form.vertex))) - set(fixed))
    edges = [(k_i[u], k_i[v]) for u, v in form.edges()]
    C = connectivity_matrix(edges)
    E = equilibrium_matrix(C, xy, free)
    k, m = dof(E)
    ind = nonpivots(rref(E))
    return k, m, [edges[i] for i in ind]
示例#2
0
def form_count_dof(form):
    k2i = form.key_index()
    xyz = form.vertices_attributes('xyz')
    fixed = [k2i[key] for key in form.anchors()]
    free = list(set(range(form.number_of_vertices())) - set(fixed))
    edges = [(k2i[u], k2i[v]) for u, v in form.edges_where({'_is_edge': True})]
    C = connectivity_matrix(edges)
    E = equilibrium_matrix(C, xyz, free)
    return dof(E)
示例#3
0
def count_dof(form):
    k2i = form.key_index()
    xyz = form.xyz()
    fixed = form.anchors(k2i=k2i)
    free = list(set(range(form.number_of_vertices())) - set(fixed))
    edges = [(k2i[u], k2i[v]) for u, v in form.edges_where({'is_edge': True})]
    C = connectivity_matrix(edges)
    E = equilibrium_matrix(C, xyz, free)
    return dof(E)
示例#4
0
def form_identify_dof(form, **kwargs):
    algo = kwargs.get('algo') or 'sympy'
    k2i = form.key_index()
    xyz = form.vertices_attributes('xyz')
    fixed = [k2i[key] for key in form.anchors()]
    free = list(set(range(form.number_of_vertices())) - set(fixed))
    edges = [(k2i[u], k2i[v]) for u, v in form.edges_where({'_is_edge': True})]
    C = connectivity_matrix(edges)
    E = equilibrium_matrix(C, xyz, free)
    return nonpivots(rref(E, algo=algo))
示例#5
0
def form_update_q_from_qind(form):
    """Update the force densities of the dependent edges of a form diagram using
    the values of the independent ones.

    Parameters
    ----------
    form : FormDiagram
        The form diagram.

    Returns
    -------
    None
        The updated force densities are stored as attributes of the edges of the form diagram.

    Examples
    --------
    .. code-block:: python

        #

    """
    k_i = form.key_index()
    uv_index = form.uv_index()
    vcount = form.number_of_vertices()
    ecount = form.number_of_edges()
    fixed = form.leaves()
    fixed = [k_i[key] for key in fixed]
    free = list(set(range(vcount)) - set(fixed))
    ind = [
        index for index, (u, v, attr) in enumerate(form.edges(True))
        if attr['is_ind']
    ]
    dep = list(set(range(ecount)) - set(ind))
    edges = [(k_i[u], k_i[v]) for u, v in form.edges()]
    xy = array(form.xy(), dtype=float64).reshape((-1, 2))
    q = array(form.q(), dtype=float64).reshape((-1, 1))
    C = connectivity_matrix(edges, 'csr')
    E = equilibrium_matrix(C, xy, free, 'csr')

    update_q_from_qind(E, q, dep, ind)

    uv = C.dot(xy)
    l = normrow(uv)
    f = q * l

    for u, v, attr in form.edges(True):
        index = uv_index[(u, v)]
        attr['q'] = q[index, 0]
        attr['f'] = f[index, 0]
        attr['l'] = l[index, 0]
示例#6
0
def form_count_dof(form):
    r"""Count the number of degrees of freedom of a form diagram.

    Parameters
    ----------
    form : FormDiagram
        The form diagram.

    Returns
    -------
    k : int
        Dimension of the null space (*nullity*) of the equilibrium matrix of the
        form diagram.
    m : int
        Dimension of the left null space of the equilibrium matrix of the form
        diagram.

    Notes
    -----
    The equilibrium matrix of the form diagram is

    .. math::

        \mathbf{E}
        =
        \begin{bmatrix}
        \mathbf{C}_{i}^{t}\mathbf{U} \\
        \mathbf{C}_{i}^{t}\mathbf{V}
        \end{bmatrix}

    References
    ----------
    ...

    Examples
    --------
    .. code-block:: python

        #

    """
    k_i = form.key_index()
    xy = form.get_vertices_attributes('xy')
    fixed = [k_i[key] for key in form.fixed()]
    free = list(set(range(len(form.vertex))) - set(fixed))
    edges = [(k_i[u], k_i[v]) for u, v in form.edges_where({'is_edge': True})]
    C = connectivity_matrix(edges)
    E = equilibrium_matrix(C, xy, free)
    k, m = dof(E)
    return k, m
示例#7
0
def form_count_dof(form):
    r"""Count the number of degrees of freedom of a form diagram.

    Parameters
    ----------
    form : FormDiagram
        The form diagram.

    Returns
    -------
    k : int
        Dimension of the null space (*nullity*) of the equilibrium matrix of the
        form diagram.
    m : int
        Dimension of the left null space of the equilibrium matrix of the form
        diagram.

    Notes
    -----
    The equilibrium matrix of the form diagram is

    .. math::

        \mathbf{E}
        =
        \begin{bmatrix}
        \mathbf{C}_{i}^{t}\mathbf{U} \\
        \mathbf{C}_{i}^{t}\mathbf{V}
        \end{bmatrix}

    Examples
    --------
    >>>
    """
    vertex_index = form.vertex_index()

    xy = form.vertices_attributes('xy')
    fixed = [vertex_index[vertex] for vertex in form.leaves()]
    free = list(set(range(form.number_of_vertices())) - set(fixed))
    edges = [(vertex_index[u], vertex_index[v]) for u, v in form.edges()]
    C = connectivity_matrix(edges)
    E = equilibrium_matrix(C, xy, free)

    k, m = dof(E)

    return int(k), int(m)
示例#8
0
def form_update_q_from_qind(form):
    """Update the force densities of the dependent edges of a form diagram using
    the values of the independent ones.

    Parameters
    ----------
    form : FormDiagram
        The form diagram.

    Returns
    -------
    None
        The updated force densities are stored as attributes of the edges of the form diagram.

    Examples
    --------
    >>>
    """
    vertex_index = form.vertex_index()
    edge_index = form.edge_index()

    vcount = form.number_of_vertices()
    ecount = form.number_of_edges()
    fixed = form.leaves()
    fixed = [vertex_index[vertex] for vertex in fixed]
    free = list(set(range(vcount)) - set(fixed))
    ind = [edge_index[edge] for edge in form.ind()]
    dep = list(set(range(ecount)) - set(ind))
    edges = [(vertex_index[u], vertex_index[v]) for u, v in form.edges()]
    xy = array(form.xy(), dtype=float64).reshape((-1, 2))
    q = array(form.q(), dtype=float64).reshape((-1, 1))
    C = connectivity_matrix(edges, 'csr')
    E = equilibrium_matrix(C, xy, free, 'csr')

    update_q_from_qind(E, q, dep, ind)

    uv = C.dot(xy)
    lengths = normrow(uv)
    forces = q * lengths

    for edge in form.edges():
        index = edge_index[edge]
        form.edge_attributes(
            edge, ['q', 'f', 'l'],
            [q[index, 0], forces[index, 0], lengths[index, 0]])
示例#9
0
def compute_jacobian(form, force):
    r"""Compute the Jacobian matrix.

    The actual computation of the Jacobian matrix :math:`\partial \mathbf{X}^* / \partial \mathbf{X}`
    where :math:`\mathbf{X}` contains the form diagram coordinates in *Fortran* order
    (first all :math:`\mathbf{x}`-coordinates, then all :math:`\mathbf{y}`-coordinates) and :math:`\mathbf{X}^*` contains the
    force diagram coordinates in *Fortran* order (first all :math:`\mathbf{x}^*`-coordinates,
    then all :math:`\mathbf{y}^*`-coordinates).

    Parameters
    ----------
    form: :class:`FormDiagram`
        The form diagram.
    force: :class:`ForceDiagram`
        The force diagram.

    Returns
    -------
    jacobian
        Jacobian matrix (2 * _vcount, 2 * vcount)

    References
    ----------
    .. [1] Alic, V. and Åkesson, D., 2017. Bi-directional algebraic graphic statics. Computer-Aided Design, 93, pp.26-37.

    Examples
    --------
    >>>
    """
    # --------------------------------------------------------------------------
    # form diagram
    # --------------------------------------------------------------------------
    vcount = form.number_of_vertices()
    k_i = form.key_index()
    leaves = [k_i[key] for key in form.leaves()]
    free = list(set(range(form.number_of_vertices())) - set(leaves))
    vicount = len(free)
    edges = [(k_i[u], k_i[v]) for u, v in form.edges()]
    xy = array(form.xy(), dtype=float64).reshape((-1, 2))
    ecount = len(edges)
    C = connectivity_matrix(edges, 'array')
    E = equilibrium_matrix(C, xy, free, 'array')
    uv = C.dot(xy)
    u = uv[:, 0].reshape(-1, 1)
    v = uv[:, 1].reshape(-1, 1)
    Ct = C.transpose()
    Cti = array(Ct[free, :])

    q = array(form.q(), dtype=float64).reshape((-1, 1))
    Q = diag(q.flatten())  # TODO: Explore sparse (diags)

    independent_edges = [(k_i[u], k_i[v]) for (u, v) in list(form.edges_where({'is_ind': True}))]
    independent_edges_idx = [edges.index(i) for i in independent_edges]
    dependent_edges_idx = list(set(range(ecount)) - set(independent_edges_idx))

    Ed = E[:, dependent_edges_idx]
    Eid = E[:, independent_edges_idx]
    qid = q[independent_edges_idx]
    EdInv = inv(array(Ed))  # TODO: Explore sparse (spinv)

    # --------------------------------------------------------------------------
    # force diagram
    # --------------------------------------------------------------------------
    _vertex_index = force.vertex_index()
    _vcount = force.number_of_vertices()
    _edges = force.ordered_edges(form)
    _edges[:] = [(_vertex_index[u], _vertex_index[v]) for u, v in _edges]
    _L = laplacian_matrix(_edges, normalize=False, rtype='array')
    _C = connectivity_matrix(_edges, 'array')
    _Ct = _C.transpose()
    _Ct = array(_Ct)
    _known = [_vertex_index[force.anchor()]]

    # --------------------------------------------------------------------------
    # Jacobian
    # --------------------------------------------------------------------------
    jacobian = zeros((_vcount * 2, vcount * 2))
    for j in range(2):  # Loop for x and y
        idx = list(range(j * vicount, (j + 1) * vicount))
        for i in range(vcount):
            dXdxi = diag(Ct[i, :])
            dxdxi = Ct[i, :].reshape(-1, 1)

            dEdXi = zeros((vicount * 2, ecount))
            dEdXi[idx, :] = Cti.dot(dXdxi)

            dEdXi_d = dEdXi[:, dependent_edges_idx]
            dEdXi_id = dEdXi[:, independent_edges_idx]

            dEdXiInv = - EdInv.dot(dEdXi_d.dot(EdInv))

            dqdXi_d = - dEdXiInv.dot(Eid.dot(qid)) - EdInv.dot(dEdXi_id.dot(qid))
            dqdXi = zeros((ecount, 1))
            dqdXi[dependent_edges_idx] = dqdXi_d
            dqdXi[independent_edges_idx] = 0
            dQdXi = diag(dqdXi[:, 0])

            d_XdXiTop = zeros((_L.shape[0]))
            d_XdXiBot = zeros((_L.shape[0]))

            if j == 0:
                d_XdXiTop = solve_with_known(_L, (_Ct.dot(dQdXi.dot(u) + Q.dot(dxdxi))).flatten(), d_XdXiTop, _known)
                d_XdXiBot = solve_with_known(_L, (_Ct.dot(dQdXi.dot(v))).flatten(), d_XdXiBot, _known)
            elif j == 1:
                d_XdXiTop = solve_with_known(_L, (_Ct.dot(dQdXi.dot(u))).flatten(), d_XdXiTop, _known)
                d_XdXiBot = solve_with_known(_L, (_Ct.dot(dQdXi.dot(v) + Q.dot(dxdxi))).flatten(), d_XdXiBot, _known)

            d_XdXi = hstack((d_XdXiTop, d_XdXiBot))
            jacobian[:, i + j * vcount] = d_XdXi
    return jacobian
示例#10
0
def form_update_from_force_direct(form, force):
    r"""Update the form diagram after a modification of the force diagram.

    Compute the geometry of the form diagram from the geometry of the force diagram
    and some constraints (location of fixed points).
    The form diagram is computed by formulating the reciprocal relationships to
    the approach in described in AGS. In order to include the constraints, the
    reciprocal force densities and form diagram coordinates are solved for at
    the same time, by formulating the equation system:

    .. math::

        \mathbf{M}\mathbf{X} = \mathbf{r}

    with :math:`\mathbf{M}` containing the coefficients of the system of equations
    including constraints, :math:`\mathbf{X}` the coordinates of the vertices of
    the form diagram and the reciprocal force densities, in *Fortran* order
    (first all :math:`\mathbf{x}`-coordinates, then all :math:`\mathbf{y}`-coordinates, then all reciprocal force
    densities, :math:`\mathbf{q}^{-1}`), and  :math:`\mathbf{r}` contains the residual (all zeroes except
    for the constraint rows).

    The addition of constraints reduces the number of independent edges, which
    must be identified during the solving procedure. Additionally, the algorithm
    fails if any force density is zero (corresponding to a zero-length edge in
    the force diagram) or if it is over-constrained.

    Parameters
    ----------
    form : compas_ags.diagrams.formdiagram.FormDiagram
        The form diagram to update.
    force : compas_bi_ags.diagrams.forcediagram.ForceDiagram
        The force diagram on which the update is based.

    """
    # --------------------------------------------------------------------------
    # form diagram
    # --------------------------------------------------------------------------
    k_i = form.key_index()
    # i_j = {i: [k_i[n] for n in form.vertex_neighbours(k)] for i, k in enumerate(form.vertices())}
    uv_e = form.uv_index()
    ij_e = {(k_i[u], k_i[v]): uv_e[(u, v)] for u, v in uv_e}
    edges = [(k_i[u], k_i[v]) for u, v in form.edges()]
    C = connectivity_matrix(edges, 'array')
    # add opposite edges for convenience...
    ij_e.update({(k_i[v], k_i[u]): uv_e[(u, v)] for u, v in uv_e})
    edges = [(k_i[u], k_i[v]) for u, v in form.edges()]
    xy = array(form.xy(), dtype=float64).reshape((-1, 2))
    q = array(form.q(), dtype=float64).reshape((-1, 1))
    # --------------------------------------------------------------------------
    # force diagram
    # --------------------------------------------------------------------------
    _i_k = {index: key for index, key in enumerate(force.vertices())}
    _xy = array(force.xy(), dtype=float64)
    _edges = force.ordered_edges(form)
    _uv_e = {(_i_k[i], _i_k[j]): e for e, (i, j) in enumerate(_edges)}
    _vcount = force.number_of_vertices()
    _C = connectivity_matrix(_edges, 'array')
    _e_v = force.external_vertices(form)
    _free = list(set(range(_vcount)) - set(_e_v))

    # --------------------------------------------------------------------------
    # compute the coordinates of the form based on the force diagram
    # with linear constraints
    # --------------------------------------------------------------------------

    # Compute dual equilibrium matrix and Laplacian matrix
    import numpy as np
    _E = equilibrium_matrix(_C, _xy, _free, 'array')
    L = laplacian_matrix(edges, normalize=False, rtype='array')

    # Get dual coordinate difference vectors
    _uv = _C.dot(_xy)
    _U = np.diag(_uv[:, 0])
    _V = np.diag(_uv[:, 1])

    # Get reciprocal force densities
    from compas_bi_ags.utilities.errorhandler import SolutionError
    if any(abs(q) < 1e-14):
        raise SolutionError(
            'Found zero force density, direct solution not possible.')
    q = np.divide(1, q)

    # Formulate the equation system
    z = np.zeros(L.shape)
    z2 = np.zeros((_E.shape[0], L.shape[1]))
    M = np.bmat([[L, z, -C.T.dot(_U)], [z, L, -C.T.dot(_V)], [z2, z2, _E]])
    rhs = np.zeros((M.shape[0], 1))
    X = np.vstack((matrix(xy)[:, 0], matrix(xy)[:, 1], matrix(q)[:, 0]))

    # Add constraints
    constraint_rows, res = force.compute_constraints(form, M)
    M = np.vstack((M, constraint_rows))
    rhs = np.vstack((rhs, res))

    # Get independent variables
    from compas_bi_ags.utilities.helpers import get_independent_stress, check_solutions
    nr_free_vars, free_vars, dependent_vars = get_independent_stress(M)
    #k, m  = dof(M)
    #ind   = nonpivots(rref(M))

    # Partition system
    Mid = M[:, free_vars]
    Md = M[:, dependent_vars]
    Xid = X[free_vars]

    # Check that solution exists
    check_solutions(M, rhs)

    # Solve
    Xd = np.asarray(np.linalg.lstsq(Md, rhs - Mid * Xid)[0])
    X[dependent_vars] = Xd

    # Store solution
    nx = xy.shape[0]
    ny = xy.shape[0]
    xy[:, 0] = X[:nx].T
    xy[:, 1] = X[nx:(nx + ny)].T

    # --------------------------------------------------------------------------
    # update
    # --------------------------------------------------------------------------
    uv = C.dot(xy)
    _uv = _C.dot(_xy)
    a = [angle_vectors_xy(uv[i], _uv[i]) for i in range(len(edges))]
    l = normrow(uv)
    _l = normrow(_uv)
    q = _l / l
    # --------------------------------------------------------------------------
    # update form diagram
    # --------------------------------------------------------------------------
    for key, attr in form.vertices(True):
        index = k_i[key]
        attr['x'] = xy[index, 0]
        attr['y'] = xy[index, 1]
    for u, v, attr in form.edges(True):
        e = uv_e[(u, v)]
        attr['l'] = l[e, 0]
        attr['a'] = a[e]
        if a[e] < 90:
            attr['f'] = _l[e, 0]
            attr['q'] = q[e, 0]
        else:
            attr['f'] = -_l[e, 0]
            attr['q'] = -q[e, 0]
    # --------------------------------------------------------------------------
    # update force diagram
    # --------------------------------------------------------------------------
    for u, v, attr in force.edges(True):
        e = _uv_e[(u, v)]
        attr['a'] = a[e]
        attr['l'] = _l[e, 0]
示例#11
0
def optimise_single(form,
                    solver='devo',
                    polish='slsqp',
                    qmin=1e-6,
                    qmax=5,
                    population=300,
                    generations=500,
                    printout=10,
                    tol=0.001,
                    plot=False,
                    frange=[],
                    indset=None,
                    tension=False,
                    planar=False):
    """ Finds the optimised load-path for a FormDiagram.

    Parameters
    ----------
    form : obj
        The FormDiagram.
    solver : str
        Differential Evolution 'devo' or Genetic Algorithm 'ga' evolutionary solver to use.
    polish : str
        'slsqp' polish or None.
    qmin : float
        Minimum qid value.
    qmax : float
        Maximum qid value.
    population : int
        Number of agents for the evolution solver.
    generations : int
        Number of generations for the evolution solver.
    printout : int
        Frequency of print output to the terminal.
    tol : float
        Tolerance on horizontal force balance.
    plot : bool
        Plot progress of the evolution.
    frange : list
        Minimum and maximum function value to plot.
    indset : list
        Independent set to use.
    tension : bool
        Allow tension edge force densities (experimental).
    planar : bool
        Only consider the x-y plane.

    Returns
    -------
    float
        Optimum load-path value.
    list
        Optimum qids

    """

    if printout:
        print('\n' + '-' * 50)
        print('Load-path optimisation started')
        print('-' * 50)

    # Mapping

    k_i = form.key_index()
    i_k = form.index_key()
    i_uv = form.index_uv()
    uv_i = form.uv_index()

    # Vertices and edges

    n = form.number_of_vertices()
    m = form.number_of_edges()
    fixed = [k_i[key] for key in form.fixed()]
    rol = [k_i[key] for key in form.vertices_where({'is_roller': True})]
    edges = [(k_i[u], k_i[v]) for u, v in form.edges()]
    sym = [uv_i[uv] for uv in form.edges_where({'is_symmetry': True})]
    free = list(set(range(n)) - set(fixed) - set(rol))

    # Co-ordinates and loads

    xyz = zeros((n, 3))
    x = zeros((n, 1))
    y = zeros((n, 1))
    z = zeros((n, 1))
    px = zeros((n, 1))
    py = zeros((n, 1))
    pz = zeros((n, 1))

    for key, vertex in form.vertex.items():
        i = k_i[key]
        xyz[i, :] = form.vertex_coordinates(key)
        x[i] = vertex.get('x')
        y[i] = vertex.get('y')
        px[i] = vertex.get('px', 0)
        py[i] = vertex.get('py', 0)
        pz[i] = vertex.get('pz', 0)

    xy = xyz[:, :2]
    px = px[free]
    py = py[free]
    pz = pz[free]

    # C and E matrices

    C = connectivity_matrix(edges, 'csr')
    Ci = C[:, free]
    Cf = C[:, fixed]
    Cit = Ci.transpose()
    E = equilibrium_matrix(C, xy, free, 'csr').toarray()
    uvw = C.dot(xyz)
    U = uvw[:, 0]
    V = uvw[:, 1]

    # Independent and dependent branches

    if indset:
        ind = []
        for u, v in form.edges():
            if geometric_key(form.edge_midpoint(u, v)[:2] + [0]) in indset:
                ind.append(uv_i[(u, v)])
    else:
        ind = nonpivots(sympy.Matrix(E).rref()[0].tolist())

    k = len(ind)
    dep = list(set(range(m)) - set(ind))

    for u, v in form.edges():
        form.set_edge_attribute((u, v), 'is_ind',
                                True if uv_i[(u, v)] in ind else False)

    if printout:
        _, s, _ = svd(E)
        print('Form diagram has {0} (RREF): {1} (SVD) independent branches '.
              format(len(ind), m - len(s)))

    # Set-up

    lh = normrow(C.dot(xy))**2
    Edinv = -csr_matrix(pinv(E[:, dep]))
    Ei = E[:, ind]
    p = vstack([px, py])
    q = array([attr['q'] for u, v, attr in form.edges(True)])[:, newaxis]
    bounds = [[qmin, qmax]] * k
    args = (q, ind, dep, Edinv, Ei, C, Ci, Cit, U, V, p, px, py, pz, tol, z,
            free, planar, lh, sym, tension)

    # Horizontal checks

    checked = True

    if tol == 0:
        for i in range(10**3):
            q[ind, 0] = rand(k) * qmax
            q[dep] = -Edinv.dot(p - Ei.dot(q[ind]))
            Rx = Cit.dot(U * q.ravel()) - px.ravel()
            Ry = Cit.dot(V * q.ravel()) - py.ravel()
            R = max(sqrt(Rx**2 + Ry**2))
            if R > tol:
                checked = False
                break

    if checked:

        # Solve

        if solver == 'devo':
            fopt, qopt = _diff_evo(_fint, bounds, population, generations,
                                   printout, plot, frange, args)

        if polish == 'slsqp':
            fopt_, qopt_ = _slsqp(_fint_, qopt, bounds, printout, _fieq, args)
            q1 = _zlq_from_qid(qopt_, args)[2]
            if fopt_ < fopt:
                if (min(q1) > -0.001 and not tension) or tension:
                    fopt, qopt = fopt_, qopt_

        z, _, q, q_ = _zlq_from_qid(qopt, args)

        # Unique key

        gkeys = []
        for i in ind:
            u, v = i_uv[i]
            gkeys.append(geometric_key(form.edge_midpoint(u, v)[:2] + [0]))
        form.attributes['indset'] = gkeys

        # Update FormDiagram

        for i in range(n):
            key = i_k[i]
            form.set_vertex_attribute(key=key, name='z', value=float(z[i]))

        for c, qi in enumerate(list(q_.ravel())):
            u, v = i_uv[c]
            form.set_edge_attribute((u, v), 'q', float(qi))

        # Relax

        q = array([attr['q'] for u, v, attr in form.edges(True)])
        Q = diags(q)
        CitQ = Cit.dot(Q)
        Di = CitQ.dot(Ci)
        Df = CitQ.dot(Cf)
        bx = px - Df.dot(x[fixed])
        by = py - Df.dot(y[fixed])
        # bz   = pz - Df.dot(z[fixed])
        x[free, 0] = spsolve(Di, bx)
        y[free, 0] = spsolve(Di, by)
        # z[free, 0] = spsolve(Di, bz)

        for i in range(n):
            form.set_vertex_attributes(
                key=i_k[i],
                names='xyz',
                values=[float(j) for j in [x[i], y[i], z[i]]])

        fopt = 0
        for u, v in form.edges():
            if form.get_edge_attribute((u, v), 'is_symmetry') is False:
                qi = form.get_edge_attribute((u, v), 'q')
                li = form.edge_length(u, v)
                fopt += abs(qi) * li**2

        form.attributes['loadpath'] = fopt

        if printout:
            print('\n' + '-' * 50)
            print('qid range : {0:.3f} : {1:.3f}'.format(min(qopt), max(qopt)))
            print('q range   : {0:.3f} : {1:.3f}'.format(min(q), max(q)))
            print('fopt      : {0:.3f}'.format(fopt))
            print('-' * 50 + '\n')

        return fopt, qopt

    else:

        if printout:
            print('Horizontal equillibrium checks failed')

        return None, None
示例#12
0
def compute_jacobian(form, force):
    r"""Compute the Jacobian matrix.

    The actual computation of the Jacobian matrix :math:`\partial \mathbf{X}^* / \partial \mathbf{X}`
    where :math:`\mathbf{X}` contains the form diagram coordinates in *Fortran* order
    (first all :math:`\mathbf{x}`-coordinates, then all :math:`\mathbf{y}`-coordinates) and :math:`\mathbf{X}^*` contains the
    force diagram coordinates in *Fortran* order (first all :math:`\mathbf{x}^*`-coordinates,
    then all :math:`\mathbf{y}^*`-coordinates).

    Parameters
    ----------
    form : compas_ags.diagrams.formdiagram.FormDiagram
        The form diagram.
    force : compas_bi_ags.diagrams.forcediagram.ForceDiagram
        The force diagram.

    Returns
    -------
    jacobian
        Jacobian matrix (2 * _vcount, 2 * vcount)
    """
    # Update force diagram based on form
    form_update_q_from_qind(form)
    force_update_from_form(force, form)

    # --------------------------------------------------------------------------
    # form diagram
    # --------------------------------------------------------------------------
    vcount = form.number_of_vertices()
    k_i = form.key_index()
    leaves = [k_i[key] for key in form.leaves()]
    free = list(set(range(form.number_of_vertices())) - set(leaves))
    vicount = len(free)
    edges = [(k_i[u], k_i[v]) for u, v in form.edges()]
    xy = array(form.xy(), dtype=float64).reshape((-1, 2))
    ecount = len(edges)
    C = connectivity_matrix(edges, 'array')
    E = equilibrium_matrix(C, xy, free, 'array')
    uv = C.dot(xy)
    u = np.asmatrix(uv[:, 0]).transpose()
    v = np.asmatrix(uv[:, 1]).transpose()
    Ct = C.transpose()
    Cti = Ct[free, :]

    q = array(form.q(), dtype=float64).reshape((-1, 1))
    Q = np.diag(np.asmatrix(q).getA1())
    Q = np.asmatrix(Q)

    independent_edges = list(form.edges_where({'is_ind': True}))
    independent_edges_idx = [edges.index(i) for i in independent_edges]
    dependent_edges_idx = list(set(range(ecount)) - set(independent_edges_idx))

    Ed = E[:, dependent_edges_idx]
    Eid = E[:, independent_edges_idx]
    qid = q[independent_edges_idx]

    # --------------------------------------------------------------------------
    # force diagram
    # --------------------------------------------------------------------------
    _vcount = force.number_of_vertices()
    _edges = force.ordered_edges(form)
    _L = laplacian_matrix(_edges, normalize=False, rtype='array')
    _C = connectivity_matrix(_edges, 'array')
    _Ct = _C.transpose()
    _Ct = np.asmatrix(_Ct)
    _k_i = force.key_index()
    _known = [_k_i[force.anchor()]]

    # --------------------------------------------------------------------------
    # Jacobian
    # --------------------------------------------------------------------------
    jacobian = np.zeros((_vcount * 2, vcount * 2))
    for j in range(2):  # Loop for x and y
        idx = list(range(j * vicount, (j + 1) * vicount))
        for i in range(vcount):
            dXdxi = np.diag(Ct[i, :])
            dxdxi = np.transpose(np.asmatrix(Ct[i, :]))

            dEdXi = np.zeros((vicount * 2, ecount))
            dEdXi[idx, :] = np.asmatrix(Cti) * np.asmatrix(
                dXdxi)  # Always half the matrix 0 depending on j (x/y)

            dEdXi_d = dEdXi[:, dependent_edges_idx]
            dEdXi_id = dEdXi[:, independent_edges_idx]

            EdInv = np.linalg.inv(np.asmatrix(Ed))
            dEdXiInv = -EdInv * (np.asmatrix(dEdXi_d) * EdInv)

            dqdXi_d = -dEdXiInv * (Eid * np.asmatrix(qid)) - EdInv * (
                dEdXi_id * np.asmatrix(qid))
            dqdXi = np.zeros((ecount, 1))
            dqdXi[dependent_edges_idx] = dqdXi_d
            dqdXi[independent_edges_idx] = 0
            dQdXi = np.asmatrix(np.diag(dqdXi[:, 0]))

            d_XdXiTop = np.zeros((_L.shape[0]))
            d_XdXiBot = np.zeros((_L.shape[0]))
            if j == 0:
                d_XdXiTop = solve_with_known(
                    _L,
                    np.array(_Ct * (dQdXi * u + Q * dxdxi)).flatten(),
                    d_XdXiTop, _known)
                d_XdXiBot = solve_with_known(
                    _L,
                    np.array(_Ct * (dQdXi * v)).flatten(), d_XdXiBot, _known)
            elif j == 1:
                d_XdXiTop = solve_with_known(
                    _L,
                    np.array(_Ct * (dQdXi * u)).flatten(), d_XdXiTop, _known)
                d_XdXiBot = solve_with_known(
                    _L,
                    np.array(_Ct * (dQdXi * v + Q * dxdxi)).flatten(),
                    d_XdXiBot, _known)

            d_XdXi = np.hstack((d_XdXiTop, d_XdXiBot))
            jacobian[:, i + j * vcount] = d_XdXi
    return jacobian