def _beam_shear(S, X, inds, indi, indf, EIx, EIy): """ Generate the beam nodal shear forces Sx, Sy and Sz. Parameters: S (array): Empty or populated beam nodal shear force array. X (array): Co-ordinates of nodes. inds (list): Indices of all beam element start nodes. indi (list): Indices of all beam element intermediate nodes. indf (list): Indices of all beam element finish nodes beams. EIx (array): Nodal EIx flexural stiffnesses for all beams. EIy (array): Nodal EIy flexural stiffnesses for all beams. Returns: array: Updated beam nodal shears. """ S *= 0 Xs = X[inds, :] Xi = X[indi, :] Xf = X[indf, :] Qa = Xi - Xs Qb = Xf - Xi Qc = Xf - Xs Qn = cross(Qa, Qb) Qnn = normrow(Qn) La = normrow(Qa) Lb = normrow(Qb) Lc = normrow(Qc) a = arccos((La**2 + Lb**2 - Lc**2) / (2 * La * Lb)) k = 2 * sin(a) / Lc mu = -0.5 * Xs + 0.5 * Xf mun = normrow(mu) ex = Qn / tile(Qnn, (1, 3)) # Temporary simplification ez = mu / tile(mun, (1, 3)) ey = cross(ez, ex) K = tile(k / Qnn, (1, 3)) * Qn Kx = tile(sum(K * ex, 1)[:, newaxis], (1, 3)) * ex Ky = tile(sum(K * ey, 1)[:, newaxis], (1, 3)) * ey Mc = EIx * Kx + EIy * Ky cma = cross(Mc, Qa) cmb = cross(Mc, Qb) ua = cma / tile(normrow(cma), (1, 3)) ub = cmb / tile(normrow(cmb), (1, 3)) c1 = cross(Qa, ua) c2 = cross(Qb, ub) Lc1 = normrow(c1) Lc2 = normrow(c2) M = sum(Mc**2, 1)[:, newaxis] Sa = ua * tile(M * Lc1 / (La * sum(Mc * c1, 1)[:, newaxis]), (1, 3)) Sb = ub * tile(M * Lc2 / (Lb * sum(Mc * c2, 1)[:, newaxis]), (1, 3)) Sa[isnan(Sa)] = 0 Sb[isnan(Sb)] = 0 S[inds, :] += Sa S[indi, :] += -Sa - Sb S[indf, :] += Sb # Add node junction duplication for when elements cross each other # mu[0, :] = -1.25*x[0, :] + 1.5*x[1, :] - 0.25*x[2, :] # mu[-1, :] = 0.25*x[-3, :] - 1.5*x[-2, :] + 1.25*x[-1, :] return S
def fd(vertices, edges, fixed, q, loads, rtype='list'): num_v = len(vertices) free = list(set(range(num_v)) - set(fixed)) xyz = asarray(vertices, dtype=float).reshape((-1, 3)) q = asarray(q, dtype=float).reshape((-1, 1)) p = asarray(loads, dtype=float).reshape((-1, 3)) C = connectivity_matrix(edges, 'csr') Ci = C[:, free] Cf = C[:, fixed] Ct = C.transpose() Cit = Ci.transpose() Q = diags([q.flatten()], [0]) A = Cit.dot(Q).dot(Ci) b = p[free] - Cit.dot(Q).dot(Cf).dot(xyz[fixed]) xyz[free] = spsolve(A, b) l = normrow(C.dot(xyz)) f = q * l r = p - Ct.dot(Q).dot(C).dot(xyz) if rtype == 'list': return [xyz.tolist(), q.ravel().tolist(), f.ravel().tolist(), l.ravel().tolist(), r.tolist()] if rtype == 'dict': return {'xyz': xyz.tolist(), 'q' : q.ravel().tolist(), 'f' : f.ravel().tolist(), 'l' : l.ravel().tolist(), 'r' : r.tolist()} return xyz, q, f, l, r
def grad(V, F, rtype='array'): """Construct the gradient operator of a trianglular mesh. Parameters ---------- V : array Vertex coordinates of the mesh. F : array Face vertex indices of the mesh. rtype : {'array', 'csc', 'csr', 'coo', 'list'} Format of the result. Returns ------- array-like Depending on rtype return type. Notes ----- The gradient operator is fully determined by the connectivity of the mesh and the coordinate difference vectors associated with the edges """ v = V.shape[0] f = F.shape[0] f0 = F[:, 0] # Index of first vertex of each face f1 = F[:, 1] # Index of second vertex of each face f2 = F[:, 2] # Index of last vertex of each face v01 = V[f1, :] - V[f0, :] # Vector from vertex 0 to 1 for each face v12 = V[f2, :] - V[f1, :] # Vector from vertex 1 to 2 for each face v20 = V[f0, :] - V[f2, :] # Vector from vertex 2 to 0 for each face n = cross(v12, v20) # Normal vector to each face A2 = normrow(n) # Length of normal vector is twice the area of the face A2 = tile(A2, (1, 3)) u = normalizerow(n) # Unit normals for each face v01_ = divide(rot90(v01, u), A2) # Vector perpendicular to v01, normalized by A2 v20_ = divide(rot90(v20, u), A2) # Vector perpendicular to v20, normalized by A2 i = hstack(( # Nonzero rows 0 * f + tile(arange(f), (1, 4)), 1 * f + tile(arange(f), (1, 4)), 2 * f + tile(arange(f), (1, 4)))).flatten() j = tile(hstack((f1, f0, f2, f0)), (1, 3)).flatten() # Nonzero columns data = hstack(( hstack((v20_[:, 0], -v20_[:, 0], v01_[:, 0], -v01_[:, 0])), hstack((v20_[:, 1], -v20_[:, 1], v01_[:, 1], -v01_[:, 1])), hstack((v20_[:, 2], -v20_[:, 2], v01_[:, 2], -v01_[:, 2])), )).flatten() G = coo_matrix((data, (i, j)), shape=(3 * f, v)) if rtype == 'array': return G.toarray() elif rtype == 'csr': return G.tocsr() elif rtype == 'csc': return G.tocsc() elif rtype == 'coo': return G else: return G
def fn(l0, *args): tol, Xt, edges = args for c, uv in enumerate(edges): network.set_edge_attribute(uv[0], uv[1], 'l0', l0[c]) X, f, l = numba_dr_run(network, tol=tol) X[:, 2] /= max(X[:, 2]) norm = mean(normrow(X - Xt)) if isnan(norm) or isinf(norm): return 10**6 return norm
def uvw_lengths(C, X): r"""Calculates the lengths and co-ordinate differences. Parameters: C (sparse): Connectivity matrix (m x n) X (array): Co-ordinates of vertices/points (n x 3). Returns: array: Vectors of co-ordinate differences in x, y and z (m x 3). array: Lengths of members (m x 1) Examples: >>> C = connectivity_matrix([[0, 1], [1, 2]], 'csr') >>> X = array([[0, 0, 0], [1, 1, 0], [0, 0, 1]]) >>> uvw array([[ 1, 1, 0], [-1, -1, 1]]) >>> l array([[ 1.41421356], [ 1.73205081]]) """ uvw = C.dot(X) return uvw, normrow(uvw)
def _create_arrays(network): """ Create arrays needed for dr_solver. Parameters: network (obj): Network to analyse. Returns: array: Constraint conditions. array: Nodal loads Px, Py, Pz. array: Resultant nodal loads. array: Sx, Sy, Sz shear force components. array: x, y, z co-ordinates. array: Nodal velocities Vx, Vy, Vz. array: Edges' initial forces. array: Edges' initial lengths. list: Compression only edges. list: Tension only edges. array: Connectivity matrix. array: Transposed connectivity matrix. array: Axial stiffnesses. array: Network edges' start points. array: Network edges' end points. array: Mass matrix. list: Edge adjacencies (rows). list: Edge adjacencies (columns). list: Edge adjacencies (values). array: Young's moduli. array: Edge areas. """ # Vertices n = network.number_of_vertices() B = zeros((n, 3)) P = zeros((n, 3)) X = zeros((n, 3)) S = zeros((n, 3)) V = zeros((n, 3)) k_i = network.key_index() for key in network.vertices(): i = k_i[key] vertex = network.vertex[key] B[i, :] = vertex['B'] P[i, :] = vertex['P'] X[i, :] = [vertex[j] for j in 'xyz'] Pn = normrow(P) # Edges m = len(network.edges()) E = zeros((m, 1)) A = zeros((m, 1)) s0 = zeros((m, 1)) l0 = zeros((m, 1)) u = [] v = [] ind_c = [] ind_t = [] edges = [] uv_i = network.uv_index() for ui, vi in network.edges(): i = uv_i[(ui, vi)] edge = network.edge[ui][vi] edges.append([k_i[ui], k_i[vi]]) u.append(k_i[ui]) v.append(k_i[vi]) E[i] = edge['E'] A[i] = edge['A'] s0[i] = edge['s0'] if edge['l0']: l0[i] = edge['l0'] else: l0[i] = network.edge_length(ui, vi) if edge['ct'] == 'c': ind_c.append(i) elif edge['ct'] == 't': ind_t.append(i) f0 = s0 * A ks = E * A / l0 # Arrays C = connectivity_matrix(edges, 'csr') Ct = C.transpose() M = mass_matrix(Ct, E, A, l0, f0, c=1, tiled=False) rows, cols, vals = find(Ct) return B, P, Pn, S, X, V, f0, l0, ind_c, ind_t, C, Ct, ks, array(u), array( v), M, rows, cols, vals, E, A
def drx_numpy(tol, steps, C, Ct, V, M, B, S, P, X, f0, ks, l0, ind_c, ind_t, refresh, factor=1, beams=None, inds=None, indi=None, indf=None, EIx=None, EIy=None): """ Numpy and SciPy dynamic relaxation solver. Parameters: tol (float): Tolerance limit. steps (int): Maximum number of iteration steps. C (array): Connectivity matrix. Ct (array): Transposed connectivity matrix. V (array): Nodal velocities Vx, Vy, Vz. M (array): Mass matrix (untiled). B (array): Constraint conditions. S (array): Sx, Sy, Sz shear force components. P (array): Nodal loads Px, Py, Pz. X (array): Nodal co-ordinates. f0 (array): Initial edge forces. ks (array): Initial edge stiffnesses. l0 (array): Initial edge lengths. ind_c (list): Compression only edges. ind_t (list): Tension only edges. refresh (int): Update progress every n steps. factor (float): Convergence factor. beams (dic): Dictionary of beam information. inds (list): Indices of all beam element start nodes. indi (list): Indices of all beam element intermediate nodes. indf (list): Indices of all beam element finish nodes beams. EIx (array): Nodal EIx flexural stiffnesses for all beams. EIy (array): Nodal EIy flexural stiffnesses for all beams. Returns: array: Updated nodal co-ordinates. array: Final forces. array: Final lengths. """ res = 1000 * tol ts, Uo = 0, 0 M = factor * tile(M, (1, 3)) while (ts <= steps) and (res > tol): uvw, l = uvw_lengths(C, X) f = f0 + ks * (l - l0) if ind_t: f[ind_t] *= f[ind_t] > 0 if ind_c: f[ind_c] *= f[ind_c] < 0 if beams: S = _beam_shear(S, X, inds, indi, indf, EIx, EIy) q = tile(f / l, (1, 3)) R = (P - S - Ct.dot(uvw * q)) * B Rn = normrow(R) res = mean(Rn) V += R / M Un = sum(0.5 * M * V**2) if Un < Uo: V *= 0 Uo = Un X += V ts += 1 if refresh: print('Iterations: {0}'.format(ts - 1)) print('Residual: {0:.3g}'.format(res)) return X, f, l
def dr_solver(tol, steps, factor, C, Ct, X, ks, l0, f0, ind_c, ind_t, P, S, B, M, V, refresh, bmesh, beams, inds, indi, indf, EIx, EIy): """ Numpy and SciPy dynamic relaxation solver. Parameters: tol (float): Tolerance limit. steps (int): Maximum number of iteration steps. factor (float): Convergence factor. C (array): Connectivity matrix. Ct (array): Transposed connectivity matrix. X (array): Nodal co-ordinates. ks (array): Initial edge axial stiffnesses. l0 (array): Initial edge lengths. f0 (array): Initial edge forces. ind_c (list): Compression only edges. ind_t (list): Tension only edges. P (array): Nodal loads Px, Py, Pz. S (array): Shear forces Sx, Sy, Sz. B (array): Constraint conditions. M (array): Mass matrix (untiled). V (array): Nodal velocities Vx, Vy, Vz. refresh (int): Update progress every n steps. bmesh (obj): Blender mesh to update. beams (bool): Dictionary of beam information. inds (list): Indices of beam element start nodes. indi (list): Indices of beam element intermediate nodes. indf (list): Indices of beam element finish nodes beams. EIx (array): Nodal EIx flexural stiffnesses. EIy (array): Nodal EIy flexural stiffnesses. Returns: array: Updated nodal co-ordinates. array: Final edge forces. array: Final edge lengths. """ res = 1000 * tol ts = 0 Uo = 0 M = factor * tile(M, (1, 3)) while (ts <= steps) and (res > tol): uvw, l = uvw_lengths(C, X) f = f0 + ks * (l - l0) if ind_t: f[ind_t] *= f[ind_t] > 0 if ind_c: f[ind_c] *= f[ind_c] < 0 if beams: S = beam_shear(S, X, inds, indi, indf, EIx, EIy) q = f / l qt = tile(q, (1, 3)) R = (P - S - Ct.dot(uvw * qt)) * B res = mean(normrow(R)) V += R / M Un = sum(M * V**2) if Un < Uo: V *= 0 Uo = Un X += V if refresh and (ts % refresh == 0): print('Step:{0} Residual:{1:.3g}'.format(ts, res)) if bmesh: update_bmesh_vertices(bmesh, X) ts += 1 if refresh: print('Step:{0} Residual:{1:.3g}'.format(ts, res)) return X, f, l
def create_arrays(network): """ Create arrays for DR solver. Parameters: network (obj): Network to analyse. Returns: array: Nodal co-ordinates x, y, z. array: Constraint conditions Bx, By, Bz. array: Nodal loads Px, Py, Pz. array: Resultant nodal loads. array: Shear force components Sx, Sy, Sz. array: Nodal velocities Vx, Vy, Vz. array: Edge Young's moduli. array: Edge areas. array: Connectivity matrix. array: Transposed connectivity matrix. array: Edge initial forces. array: Edge initial lengths. list: Compression only edges indices. list: Tension only edges indices. array: Network edges' start points. array: Network edges' end points. array: Mass matrix. array: Edge axial stiffnesses. """ # Vertices vertices = list(network.vertices()) n = len(vertices) X = zeros((n, 3)) B = zeros((n, 3)) P = zeros((n, 3)) S = zeros((n, 3)) V = zeros((n, 3)) k_i = network.key_index() for key in vertices: i = k_i[key] vertex = network.vertex[key] X[i, :] = [vertex[j] for j in 'xyz'] B[i, :] = vertex.get('B', [1, 1, 1]) P[i, :] = vertex.get('P', [0, 0, 0]) Pn = normrow(P) # Edges edges = list(network.edges()) m = len(edges) E = zeros((m, 1)) A = zeros((m, 1)) s0 = zeros((m, 1)) l0 = zeros((m, 1)) u = zeros(m, dtype=int64) v = zeros(m, dtype=int64) ind_c = [] ind_t = [] uv_i = network.uv_index() for c, uv in enumerate(edges): ui, vi = uv i = uv_i[(ui, vi)] edge = network.edge[ui][vi] E[i] = edge.get('E', 0) A[i] = edge.get('A', 0) l0[i] = edge.get('l0', network.edge_length(ui, vi)) s0[i] = edge.get('s0', 0) ct = edge.get('ct', None) if ct == 'c': ind_c.append(i) elif ct == 't': ind_t.append(i) u[c] = k_i[ui] v[c] = k_i[vi] f0 = s0 * A ks = E * A / l0 # Faces (unconfirmed testing formulation) faces = list(network.faces()) if faces: for face in faces: fdata = network.facedata[face] Eh = fdata.get('E', 0) Ah = network.face_area(face) th = fdata.get('t', 0) for ui, vi in network.face_edges(face): i = uv_i[(ui, vi)] ks[i] += 1.5 * Eh * Ah * th / l0[i]**2 # Arrays C = connectivity_matrix([[k_i[ui], k_i[vi]] for ui, vi in edges], 'csr') Ct = C.transpose() M = mass_matrix(Ct, ks, f0, c=1, tiled=False) return X, B, P, Pn, S, V, E, A, C, Ct, f0, l0, ind_c, ind_t, u, v, M, ks
def dr(vertices, edges, fixed, loads, qpre, fpre, lpre, linit, E, radius, ufunc=None, **kwargs): # -------------------------------------------------------------------------- # configuration # -------------------------------------------------------------------------- kmax = kwargs.get('kmax', 10000) dt = kwargs.get('dt', 1.0) tol1 = kwargs.get('tol1', 1e-3) tol2 = kwargs.get('tol2', 1e-6) coeff = Coeff(kwargs.get('c', 0.1)) ca = coeff.a cb = coeff.b # -------------------------------------------------------------------------- # attribute lists # -------------------------------------------------------------------------- num_v = len(vertices) num_e = len(edges) free = list(set(range(num_v)) - set(fixed)) # -------------------------------------------------------------------------- # attribute arrays # -------------------------------------------------------------------------- xyz = array(vertices, dtype=float).reshape((-1, 3)) # m p = array(loads, dtype=float).reshape((-1, 3)) # kN qpre = array(qpre, dtype=float).reshape((-1, 1)) fpre = array(fpre, dtype=float).reshape((-1, 1)) # kN lpre = array(lpre, dtype=float).reshape((-1, 1)) # m linit = array(linit, dtype=float).reshape((-1, 1)) # m E = array(E, dtype=float).reshape((-1, 1)) # kN/mm2 => GPa radius = array(radius, dtype=float).reshape((-1, 1)) # mm # -------------------------------------------------------------------------- # sectional properties # -------------------------------------------------------------------------- A = 3.14159 * radius**2 # mm2 EA = E * A # kN # -------------------------------------------------------------------------- # create the connectivity matrices # after spline edges have been aligned # -------------------------------------------------------------------------- C = connectivity_matrix(edges, 'csr') Ct = C.transpose() Ci = C[:, free] Cit = Ci.transpose() Ct2 = Ct.copy() Ct2.data **= 2 # -------------------------------------------------------------------------- # if none of the initial lengths are set, # set the initial lengths to the current lengths # -------------------------------------------------------------------------- if all(linit == 0): linit = normrow(C.dot(xyz)) # -------------------------------------------------------------------------- # initial values # -------------------------------------------------------------------------- q = ones((num_e, 1), dtype=float) l = normrow(C.dot(xyz)) f = q * l v = zeros((num_v, 3), dtype=float) r = zeros((num_v, 3), dtype=float) # -------------------------------------------------------------------------- # acceleration # -------------------------------------------------------------------------- def a(t, v): dx = v * t xyz[free] = xyz0[free] + dx[free] r[free] = p[free] - D.dot(xyz) return cb * r / mass # -------------------------------------------------------------------------- # start iterating # -------------------------------------------------------------------------- for k in xrange(kmax): q_fpre = fpre / l q_lpre = f / lpre q_EA = EA * (l - linit) / (linit * l) q_lpre[isinf(q_lpre)] = 0 q_lpre[isnan(q_lpre)] = 0 q_EA[isinf(q_EA)] = 0 q_EA[isnan(q_EA)] = 0 q = qpre + q_fpre + q_lpre + q_EA Q = diags([q[:, 0]], [0]) D = Cit.dot(Q).dot(C) mass = 0.5 * dt**2 * Ct2.dot(qpre + q_fpre + q_lpre + EA / linit) xyz0 = xyz.copy() # ---------------------------------------------------------------------- # RK # ---------------------------------------------------------------------- v0 = ca * v.copy() dv = rk2(a, v0, dt) v = v0 + dv dx = v * dt xyz[free] = xyz0[free] + dx[free] # update uvw = C.dot(xyz) l = normrow(uvw) f = q * l r = p - Ct.dot(Q).dot(uvw) # crits crit1 = norm(r[free]) crit2 = norm(dx[free]) # ufunc if ufunc: ufunc(k, crit1, crit2) # convergence if crit1 < tol1: break if crit2 < tol2: break print(k) return xyz, q, f, l, r
def fn(dofs, *args): network, Xt, div, factor, tol, steps, ds = args X = update(dofs, network, Xt, div, factor, tol, steps, ds, refresh=0, bmesh=0, plot=0) ind = closest_points_points(X, Xt, distances=False) return 1000 * mean(normrow(X - Xt[ind, :]))
edges = [(key_index[u], key_index[v]) for u, v in network.edges()] C = connectivity_matrix(edges, rtype='list') C = matlab.double(C) # compute coordinate differences in Matlab # # using an engine function # uv = matlab.engine.mtimes(C, xyz) # using workspace data matlab.engine.workspace['C'] = C matlab.engine.workspace['xyz'] = xyz uv = matlab.engine.eval('C * xyz') # compute edge lengths in Python l = normrow(uv) l = l.flatten().tolist() # plot results as edge labels plotter = NetworkPlotter(network) plotter.draw_vertices() plotter.draw_edges(text={(u, v): '%.1f' % l[index] for index, (u, v) in enumerate(network.edges())}) plotter.show()