3  Die Federkette

Häng eine Reihe gleicher Federn hintereinander — wie die Federn in einem alten Bettgestell oder die Glieder einer gedehnten Fahrradkette. Halte das linke Ende fest und zieh am rechten. Was passiert? Das rechte Glied wandert am weitesten, das nächste etwas weniger, und ganz links, am Auflager, bewegt sich nichts. Jedes Glied spürt den Zug am Ende, aber jedes verschiebt sich anders weit.

Jetzt die Frage: Wie rechnet man aus, wie weit sich jedes einzelne Glied verschiebt? Nicht nur das erste und das letzte, sondern jedes dazwischen.

1952 stand ein Team bei Boeing vor genau dieser Frage — nur ging es nicht um Federn, sondern um einen Flugzeugflügel, und nicht um Verschiebung von Hand, sondern um Hunderte gekoppelte Bauteile. Ihre Antwort benutzt heute jedes FEM-Programm der Welt, und sie ist erstaunlich einfach. In diesem Kapitel bauen wir sie an der Federkette komplett nach — mit nichts als Schulalgebra. Keine Ableitung, kein Integral, keine einzige krumme Formel.

Code
import matplotlib.pyplot as plt

def feder(ax, x0, x1, y, windungen=6, amplitude=0.05, farbe="black", lw=1.6):
    """Zeichnet eine Feder als Zickzack zwischen zwei Knoten."""
    punkte_x = [x0, x0 + 0.12 * (x1 - x0)]
    punkte_y = [y, y]
    innen_start = x0 + 0.12 * (x1 - x0)
    innen_ende = x0 + 0.88 * (x1 - x0)
    zacken = 2 * windungen
    for i in range(zacken + 1):
        anteil = i / zacken
        punkte_x.append(innen_start + anteil * (innen_ende - innen_start))
        if i % 2 == 0:
            punkte_y.append(y + amplitude)
        else:
            punkte_y.append(y - amplitude)
    punkte_x.append(x1)
    punkte_y.append(y)
    ax.plot(punkte_x, punkte_y, color=farbe, lw=lw, solid_capstyle="round")

fig, ax = plt.subplots(figsize=(9.0, 3.4))

ruhe = [0.0, 1.0, 2.0, 3.0]
verschiebung = [0.0, 0.35, 0.70, 1.05]   # optisch ueberhoeht, nur schematisch
gezogen = [ruhe[i] + verschiebung[i] for i in range(4)]
y = 0.0

# Auflager links (schraffiert)
ax.add_patch(plt.Rectangle((-0.35, -0.45), 0.35, 0.9, hatch="///",
                           facecolor="lightsteelblue", edgecolor="black"))

# Ruhelage blass
for i in range(3):
    feder(ax, ruhe[i], ruhe[i + 1], y + 0.0, farbe="0.75", lw=1.0)
for i in range(4):
    ax.plot(ruhe[i], y, "o", ms=11, color="0.85", markeredgecolor="0.6",
            zorder=3)

# Gezogener Zustand kraeftig, leicht nach oben versetzt fuer die Sichtbarkeit
yv = 0.0
for i in range(3):
    feder(ax, gezogen[i], gezogen[i + 1], yv, farbe="tab:blue", lw=1.8)
for i in range(4):
    ax.plot(gezogen[i], yv, "o", ms=13, color="tab:blue",
            markeredgecolor="black", zorder=4)
    ax.text(gezogen[i], yv + 0.16, "Knoten %d" % (i + 1), ha="center",
            fontsize=9)

# Verschiebungspfeile
for i in range(1, 4):
    ax.annotate("", xy=(gezogen[i], yv - 0.28), xytext=(ruhe[i], yv - 0.28),
                arrowprops=dict(arrowstyle="-|>", color="tab:green", lw=2.0))
ax.text(2.4, yv - 0.42, "Verschiebungen", color="tab:green", fontsize=10)

# Zugkraft rechts
ax.annotate("", xy=(gezogen[3] + 0.6, yv), xytext=(gezogen[3], yv),
            arrowprops=dict(arrowstyle="-|>", color="tab:red", lw=2.4))
ax.text(gezogen[3] + 0.62, yv + 0.02, "50 N", color="tab:red", fontsize=11,
        va="center")

# Federbeschriftung
for i in range(3):
    ax.text((ruhe[i] + ruhe[i + 1]) / 2, yv + 0.30, "Feder %d" % (i + 1),
            ha="center", fontsize=9, color="0.4")

ax.set_xlim(-0.55, 4.9)
ax.set_ylim(-0.6, 0.55)
ax.set_aspect("equal")
ax.axis("off")
plt.tight_layout()
plt.show()
Abbildung 3.1: Die Federkette des Kapitels: drei gleiche Federn, vier Knoten. Knoten 1 steckt fest im Auflager (links, schraffiert), am Knoten 4 zieht eine Kraft von 50 N nach rechts. Die blassen Umrisse zeigen die Ruhelage, die kräftigen Federn den gezogenen Zustand; die grünen Pfeile sind die Verschiebungen, die wir ausrechnen wollen. Sie wachsen von links nach rechts — je weiter vom Auflager, desto weiter der Weg.

Was man hier sieht: unser Rechenobjekt für dieses ganze Kapitel. Drei Federn, vier Knoten, links fest, rechts gezogen. Bekannt ist die Kraft (50 N) und dass Knoten 1 sich nicht bewegt; gesucht sind die drei grünen Pfeile — die Verschiebungen der Knoten 2, 3 und 4. Am Ende des Kapitels wirst du sie von Hand ausrechnen können, und du wirst dabei genau die Maschinerie benutzt haben, die auch im Flugzeugflügel steckt.

Lernziele

Nach diesem Kapitel kannst du …

  1. … für eine einzelne Feder ihre 2×2-Elementmatrix aus dem Hookeschen Gesetz aufschreiben,
  2. … drei Federn zu einem 4×4-Gleichungssystem zusammensetzen (assemblieren) und erklären, warum sich Nachbar-Federn auf der Diagonale addieren,
  3. … eine feste Einspannung als Randbedingung ins System einbauen,
  4. … das entstehende System von Hand auf die freien Knoten reduzieren und lösen,
  5. … erklären, woher die FEM historisch kommt und was „Element” im Flugzeugbau der 1950er bedeutete.
WarnungNaheliegende Vermutung

Vermutung: „Je mehr Federn ich anhänge, desto grundlegend komplizierter wird die Rechnung. Bei zwanzig Federn brauche ich eine ganz andere, viel schwierigere Methode als bei zwei.”

Warum sie naheliegt: Zwanzig gekoppelte Bauteile, jedes zieht am nächsten — das klingt nach einem hoffnungslos verhedderten Knäuel aus Wechselwirkungen, das mit jeder neuen Feder unübersichtlicher wird.

Was stattdessen stimmt: Jede Feder liefert denselben winzigen Baustein — eine 2×2-Tabelle mit vier Zahlen. Mehr Federn heißt nur: mehr gleiche Bausteine, an der richtigen Stelle aufaddiert. Der Rechenweg für zwanzig Federn ist Zeile für Zeile derselbe wie für zwei, nur öfter wiederholt. Genau diese stumpfe Gleichförmigkeit ist es, die die FEM computertauglich macht: Ein Computer tut gern tausendmal dasselbe. Wir zeigen das in diesem Kapitel Schritt für Schritt.

3.1 Eine einzelne Feder

Fangen wir so klein an wie möglich: mit einer Feder. Sie hat zwei Enden, und an jedem Ende sitzt ein Knoten. Der linke Knoten hat die Verschiebung \(u_1\), der rechte die Verschiebung \(u_2\) — beide gemessen von der Ruhelage aus, in Millimetern. Eine positive Verschiebung heißt „nach rechts gewandert”.

Was die Feder tut, hängt nur von einer Größe ab: wie weit sie gedehnt ist. Und gedehnt ist sie um den Unterschied der beiden Enden, \(u_2 - u_1\). Verschieben sich beide Enden gleich weit, wandert die Feder als Ganzes, ohne sich zu dehnen — dann ist \(u_2 - u_1 = 0\), und sie ist kräftefrei.

Das Kraftgesetz einer Feder ist das Hookesche Gesetz, und es ist so einfach, wie ein Naturgesetz nur sein kann: Die Kraft ist proportional zur Dehnung.

Code
import matplotlib.pyplot as plt

def feder(ax, x0, x1, y, windungen=7, amplitude=0.08, farbe="black", lw=1.8):
    """Zeichnet eine Feder als Zickzack zwischen zwei Knoten."""
    px = [x0, x0 + 0.14 * (x1 - x0)]
    py = [y, y]
    a = x0 + 0.14 * (x1 - x0)
    b = x0 + 0.86 * (x1 - x0)
    zacken = 2 * windungen
    for i in range(zacken + 1):
        anteil = i / zacken
        px.append(a + anteil * (b - a))
        py.append(y + amplitude if i % 2 == 0 else y - amplitude)
    px.append(x1)
    py.append(y)
    ax.plot(px, py, color=farbe, lw=lw, solid_capstyle="round")

fig, ax = plt.subplots(figsize=(8.5, 3.0))

# Ruhelage blass
feder(ax, 0.0, 2.0, 0.0, farbe="0.75", lw=1.1)
for x in (0.0, 2.0):
    ax.plot(x, 0.0, "o", ms=12, color="0.85", markeredgecolor="0.6")

# Gedehnt: linker Knoten leicht nach rechts (u1), rechter weiter (u2)
u1, u2 = 0.35, 1.1
feder(ax, 0.0 + u1, 2.0 + u2, 0.0, farbe="tab:blue")
for x, name in ((0.0 + u1, "Knoten 1"), (2.0 + u2, "Knoten 2")):
    ax.plot(x, 0.0, "o", ms=14, color="tab:blue", markeredgecolor="black",
            zorder=4)
    ax.text(x, 0.20, name, ha="center", fontsize=10)

# Verschiebungspfeile
ax.annotate("", xy=(0.0 + u1, -0.32), xytext=(0.0, -0.32),
            arrowprops=dict(arrowstyle="-|>", color="tab:green", lw=2.0))
ax.text(0.0 + u1 / 2, -0.48, "u1", color="tab:green", ha="center", fontsize=11)
ax.annotate("", xy=(2.0 + u2, -0.32), xytext=(2.0, -0.32),
            arrowprops=dict(arrowstyle="-|>", color="tab:green", lw=2.0))
ax.text(2.0 + u2 / 2, -0.48, "u2", color="tab:green", ha="center", fontsize=11)

ax.set_xlim(-0.4, 3.6)
ax.set_ylim(-0.65, 0.4)
ax.set_aspect("equal")
ax.axis("off")
plt.tight_layout()
plt.show()
Abbildung 3.2: Eine einzelne Feder mit ihren beiden Knoten. Links die Ruhelage (blass), rechts der gedehnte Zustand (blau). Der linke Knoten ist um u1 gewandert, der rechte um u2; die Dehnung der Feder ist der Unterschied u2 minus u1. Genau um diesen Unterschied ist die Feder länger geworden.

Was man hier sieht: Nur der Unterschied der beiden Verschiebungen dehnt die Feder. Der linke Knoten ist ein Stück nach rechts gewandert (\(u_1\)), der rechte weiter (\(u_2\)); die Feder ist um \(u_2 - u_1\) länger geworden. Verschöben sich beide gleich weit, bliebe die Feder gleich lang.

In Formeln: Zieht man am rechten Knoten, so ist die Kraft \(F\), mit der man ziehen muss, um ihn um \(u_2 - u_1\) weiter als den linken zu bringen,

\[ F \;=\; k \cdot (u_2 - u_1), \]

wobei \(k\) die Steifigkeit der Feder ist — die Zahl, die sagt, wie störrisch sie sich der Dehnung widersetzt. Eine steife Feder hat ein großes \(k\), eine weiche ein kleines. Für unsere Federn nehmen wir durchweg \(k = 100\ \mathrm{N/mm}\): Um eine Feder um einen Millimeter zu dehnen, braucht man 100 Newton. Dehnt man sie um \(0{,}5\ \mathrm{mm}\), so ist die Kraft \(100 \cdot 0{,}5 = 50\ \mathrm{N}\). Genau diese Zahl wird gleich wiederkehren.

3.2 Das Kräftegleichgewicht an einem Knoten

Eine einzelne Feder ist noch keine Kette. Der interessante Ort ist ein Knoten, an dem zwei Federn zusammentreffen — sagen wir Knoten 2, zwischen Feder 1 (links) und Feder 2 (rechts). Dieser Knoten wird von beiden Seiten gezogen, und die entscheidende Regel der ganzen Statik lautet: Ein Knoten, der in Ruhe ist, steht unter ausgeglichenen Kräften. Alles, was von links zieht, wird von rechts genau aufgewogen. Sonst würde er sich ja bewegen.

Schauen wir uns diese Kräfte einzeln an. Feder 1 hängt zwischen Knoten 1 und 2; sie ist um \(u_2 - u_1\) gedehnt und zieht darum an Knoten 2 mit der Kraft \(k\,(u_2 - u_1)\) nach links zurück (eine gedehnte Feder zieht ihre Enden zueinander). Feder 2 hängt zwischen Knoten 2 und 3; sie ist um \(u_3 - u_2\) gedehnt und zieht an Knoten 2 mit \(k\,(u_3 - u_2)\) nach rechts.

Code
import matplotlib.pyplot as plt

def feder(ax, x0, x1, y, windungen=5, amplitude=0.07, farbe="0.5", lw=1.4):
    px = [x0, x0 + 0.15 * (x1 - x0)]
    py = [y, y]
    a = x0 + 0.15 * (x1 - x0)
    b = x0 + 0.85 * (x1 - x0)
    zacken = 2 * windungen
    for i in range(zacken + 1):
        anteil = i / zacken
        px.append(a + anteil * (b - a))
        py.append(y + amplitude if i % 2 == 0 else y - amplitude)
    px.append(x1)
    py.append(y)
    ax.plot(px, py, color=farbe, lw=lw, solid_capstyle="round")

fig, ax = plt.subplots(figsize=(8.5, 3.2))
feder(ax, -1.6, 0.0, 0.0)
feder(ax, 0.0, 1.6, 0.0)
ax.plot(-1.6, 0.0, "o", ms=11, color="0.8", markeredgecolor="black")
ax.plot(1.6, 0.0, "o", ms=11, color="0.8", markeredgecolor="black")
ax.text(-1.6, 0.18, "Knoten 1", ha="center", fontsize=9)
ax.text(1.6, 0.18, "Knoten 3", ha="center", fontsize=9)

# Der freigeschnittene Mittelknoten
ax.plot(0.0, 0.0, "o", ms=20, color="tab:blue", markeredgecolor="black",
        zorder=5)
ax.text(0.0, 0.26, "Knoten 2", ha="center", fontsize=11, weight="bold")

# Kraft von Feder 1: nach links
ax.annotate("", xy=(-1.15, -0.02), xytext=(-0.15, -0.02),
            arrowprops=dict(arrowstyle="-|>", color="tab:purple", lw=2.4))
ax.text(-0.95, -0.36, "k·(u2−u1)\nvon Feder 1", ha="center", fontsize=9,
        color="tab:purple")

# Kraft von Feder 2: nach rechts
ax.annotate("", xy=(1.15, -0.02), xytext=(0.15, -0.02),
            arrowprops=dict(arrowstyle="-|>", color="tab:orange", lw=2.4))
ax.text(0.95, -0.36, "k·(u3−u2)\nvon Feder 2", ha="center", fontsize=9,
        color="tab:orange")

# Aeussere Knotenkraft F2 nach oben
ax.annotate("", xy=(0.0, 0.85), xytext=(0.0, 0.32),
            arrowprops=dict(arrowstyle="-|>", color="tab:red", lw=2.2))
ax.text(0.12, 0.7, "F2 (von außen)", fontsize=10, color="tab:red")

ax.set_xlim(-2.1, 2.3)
ax.set_ylim(-0.7, 1.0)
ax.set_aspect("equal")
ax.axis("off")
plt.tight_layout()
plt.show()
Abbildung 3.3: Der Mittelknoten 2 freigeschnitten: Feder 1 (links) zieht ihn mit k·(u2−u1) nach links zurück, Feder 2 (rechts) zieht ihn mit k·(u3−u2) nach rechts, und von außen wirkt eine Knotenkraft F2. Ist der Knoten in Ruhe, heben sich alle drei genau auf. Die Pfeillängen sind schematisch.

Was man hier sieht: drei Kräfte an einem Knoten. Zwei kommen von den Federn (lila und orange), eine kommt von außen (rot) — etwa der Zug, den wir aufbringen. Die Statik verlangt, dass sie sich aufheben. Schreiben wir das hin: Die äußere Kraft \(F_2\) muss tragen, was die beiden Federn dem Knoten antun. Sortiert man die Feder-Kräfte danach, welche Verschiebung sie enthalten, ergibt sich

\[ F_2 \;=\; -\,k\,u_1 \;+\; 2k\,u_2 \;-\; k\,u_3. \]

Hier steht schon fast die ganze FEM. Lies die Gleichung als Satz: Die Kraft am Knoten 2 setzt sich zusammen aus einem Beitrag seines linken Nachbarn (\(-k\,u_1\)), einem doppelten Beitrag von sich selbst (\(+2k\,u_2\) — weil an Knoten 2 zwei Federn ziehen) und einem Beitrag seines rechten Nachbarn (\(-k\,u_3\)). Die \(2k\) auf dem eigenen Platz ist kein Zufall, sondern die Handschrift eines Knotens, der zwischen zwei Federn hängt. Merke sie dir — sie ist es, die gleich in der großen Tabelle auf der Diagonale auftaucht.

3.3 Der Steckbrief der Feder: die 2×2-Elementmatrix

Bevor wir die ganze Kette bauen, halten wir fest, was eine Feder für ihre zwei Knoten bedeutet — ihren Steckbrief. Eine Feder verknüpft zwei Knoten, und zu jedem Knoten gehört eine Gleichung: die Kraft an diesem Knoten. Für die Feder zwischen Knoten 1 und 2 sind das genau zwei Zeilen, \(F_1\) und \(F_2\):

\[ \begin{aligned} F_1 &= \phantom{-}k\,u_1 - k\,u_2,\\ F_2 &= -k\,u_1 + k\,u_2. \end{aligned} \]

Die erste Zeile sagt: Zieht der rechte Knoten weg (\(u_2\) groß), so wird der linke Knoten nach rechts gezogen — mit umgekehrtem Vorzeichen. Die zweite Zeile ist ihr Spiegelbild. Beide zusammen sind bloß das Hookesche Gesetz \(F = k\,(u_2 - u_1)\), einmal von jedem Knoten aus betrachtet.

Diese zwei Gleichungen kann man ordentlich als Tabelle aufschreiben — genau so, wie wir in Kapitel 2 verschachtelte Listen als Zahlengitter gedruckt haben. Wir notieren nur noch die Vorfaktoren der Verschiebungen, in einem Quadrat aus vier Zahlen:

\[ \mathbf{K}_{\text{Feder}} \;=\; \begin{pmatrix} k & -k \\ -k & k \end{pmatrix} \;=\; \begin{pmatrix} 100 & -100 \\ -100 & 100 \end{pmatrix}. \]

Das ist die Elementmatrix einer Feder — ihr vollständiger Steckbrief. Das Wort Matrix soll dich nicht schrecken: Es ist nichts als ein ordentlich aufgeschriebenes Gleichungssystem, eine verschachtelte Liste mit zwei Zeilen und zwei Spalten, wie du sie längst kennst. Jede Zeile gehört zu einem Knoten, jede Spalte auch.

Code
import matplotlib.pyplot as plt

werte = [[100, -100], [-100, 100]]
n = 2
fig, ax = plt.subplots(figsize=(5.6, 5.0))
for r in range(n):
    for c in range(n):
        diagonal = (r == c)
        farbe = "#d9f0d3" if diagonal else "#e7dcf3"
        ax.add_patch(plt.Rectangle((c, n - 1 - r), 1, 1, facecolor=farbe,
                                   edgecolor="black", lw=1.4))
        ax.text(c + 0.5, n - 1 - r + 0.5, "%d" % werte[r][c], ha="center",
                va="center", fontsize=18, weight="bold")
namen = ["Knoten 1", "Knoten 2"]
for i in range(n):
    ax.text(-0.15, n - 1 - i + 0.5, namen[i], ha="right", va="center",
            fontsize=11, color="tab:blue", weight="bold")
    ax.text(i + 0.5, n + 0.15, namen[i], ha="center", va="bottom",
            fontsize=11, color="tab:red", weight="bold")
ax.text(-1.55, n / 2, "Zeile =\nKraft an …", ha="center", va="center",
        fontsize=9, color="tab:blue", style="italic")
ax.text(n / 2, n + 0.7, "Spalte = Verschiebung von …", ha="center",
        va="center", fontsize=9, color="tab:red", style="italic")
ax.set_xlim(-1.9, n + 0.2)
ax.set_ylim(-0.2, n + 1.05)
ax.set_aspect("equal")
ax.axis("off")
plt.tight_layout()
plt.show()
Abbildung 3.4: Der Steckbrief einer Feder als 2×2-Tabelle. Zeilen und Spalten gehören zu den beiden Knoten der Feder. Die beiden Diagonalfelder (grün, +k) sagen: „Ziehe ich an einem Knoten, wehrt sich die Feder mit k.“ Die beiden Kreuzfelder (violett, −k) sagen: „Bewegt sich der eine Knoten, zieht er den anderen mit.“ Für unsere Federn ist k = 100 N/mm.

Was man hier sieht: vier Zahlen, die alles über eine Feder sagen. Auf der Diagonale steht zweimal \(+k\) (grün): Das ist der Widerstand, den ein Knoten spürt, wenn er selbst gezogen wird. Auf den beiden Kreuzfeldern steht \(-k\) (violett): So stark zieht ein Knoten seinen Partner mit. Dass die Zeilensummen null sind (\(100 - 100 = 0\)), ist kein Zufall — es heißt: Verschiebt man beide Knoten gleich weit, entsteht keine Kraft. Die Feder merkt nur Dehnung, nicht Wanderung.

3.4 Assemblierung: aus drei Steckbriefen ein System

Jetzt kommt der Kern des Kapitels und der Kern der ganzen FEM. Wir haben drei Federn, also drei gleiche Steckbriefe. Wir haben vier Knoten. Wie wird aus den drei kleinen 2×2-Tabellen eine große Tabelle für alle vier Knoten?

Die Antwort heißt Assemblierung (vom französischen assembler, zusammensetzen — ein Wort aus dem Maschinenbau, wo man Bauteile zu einer Baugruppe zusammenfügt). Die Idee ist verblüffend mechanisch: Wir legen eine leere 4×4-Tabelle an — vier Knoten, also vier Zeilen und vier Spalten — und schieben jeden Steckbrief an die Stelle seiner beiden Knoten hinein. Feder 1 gehört zu den Knoten 1 und 2, ihr Steckbrief kommt also in die Felder der Zeilen und Spalten 1 und 2. Feder 2 gehört zu den Knoten 2 und 3, Feder 3 zu den Knoten 3 und 4.

Das Entscheidende passiert dort, wo sich zwei Federn einen Knoten teilen. Knoten 2 gehört zu Feder 1 und zu Feder 2. Beide wollen etwas in das Diagonalfeld von Knoten 2 schreiben — jede ihr \(+k\). Und was tun zwei Beiträge am selben Ort? Sie addieren sich. Aus \(k\) und \(k\) wird \(2k\). Genau das ist die \(2k\), die wir schon beim Kräftegleichgewicht gesehen haben: Sie entsteht, weil an einem inneren Knoten zwei Federn zerren, und ihre beiden Steckbriefe überlappen sich genau auf seinem Diagonalfeld.

Code
import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(6.2, 6.2))
n = 4
# Leeres Gitter
for r in range(n):
    for c in range(n):
        ax.add_patch(plt.Rectangle((c, n - 1 - r), 1, 1, facecolor="white",
                                   edgecolor="0.8", lw=1.0))
# Feder 1 belegt Knoten 0,1 (Indexe) -> Zeilen/Spalten 0,1
blau = "#cfe2f7"
for r in (0, 1):
    for c in (0, 1):
        ax.add_patch(plt.Rectangle((c, n - 1 - r), 1, 1, facecolor=blau,
                                   edgecolor="tab:blue", lw=1.6))
# Feder 2 belegt Knoten 1,2
orange = "#ffe2bf"
for r in (1, 2):
    for c in (1, 2):
        # Ueberlappungsfeld (1,1) bekommt eine Mischfarbe
        if r == 1 and c == 1:
            ax.add_patch(plt.Rectangle((c, n - 1 - r), 1, 1, facecolor="#f3d9b0",
                                       edgecolor="tab:red", lw=2.6))
        else:
            ax.add_patch(plt.Rectangle((c, n - 1 - r), 1, 1, facecolor=orange,
                                       edgecolor="tab:orange", lw=1.6))
# Zahlen eintragen
voll = [[100, -100, 0, 0],
        [-100, 200, -100, 0],
        [0, -100, 200, -100],
        [0, 0, -100, 100]]
for r in range(n):
    for c in range(n):
        if voll[r][c] != 0:
            gewicht = "bold" if (r == 1 and c == 1) else "normal"
            farbe = "tab:red" if (r == 1 and c == 1) else "black"
            ax.text(c + 0.5, n - 1 - r + 0.5, "%d" % voll[r][c], ha="center",
                    va="center", fontsize=12, weight=gewicht, color=farbe)
        else:
            ax.text(c + 0.5, n - 1 - r + 0.5, ".", ha="center", va="center",
                    fontsize=12, color="0.6")
for i in range(n):
    ax.text(-0.2, n - 1 - i + 0.5, "K%d" % (i + 1), ha="right", va="center",
            fontsize=10, color="tab:blue", weight="bold")
    ax.text(i + 0.5, n + 0.12, "K%d" % (i + 1), ha="center", va="bottom",
            fontsize=10, color="tab:red", weight="bold")
ax.text(0.5, -0.45, "Feder 1", color="tab:blue", ha="center", fontsize=10,
        weight="bold")
ax.text(2.5, -0.45, "Feder 2", color="tab:orange", ha="center", fontsize=10,
        weight="bold")
ax.text(1.5, n + 0.75, "hier addieren sie sich: 100 + 100 = 200",
        color="tab:red", ha="center", fontsize=10)
ax.set_xlim(-0.7, n + 0.2)
ax.set_ylim(-0.7, n + 1.15)
ax.set_aspect("equal")
ax.axis("off")
plt.tight_layout()
plt.show()
Abbildung 3.5: Assemblierung als Schiebespiel: Der Steckbrief von Feder 1 (blau) rutscht in die Felder der Knoten 1–2, der von Feder 2 (orange) in die Felder der Knoten 2–3. Auf dem Diagonalfeld von Knoten 2 (rot umrandet) treffen beide aufeinander — dort addieren sich ihre Beiträge zu 100 + 100 = 200. Feder 3 (hier weggelassen) macht dasselbe bei Knoten 3–4.

Was man hier sieht: Die beiden 2×2-Steckbriefe überlappen sich in genau einem Feld — dem Diagonalfeld des gemeinsamen Knotens 2 (rot umrandet). Nur dort addieren sich Zahlen; überall sonst schreibt jede Feder in ihr eigenes, leeres Feld. Die Ecken oben rechts und unten links bleiben leer (Punkte): Knoten 1 und Knoten 4 teilen sich keine Feder, also spüren sie einander nicht. Diese Leere ist kein Makel, sondern die Bandstruktur — sie macht das System später schnell lösbar.

3.4.1 Die Assemblierung im Ablauf

Weil das Ineinanderschieben der Bausteine der wichtigste Vorgang des Kapitels ist, lohnt es sich, ihm zuzusehen. Die folgende Animation baut die 4×4-Tabelle Feder für Feder auf: Jeder 2×2-Steckbrief fliegt an seinen Platz, und auf den geteilten Diagonalfeldern siehst du die Zahlen von 100 auf 200 hochspringen, wenn die zweite Feder ihren Beitrag dazulegt.

Code
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from IPython.display import HTML

n = 4
k = 100
# Jede Feder verbindet zwei benachbarte Knoten (0-basiert).
federn = [(0, 1), (1, 2), (2, 3)]

def matrix_nach(federzahl):
    """Die Systemmatrix, nachdem federzahl Federn eingebaut sind."""
    m = [[0 for _ in range(n)] for _ in range(n)]
    for f in range(federzahl):
        a, b = federn[f]
        m[a][a] += k
        m[b][b] += k
        m[a][b] -= k
        m[b][a] -= k
    return m

fig, ax = plt.subplots(figsize=(6.0, 6.2))

def zeichne(schritt):
    ax.clear()
    ax.set_xlim(-0.7, n + 0.2)
    ax.set_ylim(-0.9, n + 0.7)
    ax.set_aspect("equal")
    ax.axis("off")
    aktive = schritt  # 0..3 Federn eingebaut
    m = matrix_nach(aktive)
    a_akt = federn[aktive - 1] if aktive >= 1 else None
    for r in range(n):
        for c in range(n):
            gehoert = a_akt is not None and r in a_akt and c in a_akt
            flaeche = "#ffe0b3" if gehoert else "white"
            ax.add_patch(plt.Rectangle((c, n - 1 - r), 1, 1, facecolor=flaeche,
                                       edgecolor="0.75", lw=1.0))
            if m[r][c] != 0:
                betont = gehoert and r == c and m[r][c] == 2 * k
                ax.text(c + 0.5, n - 1 - r + 0.5, "%d" % m[r][c], ha="center",
                        va="center", fontsize=13,
                        weight="bold" if betont else "normal",
                        color="tab:red" if betont else "black")
            else:
                ax.text(c + 0.5, n - 1 - r + 0.5, ".", ha="center",
                        va="center", fontsize=12, color="0.7")
    for i in range(n):
        ax.text(-0.2, n - 1 - i + 0.5, "K%d" % (i + 1), ha="right",
                va="center", fontsize=10, color="tab:blue", weight="bold")
        ax.text(i + 0.5, n + 0.12, "K%d" % (i + 1), ha="center", va="bottom",
                fontsize=10, color="tab:red", weight="bold")
    if aktive == 0:
        titel = "Leere Systemmatrix (4 Knoten)"
    else:
        titel = "Feder %d eingebaut (Knoten %d%d)" % (
            aktive, a_akt[0] + 1, a_akt[1] + 1)
    ax.text(n / 2, -0.55, titel, ha="center", fontsize=11)
    return []

ani = FuncAnimation(fig, zeichne, frames=4, interval=1400, blit=False)
plt.close(fig)
HTML(ani.to_jshtml())
Abbildung 3.6: Assemblierung Feder für Feder: Erst legt Feder 1 ihren Steckbrief in die Felder der Knoten 1–2, dann Feder 2 in die Knoten 2–3, dann Feder 3 in die Knoten 3–4. Auf den geteilten Diagonalfeldern (Knoten 2 und 3) addiert sich der zweite Beitrag sichtbar: 100 wird zu 200.

Was man sieht: Aus dem Nichts wächst Zeile für Zeile ein Gleichungssystem. Jede Feder liefert denselben Baustein; nur ihr Platz in der großen Tabelle ist ein anderer. Und weil benachbarte Federn sich einen Knoten teilen, überlappen ihre Bausteine genau auf einem Diagonalfeld — dort, und nur dort, springt die Zahl auf 200. Nach drei Federn steht die vollständige Systemmatrix.

3.4.2 Das vollständige 4×4-System

Setzen wir alles zusammen. Vier Knoten ergeben vier Gleichungen, eine Kraft-Gleichung pro Knoten. Ordentlich als Matrix aufgeschrieben — Zeile für Knoten, Spalte für Verschiebung — lautet das System der Federkette:

\[ \begin{pmatrix} 100 & -100 & 0 & 0\\ -100 & \textcolor{orange}{200} & -100 & 0\\ 0 & -100 & \textcolor{orange}{200} & -100\\ 0 & 0 & -100 & 100 \end{pmatrix} \begin{pmatrix} u_1\\ u_2\\ u_3\\ u_4 \end{pmatrix} = \begin{pmatrix} F_1\\ 0\\ 0\\ 50 \end{pmatrix} \]

Gehen wir jede Zahl durch, denn hier ist nichts geheimnisvoll:

  • Zeile 1 gehört zu Knoten 1, an dem nur Feder 1 hängt: \(100\) auf der Diagonale, \(-100\) zum Nachbarn Knoten 2, sonst nichts.
  • Zeile 2 gehört zu Knoten 2, dem inneren Knoten zwischen Feder 1 und Feder 2: die berühmte \(\textcolor{orange}{200} = 100 + 100\) auf der Diagonale (die Überlappung!), links und rechts je \(-100\) zu den Nachbarn.
  • Zeile 3 ist das Spiegelbild von Zeile 2, einen Knoten weiter — wieder \(\textcolor{orange}{200}\) in der Mitte.
  • Zeile 4 gehört zu Knoten 4, an dem nur Feder 3 hängt: \(100\) auf der Diagonale, \(-100\) zum Nachbarn Knoten 3.

Auf der rechten Seite steht, welche Kraft von außen an jedem Knoten zieht: An Knoten 4 sind es unsere \(50\ \mathrm{N}\), an den inneren Knoten 2 und 3 zieht niemand von außen (deshalb null), und an Knoten 1 steht \(F_1\) — die noch unbekannte Auflagerkraft, die das Auflager aufbringt, um den Knoten festzuhalten.

TippDer Vorgriff aus Kapitel 2 — eingelöst

Erinnerst du dich an die 3×3-Tabelle aus Kapitel 2, die wir „mit Bedacht gewählt” nannten?

\[ \begin{pmatrix} 2 & -1 & 0\\ -1 & 2 & -1\\ 0 & -1 & 2 \end{pmatrix} \]

Genau diese Gestalt steckt in unserem System. Teilt man die ganze 4×4-Matrix durch \(k = 100\), bleibt das reine Muster übrig:

\[ \begin{pmatrix} 1 & -1 & 0 & 0\\ -1 & 2 & -1 & 0\\ 0 & -1 & 2 & -1\\ 0 & 0 & -1 & 1 \end{pmatrix}. \]

Die \(2\) auf der Diagonale ist ein Knoten mit zwei Federn, die \(-1\) daneben ist die Kopplung zweier Nachbarn, die \(0\) (bzw. der Punkt) heißt „diese beiden spüren einander nicht” — Wort für Wort das Versprechen aus Kapitel 2. Die exakte 3×3-Tabelle mit lauter Zweien auf der Diagonale bekommst du, wenn alle gezeigten Knoten innere Knoten sind, also die Kette an beiden Enden festsitzt. Genau das baust du in Übung 3.3.

3.5 Die Randbedingung: das Auflager einbauen

Das System hat noch einen Haken: Auf der linken Seite steht die unbekannte Verschiebung \(u_1\), auf der rechten die unbekannte Auflagerkraft \(F_1\) — zwei Unbekannte in einer Gleichung, und das durch alle Zeilen. So ist das System nicht lösbar. Schlimmer noch: Ohne das Auflager könnte die ganze Kette als Ganzes davonschweben, ohne sich zu dehnen — es gäbe unendlich viele Lösungen.

Was rettet uns? Eine Information, die wir noch gar nicht benutzt haben: Knoten 1 sitzt fest im Auflager, er verschiebt sich nicht. Also ist \(u_1 = 0\). Das ist eine Randbedingung — ein am Rand vorgegebener Wert. (Warum das genügt und was Randbedingungen sonst noch können, ist so wichtig, dass Kapitel 8 ein ganzes Kapitel daraus macht.)

Was tut \(u_1 = 0\) mit dem System? In jeder Zeile wird der erste Summand \(100 \cdot u_1\) oder \(-100 \cdot u_1\) zu null — die ganze erste Spalte fällt weg. Und die erste Zeile brauchen wir nicht mehr: Sie würde nur die Auflagerkraft \(F_1\) ausrechnen, die uns gerade nicht interessiert. Wir streichen also Zeile 1 und Spalte 1. Übrig bleibt ein kleineres, sauber lösbares System für die drei freien Knoten 2, 3 und 4:

\[ \begin{pmatrix} 200 & -100 & 0\\ -100 & 200 & -100\\ 0 & -100 & 100 \end{pmatrix} \begin{pmatrix} u_2\\ u_3\\ u_4 \end{pmatrix} = \begin{pmatrix} 0\\ 0\\ 50 \end{pmatrix} \]

Drei Gleichungen, drei Unbekannte, keine Auflagerkraft mehr. Dieses System können wir von Hand lösen.

3.6 Von Hand lösen

WichtigVorhersage-Punkt

Bevor du weiterrechnest: Die drei Federn sind gleich steif, und weil sie hintereinanderhängen, zieht die Kraft von 50 N durch alle drei hindurch — wie ein Seil, an dem drei gleiche Gummibänder in Reihe hängen. Was glaubst du: Verschiebt sich Knoten 4 (am Zug) gleich weit wie Knoten 2 (nah am Auflager), oder weiter, oder weniger weit? Und um welche Beträge? Leg dich fest.

Wir lösen das reduzierte System durch Einsetzen — schrittweise eine Unbekannte durch die anderen ausdrücken. Schreiben wir die drei Zeilen als Gleichungen (die Einheiten sind N/mm mal mm = N, wir lassen sie beim Rechnen weg):

\[ \begin{aligned} (\text{I})\quad & 200\,u_2 - 100\,u_3 = 0,\\ (\text{II})\quad & -100\,u_2 + 200\,u_3 - 100\,u_4 = 0,\\ (\text{III})\quad & -100\,u_3 + 100\,u_4 = 50. \end{aligned} \]

Aus Gleichung (I) folgt sofort \(200\,u_2 = 100\,u_3\), also \(u_3 = 2\,u_2\). Der mittlere Knoten wandert doppelt so weit wie der erste freie — das klingt schon nach der Kette.

Aus Gleichung (III) folgt \(100\,u_4 = 50 + 100\,u_3\), also \(u_4 = u_3 + 0{,}5\). Der letzte Knoten liegt genau einen halben Millimeter vor dem mittleren.

Jetzt setzen wir beides in Gleichung (II) ein. Mit \(u_3 = 2\,u_2\) und \(u_4 = u_3 + 0{,}5 = 2\,u_2 + 0{,}5\) wird aus (II):

\[ -100\,u_2 + 200\,(2\,u_2) - 100\,(2\,u_2 + 0{,}5) = 0. \]

Ausmultiplizieren: \(-100\,u_2 + 400\,u_2 - 200\,u_2 - 50 = 0\), also \(100\,u_2 - 50 = 0\) und damit

\[ u_2 = 0{,}5\ \mathrm{mm}. \]

Rückwärts eingesetzt: \(u_3 = 2 \cdot 0{,}5 = 1{,}0\ \mathrm{mm}\) und \(u_4 = 1{,}0 + 0{,}5 = 1{,}5\ \mathrm{mm}\). Die ganze Lösung lautet also

\[ u_1 = 0,\quad u_2 = 0{,}5\ \mathrm{mm},\quad u_3 = 1{,}0\ \mathrm{mm}, \quad u_4 = 1{,}5\ \mathrm{mm}. \]

3.6.1 Was die Zahlen bedeuten

Schau dir die Verschiebungen an: \(0 - 0{,}5 - 1{,}0 - 1{,}5\). Von Knoten zu Knoten wächst der Weg um immer denselben Betrag, \(0{,}5\ \mathrm{mm}\). Das ist kein Zufall, und es bestätigt deine Vorhersage (oder korrigiert sie): Jede der drei Federn ist um genau \(0{,}5\ \mathrm{mm}\) gedehnt, keine mehr als die andere.

Warum muss das so sein? Weil die Federn hintereinander hängen und die Kraft durch alle gleich hindurchgeht. Am Knoten 4 ziehen wir mit 50 N; dieselben 50 N muss Feder 3 tragen, sonst wäre Knoten 4 nicht im Gleichgewicht. Feder 3 gibt die 50 N an Knoten 3 weiter, der sie an Feder 2 reicht, und so fort — jede Feder trägt dieselbe Kraft von 50 N. Und eine Feder, die 50 N trägt, ist bei \(k = 100\ \mathrm{N/mm}\) um \(50/100 = 0{,}5\ \mathrm{mm}\) gedehnt. Drei gleiche Dehnungen hintereinander ergeben die Treppe \(0{,}5 - 1{,}0 - 1{,}5\).

Merke dir dieses Wechselspiel: Die Matrix-Maschinerie (assemblieren, Randbedingung, lösen) und die anschauliche Physik (gleiche Kraft, gleiche Dehnung) liefern dasselbe Ergebnis. Die Maschinerie ist nicht klüger als die Physik — sie ist nur so stur und gleichförmig, dass ein Computer sie auch bei tausend Federn ausführen kann, wo die Anschauung längst kapituliert.

Code
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from IPython.display import HTML

ruhe = [0.0, 1.0, 2.0, 3.0]
ziel = [0.0, 0.5, 1.0, 1.5]     # mm, echte Loesung
ueber = 0.5                     # optische Ueberhoehung im Bild

def feder(ax, x0, x1, y, farbe, lw=1.8):
    px = [x0, x0 + 0.13 * (x1 - x0)]
    py = [y, y]
    a = x0 + 0.13 * (x1 - x0)
    b = x0 + 0.87 * (x1 - x0)
    zacken = 12
    for i in range(zacken + 1):
        anteil = i / zacken
        px.append(a + anteil * (b - a))
        py.append(y + 0.08 if i % 2 == 0 else y - 0.08)
    px.append(x1)
    py.append(y)
    ax.plot(px, py, color=farbe, lw=lw, solid_capstyle="round")

fig, ax = plt.subplots(figsize=(9.0, 2.8))

def zeichne(frame):
    ax.clear()
    anteil = frame / 20.0
    x = [ruhe[i] + ueber * ziel[i] * anteil for i in range(4)]
    ax.add_patch(plt.Rectangle((-0.35, -0.4), 0.35, 0.8, hatch="///",
                               facecolor="lightsteelblue", edgecolor="black"))
    for i in range(3):
        feder(ax, x[i], x[i + 1], 0.0, "tab:blue")
    for i in range(4):
        ax.plot(x[i], 0.0, "o", ms=13, color="tab:blue",
                markeredgecolor="black", zorder=4)
        ax.text(x[i], 0.22, "%.1f mm" % (ziel[i] * anteil), ha="center",
                fontsize=9)
    ax.annotate("", xy=(x[3] + 0.55, 0.0), xytext=(x[3], 0.0),
                arrowprops=dict(arrowstyle="-|>", color="tab:red", lw=2.2))
    ax.text(x[3] + 0.6, 0.0, "50 N", color="tab:red", va="center", fontsize=10)
    ax.set_xlim(-0.55, 4.9)
    ax.set_ylim(-0.55, 0.5)
    ax.set_aspect("equal")
    ax.axis("off")
    return []

ani = FuncAnimation(fig, zeichne, frames=21, interval=120, blit=False)
plt.close(fig)
HTML(ani.to_jshtml())
Abbildung 3.7: Die gelöste Kette: Zug am rechten Ende, die Verschiebungen wachsen der Reihe nach. Knoten 1 bleibt am Auflager (0 mm), Knoten 2 kommt auf 0,5 mm, Knoten 3 auf 1,0 mm, Knoten 4 auf 1,5 mm. Jede Feder ist gleich weit gedehnt. Die Verschiebung ist zur Sichtbarkeit stark überhöht dargestellt.

Was man sieht: die Kette in ihrer gedehnten Ruhelage. Die Verschiebungen bilden eine gleichmäßige Treppe — der sichtbare Beweis, dass jede Feder dieselbe Last trägt und dieselbe Strecke nachgibt.

3.7 Die interaktive Einheit: der Matrix-Drucker

Die Assemblierung von Hand ist bei drei Federn noch überschaubar. Aber die eigentliche Botschaft des Kapitels ist ja: Der Rechenweg bleibt gleich, egal wie lang die Kette wird. Das probierst du jetzt selbst aus. Das folgende Programm baut die Systemmatrix für eine Kette aus beliebig vielen Federn und druckt sie als Textblock — die Nullen als Punkte, damit die Bandstruktur buchstäblich ins Auge springt.

Der Code ist genau die Papier-Assemblierung in Python: Erst eine leere Tabelle aus Nullen, dann für jede Feder ihre vier Beiträge (\(+k\) auf die zwei Diagonalfelder, \(-k\) auf die zwei Kreuzfelder) an die Stelle ihrer beiden Knoten addieren — dieselbe Doppelschleife, die du in Kapitel 2 gelernt hast.

WichtigVorhersage-Punkt

Bevor du ausführst: Stell die Zahl der Federn auf 5. Dann hat die Kette 6 Knoten, die Matrix ist also 6×6 — das sind 36 Felder. Wie viele davon sind nicht null? (Tipp: Denk an die Bandstruktur — jeder Knoten hängt nur mit sich selbst und seinen unmittelbaren Nachbarn zusammen.) Leg dich auf eine Zahl fest.

Abbildung 3.8: Vorgerenderte Fassung des Matrix-Druckers für fünf Federn (6×6-Matrix). Blau sind die belegten Felder: die Diagonale und je ein Feld links und rechts davon — 16 von 36. Alles andere ist leer. In der HTML-Fassung ersetzt dein eigenes Ergebnis dieses Bild, sobald du die Zelle darüber ausführst.

Was man sieht: Bei fünf Federn sind von den 36 Feldern nur 16 nicht null — die Diagonale (6 Felder) und je ein Feld links und rechts davon (zweimal 5). Lag deine Vorhersage richtig? Alles andere ist leer. Und je länger die Kette, desto krasser das Verhältnis: Bei 100 Federn hat die 101×101-Matrix über 10.000 Felder, aber nur rund 300 sind belegt. Diese dünne Besetzung ist ein Geschenk — Kapitel 4 zeigt, wie ein Löser sie ausnutzt und deshalb auch riesige Ketten in Sekunden schafft. Probier ruhig aus, einzelne Steifigkeiten zu ändern oder Federn dazuzuhängen: Die Bandstruktur bleibt, nur die Zahlen darin ändern sich.

3.8 Das Kapitel-Programm

Das vollständige, eigenständig lauffähige Skript zu diesem Kapitel liegt in programme/kap03/kap03_federkette.py. Es führt die drei Schritte dieses Kapitels an einem Stück vor: Es assembliert die 4×4-Systemmatrix (mit demselben baue_systemmatrix wie oben, das als wiederverwendbarer Baustein in programme/gemeinsam/matrix_drucker.py liegt — Kapitel 4 wird ihn importieren), baut die Randbedingung ein, löst die Kette und macht zum Schluss die Probe: Es rechnet aus den Verschiebungen die Knotenkräfte zurück und prüft, ob am festen Knoten die Auflagerkraft und am Zugende die 50 N herauskommen. Führe es mit python kap03_federkette.py aus und vergleiche seine Ausgabe mit unserer Handrechnung.

Zum Lösen nutzt das Skript bewusst nicht einen allgemeinen Gleichungslöser — der ist das Thema von Kapitel 4. Es verwendet stattdessen die anschauliche Physik der Reihenschaltung: Jede Feder trägt dieselbe Kraft, ihre Dehnung ist Kraft geteilt durch Steifigkeit, und die Verschiebung eines Knotens ist die Summe der Dehnungen bis zu ihm. So bleibt das Kapitel bei reiner Schulalgebra, und der maschinelle Löser bekommt im nächsten Kapitel seinen großen Auftritt.

3.9 Woher die Methode kommt

Jetzt, wo du die Maschinerie selbst gebaut hast, lohnt der Blick zurück auf ihre Erfinder — denn fast jeder Begriff dieses Kapitels stammt aus einer sehr konkreten Notlage der frühen 1950er Jahre.

HinweisGeschichte: Vom Flugzeugflügel zur Weltmethode

Nach dem Zweiten Weltkrieg wurden Flugzeuge schneller, und ihre Flügel wurden dünner und pfeilförmiger — besonders die Delta-Flügel der neuen Düsenjets. Solche Flügel ließen sich mit den klassischen Formeln der Statik nicht mehr zuverlässig berechnen: Ihre Form war zu unregelmäßig, und bei hoher Geschwindigkeit drohte Flattern, ein gefährliches Aufschaukeln.

Bei Boeing in Seattle leitete der Ingenieur M. J. (Jon) Turner die Gruppe für Strukturdynamik. In den Sommern 1952 und 1953 holte er sich zwei Gäste dazu: Ray W. Clough von der Universität Berkeley und Harold Martin von der Universität Washington. Ihre Idee, um den komplizierten Flügel berechenbar zu machen, ist genau die dieses Kapitels: Zerlege den Flügel in viele kleine, einfache Stücke — Dreiecke und Rechtecke —, für die man rechnen kann, schreibe für jedes seinen Steckbrief (seine Steifigkeitsmatrix) auf und setze alles wieder zusammen. 1956 veröffentlichten Turner, Clough, Martin und L. J. Topp das Verfahren (Turner u. a. 1956); man nannte es damals direct stiffness method, Methode der direkten Steifigkeit. Daher kommen unsere Wörter Steifigkeit, Element und Assemblierung — es war die Sprache des Maschinen- und Flugzeugbaus.

Etwa zur selben Zeit entwickelte in Europa John Argyris — an der Universität Stuttgart und in London — die energetische Theorie dahinter (Argyris 1954--1955). Den einprägsamen Namen aber prägte Ray Clough: 1960 stellte er auf einer Tagung in Pittsburgh seine Arbeit unter dem Titel „The Finite Element Method in Plane Stress Analysis” vor (Clough 1960) — und aus der direct stiffness method wurde die Finite-Elemente-Methode. Von da an ging es schnell: Ende der 1960er gab die NASA das große, frei verfügbare Programm NASTRAN in Auftrag (1968 fertiggestellt), und was als Rettung für den Flugzeugflügel begann, rechnet heute Autos, Brücken, Handys — und Stahlträger im Feuer.

Code
import matplotlib.pyplot as plt

ereignisse = [
    (1952, "Boeing: Turner holt\nClough & Martin\n(Delta-Flügel)"),
    (1955, "Argyris:\nEnergie-\ntheoreme"),
    (1956, "Turner/Clough/\nMartin/Topp:\ndirect stiffness"),
    (1960, "Clough prägt\n„finite element\"\n(Pittsburgh)"),
    (1968, "NASA:\nNASTRAN\nfertig"),
]

fig, ax = plt.subplots(figsize=(9.2, 3.2))
ax.axhline(0, color="black", lw=1.5)
for i, (jahr, text) in enumerate(ereignisse):
    oben = (i % 2 == 0)
    y = 0.55 if oben else -0.55
    ax.plot(jahr, 0, "o", ms=10, color="tab:blue", zorder=3)
    ax.annotate(text, xy=(jahr, 0), xytext=(jahr, y),
                ha="center", va="center", fontsize=8.5,
                arrowprops=dict(arrowstyle="-", color="0.5", lw=1.0),
                bbox=dict(boxstyle="round,pad=0.3", facecolor="#eef3fb",
                          edgecolor="tab:blue", lw=1.0))
    ax.text(jahr, -0.12 if oben else 0.12, str(jahr), ha="center",
            va="top" if oben else "bottom", fontsize=9, weight="bold")
ax.set_xlim(1949, 1971)
ax.set_ylim(-1.1, 1.1)
ax.set_yticks([])
ax.set_xticks([1950, 1955, 1960, 1965, 1970])
ax.spines["top"].set_visible(False)
ax.spines["left"].set_visible(False)
ax.spines["right"].set_visible(False)
ax.set_title("1950 – 1970: Die Geburt der finiten Elemente")
plt.tight_layout()
plt.show()
Abbildung 3.9: Zwei Jahrzehnte von der Notlage zur Weltmethode: von Turners Boeing-Team am Delta-Flügel (1952) über das Gründungspapier (1956) und Cloughs Namensprägung „finite element“ (1960) bis zum NASA-Programm NASTRAN (1968), das die Methode allen zugänglich machte.

Was man hier sieht: In keinen zwei Jahrzehnten wuchs aus einer Flügel-Notlage eine Methode, die heute überall steckt. Bemerkenswert ist das Tempo zwischen Idee (1952), erstem Papier (1956) und dem Namen, der blieb (1960) — und dass die Begriffe, mit denen wir eben gerechnet haben, genau von hier stammen.

TippMerkkasten
  • Eine Feder verknüpft zwei Knoten; ihr Steckbrief ist die 2×2-Elementmatrix \(\begin{pmatrix} k & -k\\ -k & k\end{pmatrix}\) — nur ein ordentlich aufgeschriebenes Gleichungssystem.
  • Assemblieren heißt: jede Elementmatrix an die Stelle ihrer Knoten in die große Systemmatrix addieren. Wo sich Federn einen Knoten teilen, addieren sich ihre Beiträge auf der Diagonale (\(k + k = 2k\)).
  • Ein fester Knoten ist eine Randbedingung (\(u = 0\)): Man streicht seine Zeile und Spalte und erhält ein kleineres, lösbares System.
  • In einer Reihenschaltung trägt jede Feder dieselbe Kraft; gleiche Federn dehnen sich darum gleich weit — die Verschiebungen wachsen als gleichmäßige Treppe.
  • Die Systemmatrix ist dünn besetzt (Bandstruktur): fast nur Nullen, die belegten Zahlen drängen sich um die Diagonale.
  • Die Begriffe Steifigkeit, Element, Assemblierung stammen aus dem Flugzeugbau der 1950er; Clough prägte 1960 den Namen „finite element”.

Roter Faden

Zurück: Die verschachtelte Liste aus Kapitel 2 ist jetzt eine Matrix geworden — genau die 3×3-Tabelle, die wir dort „mit Bedacht gewählt” nannten, ist die Gestalt der Federketten-Systemmatrix. Die Doppelschleife, die dort ein Zahlengitter druckte, druckt hier die Systemmatrix; die laufende Summe steckt in der Assemblierung („addiere den Beitrag an die richtige Stelle”).

Vor: Das 4×4-System haben wir hier von Hand gelöst; Kapitel 4 löst es maschinell und nutzt dabei die Bandstruktur aus. Und dann der große Bogen: Genau dieselbe Maschinerie — assemblieren, Randbedingung, lösen — rechnet ab Kapitel 7 die Wärme im Träger. Denn:

Ersetze „Verschiebung” durch „Temperatur” und „Kraft” durch „Wärmestrom” — dieselbe Maschinerie rechnet Wärme.

Das ist der Plan der nächsten Kapitel. Die Federkette war nie nur eine Feder; sie war die FEM im Kleinen.

Übungen

Ü 3.1 (Verstehen). Eine Kette aus zwei gleichen Federn (\(k = 100\ \mathrm{N/mm}\)) hat drei Knoten. Schreibe ihre 3×3-Systemmatrix von Hand auf, indem du die zwei Steckbriefe assemblierst. Welche Zahl steht auf dem mittleren Diagonalfeld (Knoten 2), und warum? Kontrolliere dein Ergebnis, indem du im Matrix-Drucker STEIFIGKEITEN = [100.0, 100.0] setzt.

Feder 1 belegt die Knoten 1–2, Feder 2 die Knoten 2–3. Assembliert:

\[ \begin{pmatrix} 100 & -100 & 0\\ -100 & 200 & -100\\ 0 & -100 & 100 \end{pmatrix} \]

Auf dem mittleren Diagonalfeld steht 200, weil Knoten 2 der einzige innere Knoten ist: An ihm hängen beide Federn, und ihre Beiträge (\(100 + 100\)) addieren sich genau dort. Die beiden äußeren Knoten tragen je nur eine Feder, darum steht bei ihnen 100. Die Ecken oben rechts und unten links sind null — Knoten 1 und 3 teilen sich keine Feder.

Ü 3.2 (Verändern). Nimm wieder die drei Federn aus dem Kapitel, aber mach die mittlere zehnmal steifer: \(k_2 = 1000\ \mathrm{N/mm}\) statt 100. Am Ende ziehen weiter 50 N. Sage zuerst vorher: Welche der drei Federn dehnt sich jetzt am wenigsten, und wie weit kommt das freie Ende insgesamt — weiter oder weniger weit als die 1,5 mm von vorhin? Rechne dann nach (das Skript loesungen/kap03_ue2.py macht es vor).

Auch jetzt trägt jede Feder dieselbe Kraft von 50 N (Reihenschaltung, das ändert die Steifigkeit nicht). Aber die steife Mittelfeder gibt unter 50 N nur um \(50/1000 = 0{,}05\ \mathrm{mm}\) nach — ein Zehntel der weichen Federn. Die beiden weichen Federn dehnen sich weiter je 0,5 mm. Die Verschiebungen sind also \(0 - 0{,}5 - 0{,}55 - 1{,}05\ \mathrm{mm}\): Das freie Ende kommt nur noch 1,05 mm weit statt 1,5 mm. Merksatz: Eine steife Feder verschiebt sich wenig — sie „schluckt” kaum etwas von der Bewegung. Das ist dasselbe Prinzip, das später einen gut wärmeleitenden Trägerabschnitt kaum Temperaturgefälle aufbauen lässt.

Ü 3.3 (Übertragen). Was ändert sich, wenn die Kette an beiden Enden festsitzt? Nimm eine Kette aus vier gleichen Federn (der Einfachheit halber \(k = 1\)), fünf Knoten, und halte Knoten 1 und Knoten 5 fest. Schreibe die volle 5×5-Systemmatrix auf und streiche die Zeilen und Spalten beider fester Knoten. Was bleibt übrig — und woran erinnert es dich? (Das Skript loesungen/kap03_ue3.py baut es Schritt für Schritt.)

Die volle Matrix (mit \(k = 1\)) ist

\[ \begin{pmatrix} 1 & -1 & 0 & 0 & 0\\ -1 & 2 & -1 & 0 & 0\\ 0 & -1 & 2 & -1 & 0\\ 0 & 0 & -1 & 2 & -1\\ 0 & 0 & 0 & -1 & 1 \end{pmatrix}. \]

Streicht man Zeile und Spalte 1 (Knoten 1 fest) und Zeile und Spalte 5 (Knoten 5 fest), bleibt für die drei inneren Knoten 2, 3, 4 genau

\[ \begin{pmatrix} 2 & -1 & 0\\ -1 & 2 & -1\\ 0 & -1 & 2 \end{pmatrix} \]

übrig — die 3×3-Tabelle aus Kapitel 2, jetzt vollständig eingelöst. Der Unterschied zum Kapitel-Beispiel: Weil jetzt beide Enden fest sind, trägt jeder verbleibende Knoten zwei Federn, und alle Diagonaleinträge sind 2 (nicht mehr die 1 am freien Ende). Physikalisch tauchen jetzt zwei Haltekräfte auf, eine an jedem Ende — dass Randbedingungen so viel mitbestimmen, ist der Grund, warum Kapitel 8 ihnen ein ganzes Kapitel widmet.

Das Kleingedruckte

Ein paar ehrliche Feinheiten, abseits des Hauptpfads. Erstens: Wir haben so getan, als wäre eine Feder eine perfekt lineare Sache — doppelte Kraft, doppelte Dehnung, immer. Echte Federn und echte Werkstoffe halten sich nur in einem gewissen Bereich daran; überdehnt man sie, wird es krumm. Das ganze Buch bewegt sich bewusst im linearen Bereich (Strategie-Vereinfachung Nr. 3). Zweitens: Wir haben die erste Zeile des Systems einfach gestrichen und die Auflagerkraft \(F_1\) nicht ausgerechnet. Man kann sie hinterher bestimmen (unser Kapitel-Programm tut es in der Probe: es kommen \(-50\ \mathrm{N}\) heraus, das Auflager zieht mit 50 N dagegen) — nur für die Verschiebungen braucht man sie nicht. Drittens: „Matrix” haben wir hier strikt als „ordentlich aufgeschriebenes Gleichungssystem” behandelt, ohne Matrizenrechnung. Das reicht für das ganze Buch; die üblichen Rechenregeln für Matrizen (das „Malnehmen” zweier Matrizen) brauchen wir nie. Wer sie kennt, darf sie mitdenken — wer nicht, verpasst nichts.

Argyris, John H. 1954--1955. „Energy Theorems and Structural Analysis“. Aircraft Engineering 26–27 (1954--1955).
Clough, Ray W. 1960. „The Finite Element Method in Plane Stress Analysis“. Proceedings of the 2nd ASCE Conference on Electronic Computation (Pittsburgh, Pennsylvania).
Turner, M. J., R. W. Clough, H. C. Martin, und L. J. Topp. 1956. „Stiffness and Deflection Analysis of Complex Structures“. Journal of the Aeronautical Sciences 23 (9): 805–23. https://doi.org/10.2514/8.3664.