Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • p.anaguano/informatik2022
  • a.engelke/informatik2022
  • p.schleinzer/informatik2022
  • n.rosenkranz/informatik2022
  • w.weber/informatik2022
  • xuhao.zhang/informatik2022
  • h.el-menuawy/informatik2022
  • saifalla.ibrahim/informatik2022
  • d.krause/informatik2022
  • p.schilling/informatik2022
  • j.tolke/informatik2022
  • f.luckau/informatik2022
  • danish.ahmad/informatik2022
  • emir.sagdani/informatik2022
  • d.griedel/informatik2022
  • j.mahnke/informatik2022
  • l.poehler/informatik2022
  • christoph.wrede/informatik2022
  • y.kummert/informatik2022
  • alexander.reisch/informatik2022
  • t.dickel/informatik2022
  • ni.petersen/informatik2022
  • markus.werner/informatik2022
  • s.mouammar/informatik2022
  • j.jahns/informatik2022
  • m.figueroa-castillo/informatik2022
  • b.hannan/informatik2022
  • v.lapschiess/informatik2022
  • j.hegner/informatik2022
  • g.paraschiv/informatik2022
  • e.abkaimov/informatik2022
  • l.krogmann/informatik2022
  • d.mizyed/informatik2022
  • h.almasri/informatik2022
  • a.mickan/informatik2022
  • f.shikh-alshabab/informatik2022
  • j.feldbausch/informatik2022
  • l.abdel-kader/informatik2022
  • jan.seibt/informatik2022
  • e.goekmen/informatik2022
  • nathanael.schenk/informatik2022
  • r.reksius/informatik2022
  • edmont.schulze/informatik2022
  • a.singh/informatik2022
  • p.christensen/informatik2022
  • m.woidt/informatik2022
46 results
Show changes
Showing
with 1547 additions and 0 deletions
Semester_2/Einheit_04/Pics/Cross-Over-2.gif

1.82 MiB

Semester_2/Einheit_04/Pics/Cross-Over-3.gif

1.68 MiB

Semester_2/Einheit_04/Pics/Deap_Architecture.png

22 KiB

Semester_2/Einheit_04/Pics/Generationmodel.png

54.4 KiB

Semester_2/Einheit_04/Pics/Mutation-1.gif

1.29 MiB

Semester_2/Einheit_04/Pics/Mutation-2.gif

1.16 MiB

Semester_2/Einheit_04/Pics/Mutation-3.gif

744 KiB

Semester_2/Einheit_04/Pics/Roulette.png

69.8 KiB

import numpy as np
import Pre
import Sol
import Post
def Run(Coord, ElmCon, A, E, BC, F):
"""
Perform finite element analysis of truss.
Parameters
----------
Coord : list
Nodal coordrindates. Each item is a tuple of (x,y)
coordinates of nodes.
ElmCon : list
Element connectivity table. Each item is a tuple
that contains first and second node number of bar.
A : list
Element cross section area table. Each item represents the cross section area
of bar. indices match with ElmCon.
E : list
Element elastic modulus table. Each item represents the elastic modulus
of bar. indices match with ElmCon.
theta : list
Element direction table. Each item represents the angle between the bar and
the global X axis in radians. indices match with ElmCon.
BC : list
Boundary conditions.
F : list
External forces of truss.
Returns
-------
U : ndarray
Nodal displacements, {U}.
sigma : ndarray
Element stresses
df : pandas dataframe
Geometry definition of the problem.
"""
L = Pre.LenCalc(Coord, ElmCon)
df = Pre.dataframe(ElmCon, A, E, L, Pre.angCalc(Coord, ElmCon))
df = Pre.ElmStf(df)
K = Pre.TotStf(df)
Ua = Sol.activeDisplacement(BC, K, F)
U = Post.RefineU(Ua, BC)
df = Post.Strain(U, df)
df = Post.Stress(df)
sigma = np.array(df['Stress'].tolist())
return U, sigma, df
def TotalWeight(Coord, ElmCon, rho, A):
return Pre.TrussWeight(A, Pre.LenCalc(Coord, ElmCon), rho)
%% Cell type:markdown id:a7d51c05-50c4-47e2-a928-34136b667f55 tags:
## **<font color='blue'>Problem des Handlungsreisenden</font>**
Das Problem des Handlungsreisenden - Traveling Salesman Problem (TSP) - versucht die kürzeste bzw. günstigste Rundreise durch einen Satz von $n$ Städten zu bestimmen.
Dabei darf in der Rundreise jede Stadt nur einmal besucht werden.
Die möglichen Verbindungen zwischen Städten sind in Form von Kanten gegeben, wobei die Städte den Knoten entsprechen.
Die Distanz zwischen zwei verbundenen Städten $i$ und $j$ ist durch die Kantenwichtung $B_{ij}$ gegeben
Eine Lösung wird durch die Städtefolge bzw. Permutation $X = [x_1, x_2, ... , x_n]$ beschrieben.
Der Startpunkt kann zufällig gewählt werden, da es eine Rundreise ist.
Die Gesamtdistanz bzw. Kosten $C(X)$ für eine Lösung $X$ errechnen sich aus der Summe der Wichtungen der durchlaufenden Kanten:
\begin{eqnarray}
C(X) \;=\; \sum_{i=1}^{n-1} B_{x_i x_{i+1}} + B_{x_n x_{1}}
\quad \rightarrow \quad \min
\end{eqnarray}
Der letzte Summand berücksichtigt die Rückreise von der letzten zur ersten Stadt.
### **<font color='blue'>Beispiel mit vier Städten</font>**
<center>
<img src="Pics/TSP-Beispiel.png" width="25%" height="25%">
</center>
%% Cell type:code id:e81eac5e-9fde-4c08-9087-771f76a08438 tags:
``` python
# Gewichtete Adjazenzmatrix
B = [[ 0, 4, 9, 8],
[ 4, 0, 12, 2],
[ 9, 12, 0, 10],
[ 8, 2, 10, 0] ]
```
%% Cell type:markdown id:666e1a6e-6853-40cc-a459-4a6d42a009c2 tags:
### **<font color='blue'>Brute Force</font>**
Die Brute-Force-Komplexität umfaßt hier die Anzahl der möglichen Permutationen, die sich über die Fakulätsfunktion ergibt.
Diese liegt jenseits der $2^n$ Komplexität, so dass Brute-Force für größere Probleme nicht ausführbar ist.
%% Cell type:code id:d8bd1a88-9434-4eb8-bb87-543b758efefd tags:
``` python
import math, itertools
import sys, time
def route_length( route ):
distance = distance_matrix[route[-1]][route[0]]
for town_i, town_j in zip(route[0:-1], route[1:]):
distance += distance_matrix[town_i][town_j]
return distance,
def TSP_brute_force( ):
num_act, num_tot = 0, math.factorial(len(distance_matrix)-1)
start_time = time.time()
min_length = math.inf
perm = itertools.permutations( range(1,len(distance_matrix)) )
for route in perm:
length, = route_length( (0,) + route )
num_act += 1
if length <= min_length:
min_length, min_route = length, route
print( "%9i %6.4f " % ( num_act, 100*num_act/num_tot ), min_length, min_route )
if time.time()-start_time > 30:
break
return ( min_length, (0,) + min_route )
distance_matrix = B
print( "Brute_Force: ", TSP_brute_force() )
```
%% Output
1 16.6667 34 (1, 2, 3)
2 33.3333 25 (1, 3, 2)
4 66.6667 25 (2, 3, 1)
Brute_Force: (25, (0, 2, 3, 1))
%% Cell type:markdown id:490397f9-a0c9-4a4e-964b-1b80254cd5cd tags:
### **<font color='blue'>Greedy</font>**
* Auswahl eines beliebigen Startpunktes j : Reiseweg X = [ j ]
* Einfügen der Stadt, die den mit ihr verlängerten Rundweg minimiert:
* Ersetze für jede Teilstrecke k im Reiseweg den Umweg über jede noch nicht besuchte Stadt i und bestimme jeweils die Gesamtstrecke
* Merke die Stadt i, die die kürzeste Gesamtstrecke liefert, und füge sie in den Reiseweg ein.
* Abbruch, wenn alle Städte einsortiert sind.
<center>
<img src="Pics/TSP-Greedy.png" width="85%" height="85%">
</center>
%% Cell type:code id:d9e1cb2d-3e38-41da-abd6-11d9035f74d3 tags:
``` python
def TSP_greedy( ):
route = [0] # Start-Stadt
for i in range(1,len(distance_matrix)): # Einfuegen von n-1 Staedten
min_length = math.inf
for section in range( 1, len(route)+1 ): # Alle vorhandenen Teilstrecken
for town in range( 1, len(distance_matrix) ): # werden durch eine
if town not in route: # noch nicht besuchte Stadt
current_route = route[:section] + [town] + route[section:] # ersetzt
length, = route_length( current_route )
if length < min_length:
min_length = length
opt_section, opt_town = section, town
route = route[:opt_section] + [opt_town] + route[opt_section:] # Stadt in Weg einfuegen
return min_length, route
distance_matrix = B
print( "Greedy: ", TSP_greedy() )
```
%% Output
Greedy: (25, [0, 2, 3, 1])
%% Cell type:code id:9027b030-d8ec-449a-9e41-26a81524b39c tags:
``` python
C = [# A Be Bo Br Dr Es Fr HH Ha Kö Le Mü Nü Sa St
[0,585,505,685,460,580,330,710,570,525,415, 65,170,370,170], # Augsburg
[0, 0,630,405,185,570,555,280,320,610,190,585,440,745,630], # Berlin
[0, 0, 0,340,545, 95,175,450,320, 25,465,560,390,215,350], # Bonn
[0, 0, 0, 0,460,255,455,110,120,320,355,750,595,555,650], # Bremen
[0, 0, 0, 0, 0,530,460,465,355,550,105,460,310,645,505], # Dresden
[0, 0, 0, 0, 0, 0,250,365,250, 75,450,640,465,310,425], # Essen
[0, 0, 0, 0, 0, 0, 0,485,345,195,380,395,225,200,205], # Frankfurt
[0, 0, 0, 0, 0, 0, 0, 0,150,425,385,780,605,670,655], # Hamburg
[0, 0, 0, 0, 0, 0, 0, 0, 0,295,225,635,465,540,525], # Hannover
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0,470,580,405,250,375], # Köln
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,415,265,565,505], # Leipzig
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,170,425,215], # München
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,375,215], # Nürnberg
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,220], # Saarbrücken
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]] # Stuttgart
for i in range(len(C)):
for j in range(len(C)):
C[j][i] = C[i][j]
distance_matrix = C
print( "Brute_Force: ", TSP_brute_force() )
```
%% Output
1 0.0000 5545 (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)
7 0.0000 5445 (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 11, 13, 14)
8 0.0000 5435 (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 11, 14, 13)
10 0.0000 5335 (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 14, 11)
76 0.0000 5300 (1, 2, 3, 4, 5, 6, 7, 8, 9, 13, 10, 12, 14, 11)
92 0.0000 5200 (1, 2, 3, 4, 5, 6, 7, 8, 9, 13, 14, 10, 12, 11)
95 0.0000 5155 (1, 2, 3, 4, 5, 6, 7, 8, 9, 13, 14, 12, 10, 11)
137 0.0000 5150 (1, 2, 3, 4, 5, 6, 7, 8, 10, 9, 13, 14, 11, 12)
138 0.0000 5045 (1, 2, 3, 4, 5, 6, 7, 8, 10, 9, 13, 14, 12, 11)
10087 0.0000 5040 (1, 2, 3, 4, 5, 6, 9, 7, 8, 10, 12, 11, 13, 14)
10088 0.0000 5030 (1, 2, 3, 4, 5, 6, 9, 7, 8, 10, 12, 11, 14, 13)
10090 0.0000 4930 (1, 2, 3, 4, 5, 6, 9, 7, 8, 10, 12, 13, 14, 11)
15138 0.0000 4925 (1, 2, 3, 4, 5, 6, 10, 7, 8, 9, 13, 14, 12, 11)
15858 0.0000 4895 (1, 2, 3, 4, 5, 6, 10, 8, 7, 9, 13, 14, 12, 11)
27364 0.0000 4865 (1, 2, 3, 4, 5, 6, 12, 10, 7, 8, 9, 13, 14, 11)
27484 0.0000 4835 (1, 2, 3, 4, 5, 6, 12, 10, 8, 7, 9, 13, 14, 11)
31684 0.0000 4805 (1, 2, 3, 4, 5, 6, 13, 9, 7, 8, 10, 12, 14, 11)
39842 0.0000 4770 (1, 2, 3, 4, 5, 6, 14, 13, 9, 7, 8, 10, 12, 11)
47164 0.0001 4720 (1, 2, 3, 4, 5, 7, 8, 10, 12, 6, 9, 13, 14, 11)
126858 0.0001 4670 (1, 2, 3, 4, 5, 9, 7, 8, 10, 6, 13, 14, 12, 11)
126892 0.0001 4610 (1, 2, 3, 4, 5, 9, 7, 8, 10, 12, 6, 13, 14, 11)
534978 0.0006 4590 (1, 2, 3, 4, 6, 10, 8, 7, 5, 9, 13, 14, 12, 11)
625804 0.0007 4575 (1, 2, 3, 4, 6, 12, 10, 7, 8, 5, 9, 13, 14, 11)
626524 0.0007 4530 (1, 2, 3, 4, 6, 12, 10, 8, 7, 5, 9, 13, 14, 11)
660244 0.0008 4500 (1, 2, 3, 4, 6, 13, 9, 5, 7, 8, 10, 12, 14, 11)
722882 0.0008 4465 (1, 2, 3, 4, 6, 14, 13, 9, 5, 7, 8, 10, 12, 11)
821658 0.0009 4445 (1, 2, 3, 4, 7, 8, 10, 5, 9, 6, 13, 14, 12, 11)
824524 0.0009 4420 (1, 2, 3, 4, 7, 8, 10, 12, 6, 5, 9, 13, 14, 11)
1860498 0.0021 4380 (1, 2, 3, 4, 10, 6, 7, 8, 5, 9, 13, 14, 12, 11)
1865538 0.0021 4355 (1, 2, 3, 4, 10, 6, 8, 7, 5, 9, 13, 14, 12, 11)
1905138 0.0022 4270 (1, 2, 3, 4, 10, 7, 8, 5, 6, 9, 13, 14, 12, 11)
1905256 0.0022 4245 (1, 2, 3, 4, 10, 7, 8, 5, 9, 6, 13, 12, 14, 11)
1905257 0.0022 4150 (1, 2, 3, 4, 10, 7, 8, 5, 9, 6, 13, 14, 11, 12)
1905258 0.0022 4045 (1, 2, 3, 4, 10, 7, 8, 5, 9, 6, 13, 14, 12, 11)
1945578 0.0022 4000 (1, 2, 3, 4, 10, 8, 7, 5, 9, 6, 13, 14, 12, 11)
11057897 0.0127 3955 (1, 2, 3, 7, 4, 10, 8, 5, 9, 6, 13, 14, 11, 12)
11057898 0.0127 3850 (1, 2, 3, 7, 4, 10, 8, 5, 9, 6, 13, 14, 12, 11)
11993164 0.0138 3840 (1, 2, 3, 7, 8, 4, 10, 12, 6, 5, 9, 13, 14, 11)
12136458 0.0139 3815 (1, 2, 3, 7, 8, 10, 4, 5, 9, 6, 13, 14, 12, 11)
12139324 0.0139 3755 (1, 2, 3, 7, 8, 10, 4, 12, 6, 5, 9, 13, 14, 11)
Brute_Force: (3755, (0, 1, 2, 3, 7, 8, 10, 4, 12, 6, 5, 9, 13, 14, 11))
%% Cell type:code id:3ab92bfc-5543-4350-99e8-ede2ce1e704a tags:
``` python
print( "Greedy: ", TSP_greedy() )
```
%% Output
Greedy: (2740, [0, 14, 13, 2, 9, 5, 3, 7, 8, 10, 1, 4, 6, 12, 11])
%% Cell type:markdown id:4387ebab-89b1-457c-8cd3-90614a742762 tags:
### **<font color='blue'>Genetischer Algorithmus</font>**
Der Lösungvektor ist eine Permuation der zu besuchenden Städte: jeder Eintrag ist nur einmal enthalten. Dies muss bei den Operatoren für Rekombinationen und Mutationen berücksichtig werden:
* **Operatoren**
* **Rekombination/Crossover** - in DEAP implementiert:
* `tools.cxPartialyMatched(ind1, ind2)` : Führt eine teilweise zutreffende Kreuzung an den gegebenen Individuen durch. Die beiden Individuen werden an Ort und Stelle verändert. Neben dem Austausch der Werte erfolgt auch ein Austausch, um die Validität der Individuen zu bewahren. Diese Kreuzung erwartet Sequenzindividuen von Indizes.
<center>
<img src="Pics/PartialMatch.png" width="65%" height="65%"/>
</center>
<br>
* **Mutationen** - in DEAP implementiert:
* `tools.mutShuffleIndexes(individual, indpb)` Tauscht die Attribute des eingegebenen Individuums und gibt die Mutante zurück. Es wird erwartet, dass das Individuum eine Sequenz ist. Das Argument `indpb` ist die Wahrscheinlichkeit, dass jedes Attribut verschoben wird. Normalerweise wird diese Mutation auf einen Vektor von Indizes angewendet.
<center>
<img src="Pics/Shuffle.png" width="70%" height="70%">
</center>
%% Cell type:code id:840067cd-bd26-4174-b0bf-7a23e29bde50 tags:
``` python
from deap import algorithms, base, creator, tools
import array, random, numpy
import matplotlib.pyplot as plt
distance_matrix = C
individual_size = len(distance_matrix)
def TSP_genetic():
random.seed( 42 )
creator.create( "FitnessMin", base.Fitness, weights=(-1.0,) )
creator.create( "Individual", array.array, typecode='i', fitness=creator.FitnessMin )
toolbox = base.Toolbox()
toolbox.register( "indices", random.sample, range(individual_size), individual_size ) # Attribute generator
# random.sample( population, k, *, counts=None )
# Return a k length list of unique elements chosen from the population sequence.
toolbox.register( "individual", tools.initIterate, creator.Individual, toolbox.indices )
toolbox.register( "population", tools.initRepeat, list, toolbox.individual)
toolbox.register( "mate", tools.cxPartialyMatched )
toolbox.register( "mutate", tools.mutShuffleIndexes, indpb=0.05 )
toolbox.register( "select", tools.selTournament, tournsize=3 )
toolbox.register( "evaluate", route_length )
pop = toolbox.population( n=200 )
hof = tools.HallOfFame( 1 )
stats = tools.Statistics( lambda ind: ind.fitness.values )
stats.register( "avg", numpy.mean )
stats.register( "min", numpy.min )
P_CROSSOVER = 0.7
P_MUTATION = 0.4
MAX_GENERATIONS = 40
population, logbook = algorithms.eaSimple( pop, toolbox,
cxpb=P_CROSSOVER, mutpb=P_MUTATION,
ngen=MAX_GENERATIONS,
stats=stats, halloffame=hof)
best = hof.items[0]
print( "-- Bestes Individuum : ", best )
print( "-- Beste Fitness : ", best.fitness.values[0] )
# extract statistics:
minFitnessValues, meanFitnessValues, nevals = logbook.select( "min", "avg", "nevals" )
for i in range(1,len(nevals)):
nevals[i] += nevals[i-1]
# plot statistics:
plt.plot( minFitnessValues, color='red' )
plt.plot( meanFitnessValues, color='green' )
plt.plot( nevals, color='blue' )
plt.xlabel( 'Generation' )
plt.ylabel( 'Min / Average Fitness' )
plt.title( 'Min and Average fitness over Generations' )
plt.grid()
plt.show()
TSP_genetic()
```
%% Output
gen nevals avg min
0 200 6090.88 4190
1 152 5781.12 4190
2 161 5535.55 4190
3 169 5440.1 4005
4 173 5410.18 3615
5 152 5287.93 3380
6 165 5328.25 3380
7 170 5337.45 3875
8 158 5311.65 3815
9 167 5163.77 3345
10 173 5229.48 3730
11 170 5067.82 3730
12 170 4985.25 3845
13 171 5007.88 3190
14 138 4884.43 3190
15 175 4905.48 3065
16 163 4763.82 3065
17 161 4632.45 2990
18 163 4502.12 2990
19 175 4411.9 2990
20 168 4430.62 2990
21 164 4365.73 2875
22 153 4199.82 2795
23 168 3880.3 2795
24 158 3654.2 2795
25 166 3608.25 2650
26 166 3385.55 2635
27 164 3272.43 2635
28 166 3131.47 2495
29 176 3105.82 2415
30 148 2984.72 2440
31 172 2998.6 2415
32 159 2888.9 2415
33 154 2855.93 2415
34 176 2898.4 2415
35 161 2843.53 2415
36 164 2702.65 2415
37 165 2628.35 2415
38 159 2592.55 2415
39 164 2564.7 2415
40 158 2616.55 2415
-- Bestes Individuum : Individual('i', [4, 1, 7, 3, 8, 5, 9, 2, 6, 13, 14, 0, 11, 12, 10])
-- Beste Fitness : 2415.0
%% Cell type:markdown id:9d8d5b86-efbb-4dd1-a1fb-ec7357314f8e tags:
## **<font color='blue'>Rucksackproblem mit einem Set als Lösungsvektor</font>**
Benutzt man ein `set` als Lösungsvektor bzw. zur Beschreibung eines Individuum,
d.h. es sind die Indizes der Gegenstände die im Rucksack sein sollen gespeichert,
so muss man darauf Rücksicht nehmen, dass die Länge des Lösungsvektors variieren kann.
Somit lassen sich viele Operatoren zur Rekombination und Mutation nicht anwenden und
man muss sich diese selbst konstruieren bzw. implementieren.
* **Operatoren**
* **Rekombination/Crossover**:
* `tools.cxSet(ind1, ind2)` : Die Rekombination erzeugt ein Kind aus der gemeinsame Schnittmenge der beiden Eltern und ein zweites Kind aus der Differenz der beiden Eltern. Das erste Kind enthält dann die gemeinsamen Einträge der Eltern und das zweite Kind die restlichen Einträge:
<center>
<img src="Pics/CxSet.png" width="65%" height="65%"/>
</center>
<br>
* **Mutation**:
* `tools.mutSet(individual)` Entfernt mit der der Wahrscheinlichkeit von 50% ein im `set` enthaltenes Element oder fügt ein zufällig gewähltes Element dem `set` hinzu. Es wird erwartet, dass das Individuum eine Sequenz ist.
Normalerweise wird diese Mutation auf einen Vektor von Indizes angewendet.
<center>
<img src="Pics/MutSet.png" width="70%" height="70%">
</center>
%% Cell type:code id:752131e4-74a7-4e7d-a996-df4ee4e9f17f tags:
``` python
random.seed( 42 ) # Für reproduzierbar zufällige Zahlen
# item, weight, value
if False:
items = [ ( "A", 100, 40 ),
( "B", 50, 35 ),
( "C", 45, 18 ),
( "D", 20, 4 ),
( "E", 10, 10 ),
( "F", 5, 2 ) ]
maxCapacity = 100
else:
items = [ ("map", 9, 150),
("compass", 13, 35),
("water", 153, 200),
("sandwich", 50, 160),
("glucose", 15, 60),
("tin", 68, 45),
("banana", 27, 60),
("apple", 39, 40),
("cheese", 23, 30),
("beer", 52, 10),
("suntan cream", 11, 70),
("camera", 32, 30),
("t-shirt", 24, 15),
("trousers", 48, 10),
("umbrella", 73, 40),
("waterproof trousers", 42, 70),
("waterproof overclothes", 43, 75),
("note-case", 22, 80),
("sunglasses", 7, 20),
("towel", 18, 12),
("socks", 4, 50),
("book", 30, 10) ]
maxCapacity = 400
NBR_ITEMS = len( items )
MAX_ITEM = len( items )
# Gesamtablauf
def knapsack_with_set():
def getKnapsackValue(individual):
weight = 0.0
value = 0.0
for item in individual:
weight += items[item][1]
value += items[item][2]
if weight > maxCapacity:
return 0, # Ensure overweighted bags are dominated
return value,
def cxSet(ind1, ind2):
"""Apply a crossover operation on input sets. The first child is the
intersection of the two sets, the second child is the difference of the
two sets.
"""
temp = set(ind1) # Used in order to keep type
ind1 &= ind2 # Intersection (inplace)
ind2 ^= temp # Symmetric Difference (inplace)
return ind1, ind2
def mutSet(individual):
"""Mutation that pops or add an element."""
if random.random() < 0.5:
if len(individual) > 0: # We cannot pop from an empty set
individual.remove(random.choice(sorted(tuple(individual))))
else:
individual.add(random.randrange(NBR_ITEMS))
return individual,
IND_INIT_SIZE = 5
creator.create("Fitness", base.Fitness, weights=(1.0,))
creator.create("Individual", set, fitness=creator.Fitness)
toolbox = base.Toolbox()
toolbox.register("attr_item", random.randrange, NBR_ITEMS)
toolbox.register("individual", tools.initRepeat, creator.Individual, toolbox.attr_item, IND_INIT_SIZE)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)
toolbox.register( "evaluate", getKnapsackValue )
toolbox.register( "mate", cxSet)
toolbox.register( "mutate", mutSet)
toolbox.register( "select", tools.selTournament, tournsize=3 ) # 3
# Konstanten
POPULATION_SIZE = 3*NBR_ITEMS
MAX_GENERATIONS = 10*NBR_ITEMS
P_CROSSOVER = 0.9 # 0.9
P_MUTATION = 0.1 # 0.1
HALL_OF_FAME_SIZE = 1
# Erzugen einer initialen Population (Generation 0)
population = toolbox.population( n=POPULATION_SIZE )
# Definition des Statistik-Objects
stats = tools.Statistics( lambda ind: ind.fitness.values )
stats.register( "avg", numpy.mean, axis=0 )
stats.register( "max", numpy.max , axis=0 )
# Definition des Hall-of-Fame Objekts
hof = tools.HallOfFame(HALL_OF_FAME_SIZE)
population, logbook = algorithms.eaSimple( population, toolbox,
cxpb=P_CROSSOVER, mutpb=P_MUTATION,
ngen=MAX_GENERATIONS,
stats=stats, halloffame=hof, verbose=True)
# print best solution found:
best = hof.items[0]
print( "++ Brute-Force Permutationen:", 2**NBR_ITEMS )
print( "-- Bestes Individuum : ", best )
print( "-- Beste Fitness : ", best.fitness.values )
# extract statistics:
maxFitnessValues, meanFitnessValues, nevals = logbook.select( "max", "avg", "nevals" )
for i in range(1,len(nevals)):
nevals[i] = nevals[i-1] + nevals[i]
print( "-- Summe nevals : ", nevals[-1] )
# plot statistics:
#sns.set_style("whitegrid")
plt.plot( maxFitnessValues, color='red' )
plt.plot( meanFitnessValues, color='green' )
#plt.plot( nevals, color='blue' )
plt.xlabel( 'Generation' )
plt.ylabel( 'Max / Average Fitness' )
plt.title( 'Max and Average fitness over Generations' )
plt.grid()
plt.show()
knapsack_with_set()
```
%% Output
gen nevals avg max
0 66 [283.83333333] [550.]
1 62 [288.81818182] [825.]
2 66 [321.27272727] [762.]
3 62 [268.25757576] [835.]
4 58 [324.56060606] [760.]
5 66 [264.18181818] [775.]
6 59 [334.18181818] [820.]
7 56 [362.71212121] [775.]
8 62 [357.3030303] [775.]
9 63 [386.18181818] [875.]
10 62 [309.75757576] [790.]
11 63 [330.84848485] [770.]
12 60 [328.81818182] [737.]
13 59 [358.27272727] [850.]
14 62 [326.95454545] [870.]
15 64 [358.01515152] [830.]
16 64 [324.59090909] [865.]
17 60 [365.09090909] [840.]
18 58 [428.43939394] [917.]
19 63 [363.51515152] [840.]
20 62 [351.04545455] [840.]
21 56 [355.53030303] [757.]
22 60 [355.27272727] [817.]
23 57 [341.71212121] [755.]
24 62 [291.71212121] [812.]
25 66 [248.77272727] [812.]
26 59 [308.16666667] [855.]
27 59 [330.48484848] [807.]
28 62 [361.74242424] [807.]
29 66 [352.98484848] [887.]
30 56 [407.16666667] [770.]
31 61 [412.24242424] [887.]
32 62 [346.74242424] [735.]
33 58 [367.27272727] [845.]
34 62 [410.10606061] [925.]
35 62 [356.89393939] [800.]
36 62 [372.96969697] [765.]
37 64 [384.25757576] [877.]
38 58 [399.28787879] [980.]
39 49 [431.06060606] [980.]
40 65 [427.25757576] [980.]
41 59 [436.74242424] [755.]
42 57 [387.92424242] [760.]
43 61 [402.83333333] [880.]
44 62 [420.83333333] [775.]
45 64 [432.57575758] [775.]
46 61 [433.22727273] [865.]
47 61 [425.15151515] [835.]
48 64 [387.65151515] [940.]
49 58 [433.60606061] [940.]
50 64 [383.86363636] [845.]
51 59 [395.34848485] [815.]
52 63 [393.1969697] [830.]
53 58 [389.68181818] [830.]
54 58 [385.04545455] [895.]
55 56 [392.46969697] [847.]
56 55 [418.60606061] [905.]
57 58 [369.51515152] [802.]
58 54 [390.98484848] [822.]
59 60 [398.12121212] [940.]
60 65 [339.37878788] [802.]
61 60 [345.84848485] [785.]
62 60 [369.31818182] [740.]
63 64 [339.46969697] [837.]
64 60 [371.89393939] [842.]
65 56 [371.71212121] [870.]
66 55 [431.59090909] [917.]
67 63 [384.87878788] [870.]
68 60 [390.48484848] [870.]
69 51 [443.60606061] [870.]
70 60 [438.34848485] [870.]
71 56 [400.06060606] [790.]
72 60 [388.77272727] [812.]
73 65 [365.83333333] [770.]
74 64 [361.03030303] [845.]
75 61 [348.63636364] [822.]
76 64 [341.04545455] [782.]
77 60 [363.98484848] [750.]
78 57 [399.92424242] [817.]
79 60 [365.68181818] [755.]
80 64 [384.18181818] [765.]
81 56 [407.66666667] [800.]
82 64 [334.21212121] [817.]
83 63 [375.5] [835.]
84 64 [379.71212121] [945.]
85 62 [347.83333333] [880.]
86 60 [387.12121212] [880.]
87 63 [395.03030303] [860.]
88 57 [404.31818182] [860.]
89 64 [365.53030303] [890.]
90 60 [362.12121212] [890.]
91 61 [421.81818182] [780.]
92 54 [449.84848485] [780.]
93 57 [426.66666667] [810.]
94 56 [422.27272727] [975.]
95 64 [429.01515152] [975.]
96 58 [465.98484848] [900.]
97 63 [462.98484848] [900.]
98 65 [460.07575758] [875.]
99 63 [440.45454545] [1030.]
100 53 [439.1969697] [885.]
101 64 [395.86363636] [825.]
102 58 [387.83333333] [825.]
103 59 [446.36363636] [805.]
104 64 [409.46969697] [805.]
105 60 [413.13636364] [825.]
106 61 [414.75757576] [830.]
107 60 [445.93939394] [830.]
108 60 [438.84848485] [880.]
109 60 [447.33333333] [855.]
110 58 [433.42424242] [795.]
111 64 [402.56060606] [850.]
112 61 [409.46969697] [885.]
113 58 [388.33333333] [895.]
114 63 [395.] [830.]
115 56 [386.13636364] [930.]
116 56 [376.06060606] [930.]
117 57 [425.15151515] [770.]
118 56 [414.77272727] [890.]
119 52 [411.36363636] [905.]
120 62 [416.81818182] [815.]
121 56 [437.34848485] [945.]
122 61 [450.22727273] [960.]
123 58 [417.1969697] [840.]
124 56 [441.06060606] [920.]
125 59 [469.92424242] [845.]
126 53 [450.15151515] [790.]
127 64 [417.15151515] [955.]
128 56 [469.83333333] [955.]
129 58 [485.22727273] [955.]
130 60 [425.22727273] [790.]
131 58 [420.68181818] [815.]
132 66 [399.77272727] [890.]
133 56 [397.04545455] [890.]
134 65 [428.25757576] [885.]
135 58 [426.66666667] [885.]
136 62 [402.1969697] [895.]
137 59 [426.59090909] [955.]
138 64 [428.63636364] [955.]
139 63 [380.22727273] [750.]
140 62 [353.25757576] [830.]
141 58 [354.84848485] [755.]
142 57 [358.81818182] [830.]
143 61 [422.34848485] [815.]
144 62 [348.63636364] [745.]
145 58 [374.95454545] [895.]
146 60 [376.28787879] [840.]
147 56 [375.] [950.]
148 58 [406.74242424] [950.]
149 64 [370.45454545] [950.]
150 56 [358.63636364] [775.]
151 61 [333.81818182] [845.]
152 55 [370.37878788] [785.]
153 57 [414.31818182] [835.]
154 58 [378.25757576] [725.]
155 64 [335.98484848] [885.]
156 66 [373.40909091] [785.]
157 58 [370.3030303] [905.]
158 62 [364.31818182] [830.]
159 60 [339.24242424] [775.]
160 62 [371.51515152] [815.]
161 64 [391.66666667] [815.]
162 63 [373.63636364] [855.]
163 62 [336.28787879] [755.]
164 63 [387.57575758] [860.]
165 58 [413.48484848] [840.]
166 58 [373.78787879] [865.]
167 59 [353.86363636] [865.]
168 60 [359.24242424] [790.]
169 58 [338.06060606] [810.]
170 64 [371.06060606] [760.]
171 59 [373.71212121] [750.]
172 61 [334.92424242] [885.]
173 60 [355.07575758] [895.]
174 56 [361.06060606] [895.]
175 55 [414.1969697] [895.]
176 59 [410.37878788] [895.]
177 61 [369.46969697] [860.]
178 60 [298.63636364] [800.]
179 62 [336.74242424] [805.]
180 60 [392.27272727] [815.]
181 61 [417.27272727] [835.]
182 63 [431.51515152] [835.]
183 59 [450.83333333] [835.]
184 58 [418.48484848] [830.]
185 61 [415.68181818] [795.]
186 60 [374.24242424] [880.]
187 54 [453.71212121] [880.]
188 57 [446.13636364] [880.]
189 61 [437.5] [875.]
190 60 [416.28787879] [875.]
191 64 [381.59090909] [755.]
192 52 [410.83333333] [870.]
193 63 [385.3030303] [880.]
194 56 [402.1969697] [805.]
195 58 [418.56060606] [815.]
196 53 [388.78787879] [815.]
197 61 [399.6969697] [815.]
198 64 [383.10606061] [685.]
199 60 [382.04545455] [850.]
200 59 [408.25757576] [800.]
201 62 [325.37878788] [800.]
202 59 [332.57575758] [800.]
203 63 [353.63636364] [720.]
204 63 [393.48484848] [780.]
205 64 [385.75757576] [805.]
206 64 [387.04545455] [870.]
207 53 [390.45454545] [945.]
208 57 [397.5] [820.]
209 61 [407.34848485] [820.]
210 62 [387.87878788] [675.]
211 57 [385.53030303] [685.]
212 59 [351.51515152] [695.]
213 61 [400.56060606] [740.]
214 63 [380.83333333] [855.]
215 60 [362.95454545] [855.]
216 66 [374.84848485] [750.]
217 61 [394.46969697] [845.]
218 62 [299.39393939] [700.]
219 59 [316.66666667] [875.]
220 51 [420.07575758] [865.]
++ Brute-Force Permutationen: 4194304
-- Bestes Individuum : Individual({0, 1, 2, 3, 4, 6, 10, 15, 16, 17, 18, 20})
-- Beste Fitness : (1030.0,)
-- Summe nevals : 13276
%% Cell type:markdown id:0f66ea44 tags:
## <font color='blue'> Anleitung zur Nutzung des Berechnungstools
Wir nutzen die Berechnung mit der Finiten-Elemente-Methode im Stile einer Blackbox, d.h. wir werden uns nicht mit den mathematischen Hintergründen oder der Implementierung beschäftigen. Wir werden lediglich erklären, wie man die zum größten Teil auf dem öffentlichen Projekt https://github.com/SaaadRaaa/Truss-Optimization basierenden FE-Funktionen nutzt, sodass wir sie z.B. für die Optimierung mit deap Verwenden können.
Dieses Tool erfordert numpy, matplotlib, und pandas. Sollte eines dieser Pakete noch nicht installiert sein, muss es über die Konsole (geöffnet im Jupyterlab bei Nutzung von Jupyterhub, oder der cmd bei Nutzung einer lokalen installation) mit `pip3 install numpy matplotlib pandas` installiert werden.
Die Berechnungsmethode `FETool.Run()` erhält als Eingabewerte die Geometrie (Knoten und Stäbe), Eigenschaften der Stäbe (Querschnittsfläche und E-Modul), sowie Eigenschaften der Knoten (Rand-/Lagerbedingung und angreifende Kräfte). Sie gibt eine Liste mit Knotenverschiebungen und eine Liste mit Spannungen der Stäbe zurück. Zudem wird ein pandas dataframe zurückgegeben, in dem noch weitere Resultate, z.B. die Dehnungen enthalten sind. Diesen werden wir jedoch nicht nicht verwenden.
Die Funktionsweise wird nun an zwei Beispielen gezeigt.
#### <font color = 'blue'> 1. Beispiel
Das erste Beispiel zeigt an ein einfaches Dreieck, um die Bedienung zu erklären:
%% Cell type:code id:3da4d537 tags:
``` python
# Das Paket ist nicht global installiert. Die Quelldateien muessen im selben Ordner liegen, wie das Notebook, das es verwendet
import FETool
# **** Geometrie-Definition (Knoten, Stäbe mit Flächen und E-Modul)
Coord = [(0,0),(2,0),(1,1)] # Knotenkoordinaten (Liste mit Tupeln)
ElmCon = [(1,2),(1,3),(2,3)] # Stäbe: Verbindungen zwischen Knoten. Knotennummern beginnen bei 1. (Liste mit Tupeln)
A = [1,2,3] # Querschnittsflaeche der Stäbe (Gleiche Reihenfolge wie ElmCon), (Liste mit Zahlen)
E = [100,100,100] # E-Modul der Stäbe (Gleiche Reihenfolge wie ElmCon), (Liste mit Zahlen)
# **** Lastfall-Definition: Lagerung und Kräfte
# Lagerung: Liste: [KnotenId, U.x: 1 oder U.y: 2, Wert = 0]
# Kraft: Liste: [KnotenId, F.x, F.y]
BC = [[1,1,0],[1,2,0],[2,2,0],[2,1,0]] # Zwei Festlager -> ueberbestimmt, das ist aber in der Elastostatik kein Problem
F = [[3,2,1]] # An Knoten 3 eine Kraft nach rechts+oben
# **** Berechnung
U, sigma, df = FETool.Run( Coord, ElmCon, A, E, BC, F ) # Berechnung der Verschiebungen und Spannungen
#Im zusätzlich zurückgegebenen dataframe df sind weitere Informationen enthalten, die wir hier aber nicht verwenden
```
%% Cell type:markdown id:6bfccb50 tags:
Es stehen Methoden zur Verfügung, um die Fachwerkstruktur, optional mit Querschnittsflächen, Verformungen oder Spannungen grafisch darstellen zu lassen. Diese Methoden benötigen je nach Art neben den Knoten und Verbindungen noch die Querschnittsfläche, die berechneten Verschiebungen oder die berechneten Spannungen:
%% Cell type:code id:1c094153 tags:
``` python
FETool.Post.ShowTruss( Coord, ElmCon ) # Stellt Fachwerk schematisch dar
```
%% Output
%% Cell type:code id:3d520aba tags:
``` python
FETool.Post.ShowTrussCross( Coord, ElmCon, A ) # Stellt Fachwerk dar und visualisiert den Querschnitt der Stäbe an.
```
%% Output
%% Cell type:code id:2724b4ca tags:
``` python
FETool.Post.ShowDeformedTruss(Coord, ElmCon, U) # Die Verformung wird standardmaessig mit Faktor 10 dargestellt.
```
%% Output
%% Cell type:code id:6ccd84b7 tags:
``` python
FETool.Post.ShowTrussStress( Coord, ElmCon, sigma ) # Rot sind Zugspannungen, Blau Druckspannungen, Weiß Nullstaebe beim aktuellen Lastfall.
```
%% Output
%% Cell type:markdown id:40173a9b tags:
Zudem steht eine Methode zur Verfügung, die unter Angabe der Knoten, Stäbe, deren Materialdichten (zuvor nicht benötigt) und Querschnittsflächen das Gewicht des Fachwerks berechnet:
%% Cell type:code id:709640da tags:
``` python
rho = [5 for i in range(len(ElmCon))] # Diese "List Comprehension" erzeugt eine Liste mit so vielen "5" wie Einträge in ElmCon vorhanden sind
gewicht = FETool.TotalWeight( Coord, ElmCon, rho, A )
print(f'Das Gewicht des Fachwerks beträgt {gewicht} kg.')
```
%% Output
Das Gewicht des Fachwerks beträgt 45.35533905932738 kg.
%% Cell type:markdown id:b17d8bce tags:
#### <font color = 'blue'> 2. Beispiel
Das zweite Beispiel zeigt ein praxisnäheres Beispiel einer kleinen Brücke. Es hat mehr Knoten und Stäbe, sowie passendere Größen für die Parameter (im SI System). Die Bedienung funktioniert aber identisch.
%% Cell type:code id:b75c857d tags:
``` python
# Ein etwas praxisnaeheres Beispiel einer kleinen Bruecke
Coord = [(0,0),(3,0),(6,0),(9,0),(1.5,2),(4.5,2),(7.5,2)] # zuerst die unteren Knoten, dann die oberen
ElmCon = [(1,2),(2,3),(3,4),(1,5),(5,2),(2,6),(6,3),(3,7),(7,4),(5,6),(6,7)] # zuerst die untere Linie, dann die diagonalen Linien, dann die obere Linie
A = [0.0015 for x in range(len(ElmCon))] # Flaeche in m**3. Entspricht 3*5mm*100mm. Fuer alle Elemente
E = [ 210e9 for x in range(len(ElmCon))] # E-Modul von Stahl in Pa.Fuer alle Elemente
BC = [[1,1,0],[1,2,0],[4,2,0]] # Fest-Los-Lagerung
F = [[2,0,-500000],[3,0,-500000]] # ca 100t in N verteilt auf die beiden mittleren unteren Knoten
U, sigma, df = FETool.Run(Coord, ElmCon, A, E, BC, F)
# Eine andere Verschiebungsskalierung kann per optionalen Parameter eingestellt werden. Hier z.B. Faktor 5
FETool.Post.ShowDeformedTruss(Coord, ElmCon, U, 5)
```
%% Output
%% Cell type:code id:7a2e7134 tags:
``` python
FETool.Post.ShowTrussStress(Coord, ElmCon, sigma)
```
%% Output
Semester_2/Einheit_05/Pics/CxSet.png

17.2 KiB

Semester_2/Einheit_05/Pics/MutSet.png

35.8 KiB

Semester_2/Einheit_05/Pics/PartialMatch.png

40.7 KiB

Semester_2/Einheit_05/Pics/Shuffle.png

54.9 KiB

Semester_2/Einheit_05/Pics/TSP-Beispiel.png

24.3 KiB

Semester_2/Einheit_05/Pics/TSP-Greedy.png

84.7 KiB

Semester_2/Einheit_05/Pics/Truss_layout_annotated.png

29.2 KiB

import numpy as np
from math import sin, cos
import matplotlib.pyplot as plt
import matplotlib
def RefineU(Ua, BC):
"""
Take active displacements and boundary conditions, return nodal displacements, {U}.
Parameters
----------
Ua : ndarray
Active (Unconstrained) displacements matrix, {Ua}.
BC : list
Boundary conditions.
Returns
-------
Unew : ndarray
Nodal displacements, {U}.
"""
# nd = len(BC) * 2
nd = len(BC)+Ua.shape[0]
Unew = np.zeros(nd)
BCnew = [(x[0]-1)*2+x[1]-1 for x in BC]
BCval = [x[2] for x in BC]
j,k = 0, 0
for i in range(nd):
if i in BCnew:
Unew[i] = BCval[j]
j += 1
else:
Unew[i] = Ua[k]
k += 1
return Unew
def RefineF(Fc, F):
"""
Take reaction forces and external forces, return nodal forces matrix, {F}.
Parameters
----------
Fc : ndarray
Reaction forces of truss (constrained forces matrix, {Fc}).
F : list
External forces of truss.
Returns
-------
Fnew : ndarray
Nodal forces, {F}.
"""
nd = len(F) * 2
Fnew = np.zeros(nd)
Fnew = np.concatenate((Fc, Fnew))
for i in F:
Fnew[(i[0]*2)-2] = i[1]
Fnew[(i[0]*2)-1] = i[2]
return Fnew
def Strain(U, df):
"""
Take nodal displacements and dataframe of the problem, return elements' strain.
Parameters
----------
U : ndarray
Nodal displacements.
df : pandas dataframe
Geometry definition of the problem.
Returns
-------
df : pandas dataframe
With strain of each element added to it.
"""
strain = []
for i in df.index:
node1 = df.iloc[i, 1][0]
node2 = df.iloc[i, 1][1]
Direction = df.iloc[i, 5]
Length = df.iloc[i, 2]
u1 = U[(2*node1)-2] * cos(Direction) + U[2*node1-1] * sin(Direction)
u2 = U[(2*node2)-2] * cos(Direction) + U[2*node2-1] * sin(Direction)
strain.append((u2-u1)/Length)
df.insert(loc=6, column='Strain', value=strain)
return df
def Stress(df):
"""
Take nodal displacements and dataframe of the problem, return elements' stress.
Parameters
----------
df : pandas dataframe
Geometry definition of the problem.
Returns
-------
df : pandas dataframe
With stress of each element added to it.
"""
stress = []
for i in df.index:
E = df.iloc[i, 4]
epsilon = df.iloc[i, 6]
stress.append(E * epsilon)
df.insert(loc=6, column='Stress', value=stress)
return df
def ShowTruss(Coord, ElmCon):
"""
Plot truss structure with node number and element number annotation.
Parameters
----------
Coord : list
Nodal coordrindates. Each item is a tuple of (x,y)
coordinates of nodes.
ElmCon : list
Element connectivity table. Each item is a tuple
that contains first and second node number of bar.
Returns
-------
None.
"""
x = [Coord[i][0] for i in range(len(Coord))]
y = [Coord[i][1] for i in range(len(Coord))]
ratio = (max(y)-min(y))/(max(x)-min(x))
plt.figure(figsize=(10, 10*ratio), dpi=50)
ax = plt.axes()
plt.ylim(np.min(y) - np.max(y)*0.1, np.max(y) + np.max(y)*0.1)
plt.xlim(np.min(x) - np.max(x)*0.1, np.max(x) + np.max(x)*0.1)
ax.scatter(x, y)
ma = np.max(x) * 0.01
for num in range(len(Coord)):
ax.annotate(str(num+1), (Coord[num][0] + ma, Coord[num][1] + ma),
bbox=dict(boxstyle="circle", alpha=0.1), fontsize=12)
for i, sele in enumerate(ElmCon):
ax.plot([Coord[sele[0]-1][0], Coord[sele[1]-1][0]],
[Coord[sele[0]-1][1], Coord[sele[1]-1][1]], 'k', lw=1)
ax.annotate(str(i+1), ((Coord[sele[0]-1][0]+Coord[sele[1]-1][0])/2 + ma, (Coord[sele[0]-1][1] +
Coord[sele[1]-1][1])/2 + ma), bbox=dict(boxstyle="square", fc="g", alpha=0.2), fontsize=12)
plt.show()
def ShowDeformedTruss(Coord, ElmCon, U, scale=10.0):
"""
Plot truss structure with node number and element number annotation.
Also plots the deformed truss structure after loading.
Parameters
----------
Coord : list
Nodal coordrindates. Each item is a tuple of (x,y)
coordinates of nodes.
ElmCon : list
Element connectivity table. Each item is a tuple
that contains first and second node number of bar.
U : ndarray
Nodal displacements.
scale : float, optional
Deformation scaling. The default is 10.0 .
Returns
-------
None.
"""
x = [Coord[i][0] for i in range(len(Coord))]
y = [Coord[i][1] for i in range(len(Coord))]
ratio = (max(y)-min(y))/(max(x)-min(x))
plt.figure(figsize=(10, 10*ratio), dpi=50)
ax = plt.axes()
ax.scatter(x, y)
ma = np.max(x) * 0.01
for num in range(len(Coord)):
ax.annotate(str(num+1), (x[num] + ma, y[num] + ma),
bbox=dict(boxstyle="circle", alpha=0.1), fontsize=12)
for i, sele in enumerate(ElmCon):
ax.plot([x[sele[0]-1], x[sele[1]-1]],
[y[sele[0]-1], y[sele[1]-1]], 'k', lw=1)
ax.annotate(str(i+1), ((x[sele[0]-1]+x[sele[1]-1])/2 + ma, (y[sele[0]-1]+y[sele[1]-1]
)/2 + ma), bbox=dict(boxstyle="square", fc="g", alpha=0.2), fontsize=12)
U = U.reshape((-1, 2))*scale
newx = [tx+U[i, 0] for i, tx in enumerate(x)]
newy = [ty+U[i, 1] for i, ty in enumerate(y)]
ax.scatter(newx, newy)
for i, sele in enumerate(ElmCon):
ax.plot([newx[sele[0]-1], newx[sele[1]-1]],
[newy[sele[0]-1], newy[sele[1]-1]], 'r:', lw=1)
plt.show()
def ShowTrussCross(Coord, ElmCon, A):
"""
Plot truss structure with node number and element number annotation.
Maps elements cross-sections to linewidth between [0,5].
Parameters
----------
Coord : list
Nodal coordrindates. Each item is a tuple of (x,y)
coordinates of nodes.
ElmCon : list
Element connectivity table. Each item is a tuple
that contains first and second node number of bar.
A : list
Elements Cross-sections.
Returns
-------
None.
"""
mx = max(A)
scale_factor= 10
#normalA = [(((b-a)*(x-mn))/(mx-mn))+a for x in A]
normalA = [(scale_factor/mx) * x for x in A]
x = [Coord[i][0] for i in range(len(Coord))]
y = [Coord[i][1] for i in range(len(Coord))]
ratio = (max(y)-min(y))/(max(x)-min(x))
plt.figure(figsize=(10, 10*ratio), dpi=50)
ax = plt.axes()
plt.ylim(np.min(y) - np.max(y)*0.1, np.max(y) + np.max(y)*0.1)
plt.xlim(np.min(x) - np.max(x)*0.1, np.max(x) + np.max(x)*0.1)
ax.scatter(x, y)
ma = np.max(x) * 0.01
for num in range(len(Coord)):
ax.annotate(str(num+1), (Coord[num][0] + ma, Coord[num][1] + ma),
bbox=dict(boxstyle="circle", alpha=0.1), fontsize=12)
for i, sele in enumerate(ElmCon):
ax.plot([Coord[sele[0]-1][0], Coord[sele[1]-1][0]],
[Coord[sele[0]-1][1], Coord[sele[1]-1][1]], 'k', lw=normalA[i])
ax.annotate(str(i+1), ((Coord[sele[0]-1][0]+Coord[sele[1]-1][0])/2 + ma, (Coord[sele[0]-1][1] +
Coord[sele[1]-1][1])/2 + ma), bbox=dict(boxstyle="square", fc="g", alpha=0.2), fontsize=12)
plt.show()
def PlotWDS(w, d, s):
"""
Plot structure weight vs. maximum deflection vs. maximum stress where
maximum stresses are mapped to markers size between [20,80].
Parameters
----------
w : list
A list of weights.
d : list
A list of maximum deflections.
s : list
A list of maximum Stresses.
Returns
-------
None.
"""
plt.rcParams["figure.figsize"] = (5, 5)
mx, mn = max(s), min(s)
b, a = 80, 20
nst = [(((b-a)*(x-mn))/(mx-mn))+a for x in s]
plt.figure()
ax = plt.axes()
ax.scatter(w, d, s=nst, alpha=0.3)
plt.xlabel('Weight (lb.)')
plt.ylabel('Maximum Deflection (in.)')
def PlotWD(w, D):
"""
Plot structure weight vs. maximum deflection.
Parameters
----------
w : list
A list of weights.
D : list
A list of maximum deflections.
Returns
-------
None.
"""
plt.rcParams["figure.figsize"] = (8, 5*len(w))
figure, axis = plt.subplots(len(w), 1)
for j in range(len(w)):
axis[j].plot(w[j], D[j])
axis[j].set_title("Bar %s" % (j+1))
for ax in axis.flat:
ax.set(xlabel='Weight (lb.)', ylabel='Maximum Deflection (in.)')
def ShowTrussElast(Coord, ElmCon, E):
"""
Plot truss structure with node number and element number annotation.
Maps elements elasticity modulus to colors of black or red.
Parameters
----------
Coord : list
Nodal coordrindates. Each item is a tuple of (x,y)
coordinates of nodes.
ElmCon : list
Element connectivity table. Each item is a tuple
that contains first and second node number of bar.
E : list
Elements' elastic modulus.
Returns
-------
None.
"""
mx, mn = max(E), min(E)
b, a = 5, 1
normalA = [(((b-a)*(x-mn))/(mx-mn))+a for x in E]
for i in range(len(normalA)):
if normalA[i] == 1:
normalA[i] = 'r'
else:
normalA[i] = 'k'
x = [Coord[i][0] for i in range(len(Coord))]
y = [Coord[i][1] for i in range(len(Coord))]
ratio = (max(y)-min(y))/(max(x)-min(x))
plt.figure(figsize=(10, 10*ratio), dpi=50)
ax = plt.axes()
plt.ylim(np.min(y) - np.max(y)*0.1, np.max(y) + np.max(y)*0.1)
plt.xlim(np.min(x) - np.max(x)*0.1, np.max(x) + np.max(x)*0.1)
ax.scatter(x, y)
ma = np.max(x) * 0.01
for num in range(len(Coord)):
ax.annotate(str(num+1), (Coord[num][0] + ma, Coord[num][1] + ma),
bbox=dict(boxstyle="circle", alpha=0.1), fontsize=12)
for i, sele in enumerate(ElmCon):
ax.plot([Coord[sele[0]-1][0], Coord[sele[1]-1][0]],
[Coord[sele[0]-1][1], Coord[sele[1]-1][1]], normalA[i])
ax.annotate(str(i+1), ((Coord[sele[0]-1][0]+Coord[sele[1]-1][0])/2 + ma, (Coord[sele[0]-1][1] +
Coord[sele[1]-1][1])/2 + ma), bbox=dict(boxstyle="square", fc="g", alpha=0.2), fontsize=12)
def ShowTrussStress(Coord, ElmCon, sigma):
"""
Plot truss structure with node number and element number annotation.
Maps elements elasticity modulus to colors of black or red.
Parameters
----------
Coord : list
Nodal coordrindates. Each item is a tuple of (x,y)
coordinates of nodes.
ElmCon : list
Element connectivity table. Each item is a tuple
that contains first and second node number of bar.
sigma : list
Elements' stress values.
Returns
-------
None.
"""
mx = max(map(abs,sigma))
normalA = [0.5+x*(0.5/mx) for x in sigma]
colormap = matplotlib.cm.get_cmap('seismic')
x = [Coord[i][0] for i in range(len(Coord))]
y = [Coord[i][1] for i in range(len(Coord))]
ratio = (max(y)-min(y))/(max(x)-min(x))
plt.figure(figsize=(10, 10*ratio), dpi=50)
ax = plt.axes()
plt.ylim(np.min(y) - np.max(y)*0.1, np.max(y) + np.max(y)*0.1)
plt.xlim(np.min(x) - np.max(x)*0.1, np.max(x) + np.max(x)*0.1)
ax.scatter(x, y)
ma = np.max(x) * 0.01
for num in range(len(Coord)):
ax.annotate(str(num+1), (Coord[num][0] + ma, Coord[num][1] + ma),
bbox=dict(boxstyle="circle", alpha=0.1), fontsize=12)
for i, sele in enumerate(ElmCon):
ax.plot([Coord[sele[0]-1][0], Coord[sele[1]-1][0]],
[Coord[sele[0]-1][1], Coord[sele[1]-1][1]], color = colormap(normalA[i]), linewidth = 4)
ax.annotate(str(i+1), ((Coord[sele[0]-1][0]+Coord[sele[1]-1][0])/2 + ma, (Coord[sele[0]-1][1] +
Coord[sele[1]-1][1])/2 + ma), bbox=dict(boxstyle="square", fc="g", alpha=0.2), fontsize=12)
import numpy as np
import pandas as pd
from math import sin, cos, pi, atan
def LenCalc(Coord, ElmCon):
"""
Calculate the lengths of bar elements.
Parameters
----------
Coord : list
Nodal coordrindates. Each item is a tuple of (x,y)
coordinates of nodes.
ElmCon : list
Element connectivity table. Each item is a tuple
that contains first and second node number of bar.
Returns
-------
L : list
Element length table. Each item represents the length
of bar. indices match with ElmC.
"""
L = list()
for elem in ElmCon:
slength = (Coord[elem[0]-1][0]-Coord[elem[1]-1][0])**2 +\
(Coord[elem[0]-1][1]-Coord[elem[1]-1][1])**2
L.append(np.sqrt(slength))
return L
def angCalc(Coord, ElmCon):
"""
Calculate the orientations of bar elements.
Parameters
----------
Coord : list
Nodal coordrindates. Each item is a tuple of (x,y)
coordinates of nodes.
ElmCon : list
Element connectivity table. Each item is a tuple
that contains first and second node number of bar.
Returns
-------
angs : list
Element direction table. Each item represents the angle between the bar and
the global X axis in radians. indices match with ElmCon.
"""
angs = list()
for elem in ElmCon:
n1 = Coord[elem[0]-1]
n2 = Coord[elem[1]-1]
if n1[0] == n2[0]:
angs.append(pi/2)
elif n1[1] == n2[1]:
angs.append(0)
else:
angs.append(atan((n2[1]-n1[1])/(n2[0]-n1[0])))
return angs
def dataframe(ElmC, A, E, L, theta):
"""
Take each element's connectivity, cross section area, elastic modulus, length and direction,
return a pandas dataframe containing geometry definition of the problem.
Parameters
----------
ElmC : list
Element connectivity table. Each item is a tuple
that contains first and second node number of bar.
A : list
Element cross section area table. Each item represents the cross section area
of bar. indices match with ElmC.
E : list
Element elastic modulus table. Each item represents the elastic modulus
of bar. indices match with ElmC.
L : list
Element length table. Each item represents the length
of bar. indices match with ElmC.
theta : list
Element direction table. Each item represents the angle between the bar and
the global X axis in radians. indices match with ElmC.
Returns
-------
df : pandas dataframe
Geometry definition of the problem.
"""
d = {'#': list(range(1, len(ElmC)+1)), 'Connectivity': ElmC, 'Length': L, 'Cross section area': A, 'Young\'s modulus': E,
'Direction (Rad)': theta}
df = pd.DataFrame(data=d)
return df
def ElmStf(df):
"""
Take problem's dataframe and add a column containing each element's
stiffness matrix.
Parameters
----------
df : pandas dataframe
Geometry definition of the problem.
Returns
-------
df : pandas dataframe
With element stiffness matrices added to it.
"""
ke = []
for i in df.index:
k = df.iloc[i, 4] * df.iloc[i, 3] / df.iloc[i, 2]
ke.append(np.zeros((4, 4)))
ke[i][0, 0] = ke[i][2, 2] = k * cos(df.iloc[i, 5]) ** 2
ke[i][1, 1] = ke[i][3, 3] = k * sin(df.iloc[i, 5]) ** 2
ke[i][0, 1] = ke[i][1, 0] = k * cos(df.iloc[i, 5]) * sin(df.iloc[i, 5])
ke[i][2, 3] = ke[i][3, 2] = k * cos(df.iloc[i, 5]) * sin(df.iloc[i, 5])
ke[i][0, 2] = ke[i][2, 0] = -k * cos(df.iloc[i, 5]) ** 2
ke[i][1, 3] = ke[i][3, 1] = -k * sin(df.iloc[i, 5]) ** 2
ke[i][3, 0] = ke[i][2, 1] = -k * \
cos(df.iloc[i, 5]) * sin(df.iloc[i, 5])
ke[i][1, 2] = ke[i][0, 3] = -k * \
cos(df.iloc[i, 5]) * sin(df.iloc[i, 5])
df.insert(loc=6, column='Stiffness matrix', value=ke)
return df
def TotStf(df):
"""
Take problem's dataframe and return global stiffness matrix.
Parameters
----------
df : pandas dataframe
Geometry definition of the problem.
Returns
-------
K : ndarray
Global stiffness matrix.
"""
nd = max(df['Connectivity'].max())
K = np.zeros((2*nd, 2*nd))
for num in df.index:
i = df.iloc[num, 1][0]-1
j = df.iloc[num, 1][1]-1
K[2*i, 2*i] += df.iloc[num, 6][0, 0]
K[2*i+1, 2*i+1] += df.iloc[num, 6][1, 1]
K[2*i+1, 2*i] += df.iloc[num, 6][0, 1]
K[2*i, 2*i+1] += df.iloc[num, 6][1, 0]
K[2*j, 2*j] += df.iloc[num, 6][2, 2]
K[2*j+1, 2*j+1] += df.iloc[num, 6][3, 3]
K[2*j+1, 2*j] += df.iloc[num, 6][2, 3]
K[2*j, 2*j+1] += df.iloc[num, 6][3, 2]
K[2*j, 2*i] += df.iloc[num, 6][0, 2]
K[2*j+1, 2*i+1] += df.iloc[num, 6][1, 3]
K[2*j+1, 2*i] += df.iloc[num, 6][3, 0]
K[2*j, 2*i+1] += df.iloc[num, 6][2, 1]
K[2*i, 2*j] += df.iloc[num, 6][2, 0]
K[2*i+1, 2*j+1] += df.iloc[num, 6][3, 1]
K[2*i+1, 2*j] += df.iloc[num, 6][1, 2]
K[2*i, 2*j+1] += df.iloc[num, 6][0, 3]
return K
def TrussWeight(A, L, density):
"""
Calculate truss weight.
Parameters
----------
A : list
A list of elements cross-sections.
L : list
A list of elements weights.
density : list
Material density.
Returns
-------
_ : float
Structure Weight.
"""
return sum([lA*lL*ldensity for lA, lL, ldensity in zip(A, L, density)])