Note

This notebook can be downloaded here: Optimization_practical_work.ipynb

Practical work: optimizing a bridge structure

This tutorial aims optimizing the structure of a bridge using the truss package. *In order to use it, download the file ``truss.py`` and put it into your current work directory.

%load_ext autoreload
%autoreload 2
The autoreload extension is already loaded. To reload it, use:
  %reload_ext autoreload
import numpy as np
import matplotlib.pyplot as plt
import copy, os
from scipy import optimize
import truss
%matplotlib nbagg

Building the bridge structure

In this session, we will modelled a bridge structure using truss and optimize it using various criteria. The basic structure is introduced below. It is made of steel bars and loaded with one vertical force on \(G\). The bridge is symmetrical so only the left half is modelled.

E       = 210.e9   # Young Modulus [Pa]
rho     = 7800.    # Density       [kg/m**3]
A       = 5.e-2    # Cross section [m**2]
sigmay  = 400.e6   # Yield Stress  [Pa]

# Model definition
model = truss.Model() # Model definition

# NODES
nA = model.add_node((0.,0.), label = "A")
nC = model.add_node((3.,0.), label = "C")
nD = model.add_node((3.,3.), label = "D")
nE = model.add_node((6.,0.), label = "E")
nF = model.add_node((6.,3.), label = "F")
nG = model.add_node((9.,0.), label = "G")
nH = model.add_node((9.,3.), label = "H")

# BOUNDARY CONDITIONS
nA.block[1] = True
nG.block[0] = True
nH.block[0] = True

# BARS
AC = model.add_bar(nA, nC, modulus = E, density = rho, section = A, yield_stress = sigmay)
CD = model.add_bar(nC, nD, modulus = E, density = rho, section = A, yield_stress = sigmay)
AD = model.add_bar(nA, nD, modulus = E, density = rho, section = A, yield_stress = sigmay)
CE = model.add_bar(nC, nE, modulus = E, density = rho, section = A, yield_stress = sigmay)
DF = model.add_bar(nD, nF, modulus = E, density = rho, section = A, yield_stress = sigmay)
DE = model.add_bar(nD, nE, modulus = E, density = rho, section = A, yield_stress = sigmay)
EF = model.add_bar(nE, nF, modulus = E, density = rho, section = A, yield_stress = sigmay)
EG = model.add_bar(nE, nG, modulus = E, density = rho, section = A, yield_stress = sigmay)
FH = model.add_bar(nF, nH, modulus = E, density = rho, section = A, yield_stress = sigmay)
FG = model.add_bar(nF, nG, modulus = E, density = rho, section = A, yield_stress = sigmay)
GH = model.add_bar(nG, nH, modulus = E, density = rho, section = A, yield_stress = sigmay)

# STRUCTURAL LOADING
nG.force = np.array([0., -1.e6])


%timeit model.solve()
model
3.17 ms ± 395 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
<Model: 7 nodes, 11 bars>
xlim, ylim = model.bbox(deformed = False)
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.set_aspect("equal")
#ax.axis("off")
model.draw(ax, deformed = False, field = "stress", label = True, force_scale = 1.e-6, forces = True)
plt.xlim(xlim)
plt.ylim(ylim)
plt.grid()
plt.xlabel("Axe $x$")
plt.ylabel("Axe $y$")
<IPython.core.display.Javascript object>
<matplotlib.text.Text at 0x7f79009258d0>

Detailed results at the nodes

model.data(at = "nodes")
block coords disp force label
bx by x y ux uy Fx Fy o
0 False True 0 0 -0.00114286 0 0 1e+06 A
1 False False 3 0 -0.000857143 -0.00337955 0 0 C
2 False False 3 3 0.00142857 -0.00337955 0 0 D
3 False False 6 0 -0.000571429 -0.00618767 0 0 E
4 False False 6 3 0.000857143 -0.00647339 0 0 F
5 True False 9 0 0 -0.00813865 3e+06 -1e+06 G
6 True False 9 3 0 -0.00813865 -3e+06 0 H

Detailed results on the bars

model.data(at = "bars")
conn direction geometry props state
c1 c2 dx dy length volume density mass section elongation failure strain stress tension
0 A C 1 0 3 0.15 7800 1170 0.05 0.000285714 False 9.52381e-05 2e+07 1e+06
1 C D 0 1 3 0.15 7800 1170 0.05 0 False 0 0 0
2 A D 0.707107 0.707107 4.24264 0.212132 7800 1654.63 0.05 -0.000571429 False -0.000134687 -2.82843e+07 -1.41421e+06
3 C E 1 0 3 0.15 7800 1170 0.05 0.000285714 False 9.52381e-05 2e+07 1e+06
4 D F 1 0 3 0.15 7800 1170 0.05 -0.000571429 False -0.000190476 -4e+07 -2e+06
5 D E 0.707107 -0.707107 4.24264 0.212132 7800 1654.63 0.05 0.000571429 False 0.000134687 2.82843e+07 1.41421e+06
6 E F 0 1 3 0.15 7800 1170 0.05 -0.000285714 False -9.52381e-05 -2e+07 -1e+06
7 E G 1 0 3 0.15 7800 1170 0.05 0.000571429 False 0.000190476 4e+07 2e+06
8 F H 1 0 3 0.15 7800 1170 0.05 -0.000857143 False -0.000285714 -6e+07 -3e+06
9 F G 0.707107 -0.707107 4.24264 0.212132 7800 1654.63 0.05 0.000571429 False 0.000134687 2.82843e+07 1.41421e+06
10 G H 0 1 3 0.15 7800 1170 0.05 0 False 0 0 0

Dead (i. e. structural) mass

m0 = model.mass()
m0 * 1.e-3 # Mass in tons !
14.323889603929565

Questions

Question 1: Verify that the yield stress is not exceeded anywhere, do you think this structure has an optimimum weight ? You can use the state/failure data available on the whole model.

# Example:
model.data(at = "bars").state.failure.values

#...
array([False, False, False, False, False, False, False, False, False,
       False, False], dtype=object)

Question 2: Modify all the cross sections at the same time in order to minimize weight while keeping acceptable stress level.

Question 3: We want to modify the position along the \(\vec y\) axis of the points \(D\), \(F\) and \(H\) in order to minimize the vertical displacement of the node \(G\) times the mass of the structure \(\alpha\):

\[\alpha = |u_y(G)| m\]

Where \(u_y(G)\) is the displacement of the node \(G\) along the \(\vec y\) axis and \(m\) the mass of the whole structure.

Do not further modify the sections determined in question 4. Comment the solution.

Question 4: Same question with displacements also along \(\vec x\) of \(C\), \(D\), \(E\) and \(F\). Is it better ?

Question 5: You can now try to perform topological optimization by removing/merging well chosen beams and nodes. In order to make the structure even more efficient.

Question 6: You are now asked to optimize the cross section along with the position of \(C\), \(D\), \(E\) and \(F\) in order to reach the yield stress in each individual beam.