Networks of bars and nodes
Many calculations in structural design involve networks of bars (or branches, edges, …) and nodes (or vertices, …). Essential to these calculations is information about how the elements of the network are connected. This connectivity can be described with a matrix.
Connectivity matrix
Each row in a connectivity matrix corresponds to a bar of the network and contains zeros in all columns, except in the columns of the nodes to which it is connected. There the entries are ‘-1’ and ‘+1’ indicating the direction of the edge:
\begin{equation}
C_{ij} = \left\{
\begin{aligned}
+1 & \text{ if vertex } i \text{ is the head of edge } j\\
-1 & \text{ if vertex } i \text{ is the tail of edge } j\\
0 & \text{ otherwise}
\end{aligned}
\right.
\end{equation}
The direction of the edges can be chosen arbitrarily. A typical convention is to direct all edges from the vertex with the lower index to the one with the higher index. Constructing a connectivity matrix in Python is easy if you have list of edge vertex pairs.
01.
import
numpy as np
02.
03.
edges
=
[[
0
,
4
], [
1
,
4
], [
2
,
4
], [
3
,
4
]]
04.
vertices
=
[[
0
,
0
,
0
], [
1
,
0
,
0
], [
1
,
1
,
0
], [
0
,
1
,
0
], [
0.5
,
0.5
,
0
]]
05.
06.
m
=
len(edges)
07.
n
=
len(vertices)
08.
09.
C
=
np.zeros((m, n))
10.
for
i
in
range(m):
11.
u, v
=
edges[i]
12.
C[i, u]
=
-
1
13.
C[i, v]
=
+
1
14.
15.
# print C
16.
array([[
-
1.
,
0.
,
0.
,
0.
,
1.
],
17.
[
0.
,
-
1.
,
0.
,
0.
,
1.
],
18.
[
0.
,
0.
,
-
1.
,
0.
,
1.
],
19.
[
0.
,
0.
,
0.
,
-
1.
,
1.
]])
Once you have the connectivity matrix, it is easy to compute some of the properties of the elements of the network.
01.
xyz
=
np.array(vertices)
02.
03.
# coordinate difference vectors
04.
uvw
=
C.dot(xyz)
05.
06.
# edge lengths
07.
l
=
np.sqrt(np.sum(np.square(uvw), axis
=
1
))
08.
# or, since operations like ** are elementwise
09.
l
=
np.sum(uvw
*
*
2
, axis
=
1
)
*
*
0.5
10.
11.
# make l into a column vector
12.
l
=
l.reshape((
-
1
,
1
))
13.
14.
# normalized coordinate difference vectors
15.
unit
=
np.divide(uvw, np.tile(l,
3
))
16.
# or, since numpy does something called 'broadcasting'
17.
unit
=
np.divide(uvw, l)
18.
# or, since operations like / are elementwise
19.
unit
=
uvw
/
l
Laplacian matrix, Degree matrix, Adjacency matrix
The laplacian matrix of a network can be derived from its connectivity matrix.
1.
L
=
C.T.dot(C)
2.
3.
# print L
4.
array([[
1.
,
0.
,
0.
,
0.
,
-
1.
],
5.
[
0.
,
1.
,
0.
,
0.
,
-
1.
],
6.
[
0.
,
0.
,
1.
,
0.
,
-
1.
],
7.
[
0.
,
0.
,
0.
,
1.
,
-
1.
],
8.
[
-
1.
,
-
1.
,
-
1.
,
-
1.
,
4.
]])
The laplacian matrix contains on its diagonal the degree of each vertex of the network.
1.
degree
=
L.diagonal()
2.
3.
# print degree
4.
array([
1.
,
1.
,
1.
,
1.
,
4.
])
The laplacian matrix is related to the degree matrix and the adjacency matrix in the following way:
\begin{equation}
\mathbf{L} = \mathbf{D} – \mathbf{A}
\end{equation}
Therefore, the adjacency matrix can be derived from the laplacian matrix like this
01.
D
=
np.diagflat(degree)
02.
A
=
D
-
L
03.
04.
# print A
05.
array([[
0.
,
0.
,
0.
,
0.
,
1.
],
06.
[
0.
,
0.
,
0.
,
0.
,
1.
],
07.
[
0.
,
0.
,
0.
,
0.
,
1.
],
08.
[
0.
,
0.
,
0.
,
0.
,
1.
],
09.
[
1.
,
1.
,
1.
,
1.
,
0.
]])
Sparse matrices
Text will follow later…
01.
from
scipy.sparse
import
coo_matrix, diags
02.
03.
m
=
len(edges)
04.
data
=
np.array([
-
1
]
*
m
+
[
1
]
*
m)
05.
rows
=
np.array(range(m)
+
range(m))
06.
cols
=
np.array([edge[
0
]
for
edge
in
edges]
+
[edge[
1
]
for
edge
in
edges])
07.
C
=
coo_matrix((data, (rows, cols))).tocsr()
08.
L
=
C.transpose().dot(C)
09.
D
=
diags([L.diagonal()], [
0
])
10.
A
=
D
-
L