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