17  Die Rückkopplung schließt sich

In Kapitel 16 stand am Ende eine offene Tür: Die Antwortregel des Mechanostaten — Aufbau oben, Abbau unten, Totzone dazwischen — war zwar bei jeder Einzelstelle geschlossen (\(\rho \to E \to S \to \rho\)), aber die Stellen wussten nichts voneinander. Jede spürte eine feste Spannungsamplitude. In Wahrheit hängt die Spannung jeder Stelle von der ganzen Dichteverteilung ab: Wird ein Fleck steifer, zieht er Last an sich und entlastet die Nachbarn. Dieses Kapitel schließt diese letzte, räumliche Rückkopplung. Der Kreis Dichte → E-Modul → Rechnung → Reiz → Dichte läuft jetzt über das ganze Feld und wird erstmals echt gerechnet — mit einer erneuten globalen Gleichgewichtsrechnung in jeder Iteration. Und mit dem geschlossenen Kreis kommt ein Problem, das kein Zeichentrick ist: Eine zu naive Regel malt ein Schachbrett, das echter aussieht als die Wahrheit.

Lernziele

Nach diesem Kapitel kannst du

  1. die Umbauschleife als Folge statischer FEM-Rechnungen erklären (Dichte → E → Rechnung → Reiz → Dichte) und die räumliche Rückkopplung von der lokalen Einzelstelle unterscheiden,
  2. erklären, warum ein rein lokaler Reiz ein Schachbrettmuster erzeugt und wie ein nichtlokaler Filter mit fester physischer Reichweite dagegen wirkt,
  3. Konvergenz einer Fixpunktiteration erkennen (die Änderung wird klein) und von Scheinstationarität (Oszillation, Periode 2) unterscheiden,
  4. begründen, warum nur das auf der Filterskala geglättete Dichtefeld netzunabhängig ist und jedes feinere Dichtebild Scheinauflösung bleibt.

17.1 Der Aufhänger: zwei Ergebnisse, beide „konvergiert“ — welches ist echt?

Bevor das erste Bild entsteht, richtet sich das Rechenwerk dieses Kapitels ein. Es lädt zweierlei: den Rechenkern der gekoppelten Schleife (programme/kap17/rueckkopplung.py) und das offline vorberechnete Datenpaket, aus dem jedes gezeigte Dichtefeld stammt — eine volle Umbaurechnung dauert zu lange, um sie beim Blättern zu starten. Dazu kommen die drei Buchnetze und die beiden Zeichenroutinen, die alle Dichtebilder auf dieselbe magma-Skala von 0,2 bis 1,0 g/cm³ stellen, und die Nachschlagewerke, aus denen der Text seine Zahlen holt, statt sie abzuschreiben. Gedeutet wird darin nichts; das tut der Text. Wer wissen will, woher ein Name kommt, klappt die Zelle auf.

Das Rechenwerk dieses Kapitels — zum Nachlesen aufklappen
# --- Nur Zeichen- und Ladehilfen. Die EINE Rechenquelle der gekoppelten Schleife
# ist programme/kap17/rueckkopplung.py; jedes gezeichnete Dichtefeld stammt aus dem
# OFFLINE vorberechneten Datenpaket rueckkopplung_daten.json. Diese
# Zelle konfiguriert nur und deutet nichts.
import json
import pathlib
import sys

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.tri as mtri
from matplotlib.patches import FancyArrowPatch, Circle

sys.path.insert(0, str(pathlib.Path("programme/gemeinsam").resolve()))
sys.path.insert(0, str(pathlib.Path("programme/kap14").resolve()))
sys.path.insert(0, str(pathlib.Path("programme/kap17").resolve()))
import rueckkopplung as R16

komma = R16.komma
PAKET = json.loads(pathlib.Path("programme/kap17/rueckkopplung_daten.json").read_text())
NETZE = {s: json.loads(pathlib.Path("netze/netz_%s.json" % s).read_text())
         for s in ("grob", "mittel", "fein")}

# feste Dichte-Skala wie die Reglerwerkstatt (magma, 0,2–1,0 g/cm³).
DICHTE_MIN, DICHTE_MAX = 0.2, 1.0

def fest(x, stellen=2):
    """Zahl mit FESTER Nachkommastelle, deutsches Komma (komma() kürzt Nullen —
    dann stünde im Paneltitel „KS 0,4“ neben „KS 0,07“)."""
    return ("%%.%df" % stellen % x).replace(".", ",")


def dichte_bild(ax, netz, feld, titel, orient=True):
    """Ein Dichtefeld über einem Buchnetz mit fester magma-Skala und Panel-Titel.

    ``orient`` steuert die Ortszeile: In gestapelten Bildreihen tragen alle Panels
    dieselbe Orientierung; sie wird deshalb NUR im untersten Panel gezeichnet
    (``orient=True``). Weil ``set_aspect("equal")`` die Panelbreite an die
    y-Spanne bindet, bekommen Panels OHNE Ortszeile auch einen engeren
    y-Ausschnitt — sonst schrumpft der Knochen in gestapelten Reihen auf einen
    Bruchteil der Spaltenbreite (gemessener Befund der Bildprüfung). Der Titel
    steht als Achsentitel über der Datenfläche, der 10-mm-Maßstab mit festem
    Punktabstand UNTER seinem Balken (nie in ihm)."""
    kn = np.asarray(netz["knoten"]) * 1000.0
    tri = mtri.Triangulation(kn[:, 0], kn[:, 1], netz["dreiecke"])
    tp = ax.tripcolor(tri, facecolors=np.asarray(feld), cmap="magma",
                      vmin=DICHTE_MIN, vmax=DICHTE_MAX)
    ax.set_title(titel, fontsize=7.5, weight="bold", loc="left", pad=3.0)
    ax.annotate("oben", xy=(-6, 25), xytext=(-6, 11), fontsize=6.5, color="#444",
                ha="center", va="top", arrowprops=dict(arrowstyle="->",
                color="#444", lw=1.1))
    if orient:
        ax.text(-9, -5.5, "hinten (Ferse)", fontsize=6.5, color="#666")
        ax.text(67, -5.5, "vorn (Zehen)", fontsize=6.5, color="#666", ha="right")
        ax.plot([28, 38], [-10.5, -10.5], color="#333", lw=2.0, solid_capstyle="butt")
        ax.annotate("10 mm", xy=(33, -10.5), xytext=(0, -5), ha="center", va="top",
                    textcoords="offset points", fontsize=6.5, color="#333",
                    annotation_clip=False)
        unten = -16.5
    else:
        unten = -2.5
    ax.set_xlim(-10, 68); ax.set_ylim(unten, 43.5)
    ax.set_aspect("equal"); ax.axis("off")
    return tp

def gemeinsame_colorbar(fig, tp, achsen, rechts=0.80, hspace=0.18):
    """Eine gemeinsame Colorbar in EIGENER Achse VOLLSTÄNDIG rechts außerhalb der
    Panelspalte: die Panels enden bei ``rechts``, die Colorbar
    steht mit horizontalem Abstand daneben — sie durchschneidet keinen Paneltitel.
    ``hspace`` bleibt klein: jeder Zwischenraum geht bei ``aspect="equal"`` direkt
    von der Breite ab, mit der die Knochen gezeichnet werden."""
    fig.subplots_adjust(left=0.03, right=rechts, top=0.945, bottom=0.045,
                        hspace=hspace, wspace=0.12)
    cax = fig.add_axes([rechts + 0.045, 0.10, 0.02, 0.80])
    cb = fig.colorbar(tp, cax=cax, extend="neither")
    cb.set_label("Dichte ρ (g/cm³), magma 0,2–1,0", fontsize=7)
    cb.ax.tick_params(labelsize=7)

# Nachschlagewerke: jede Zahl, die der Text nennt, wird von hier geholt.
FILTER = {w["radius_mm"]: w for w in PAKET["regler_filter"]["werte"]}
SCHRITT = {w["eta"]: w for w in PAKET["regler_schrittweite"]["werte"]}
STUFEN = {s["stufe"]: s for s in PAKET["regler_netz"]["stufen"]}
HEIL = PAKET["heilung"]
EINLAUF = PAKET["animation_einlauf"]
VERGLEICH = PAKET["ap2"]              # die Kennzahlen der Vergleichsrechnung
S0_GROB = PAKET["grob"]["s0"]         # Sollwert des Buchnetzlaufs (J/kg)

_g = NETZE["grob"]
_kg = np.asarray(_g["knoten"])[np.asarray(_g["dreiecke"])]
_flg = 0.5 * np.abs((_kg[:, 1, 0] - _kg[:, 0, 0]) * (_kg[:, 2, 1] - _kg[:, 0, 1])
                    - (_kg[:, 2, 0] - _kg[:, 0, 0]) * (_kg[:, 1, 1] - _kg[:, 0, 1]))
H_GROB = float(np.sqrt(_flg.mean())) * 1000.0   # mm, Elementgröße des groben Netzes
RH_SCHRANKE = 3.5                                # Schranke R/h der Vergleichsrechnung

def ks(radius_mm, stellen=3):
    """Kantensprung des stationären Laufs bei diesem Filterradius."""
    return komma(FILTER[radius_mm]["kantensprung"], stellen)

def rh(radius_mm, stellen=1):
    """Verhältnis R/h auf dem groben Buchnetz."""
    return komma(radius_mm / H_GROB, stellen)

# Das Handnetz: Nachbarpaare, das raue Testfeld und der kleinste Schwerpunktabstand —
# die Zahlen, die der Filterblock weiter unten im Text braucht.
HAND_PAARE = R16.kantenpaare(R16.HANDNETZ_DREIECKE)
HAND_ROH = ([1.0, 0.2] * 7)[:13]
_hsw = [tuple(np.mean([R16.HANDNETZ_KNOTEN[i] for i in d], axis=0))
        for d in R16.HANDNETZ_DREIECKE]
_habst = sorted((float(np.hypot(_hsw[a][0] - _hsw[b][0], _hsw[a][1] - _hsw[b][1])) * 1000.0,
                 a, b) for a in range(13) for b in range(a + 1, 13))
HAND_NAH_MM, HAND_NAH_A, HAND_NAH_B = _habst[0]

def hand_ks(radius_mm, stellen=3):
    """Kantensprung des rauen Handnetz-Testfelds nach EINEM Filterschritt der Weite R."""
    if radius_mm <= 0:
        return komma(R16.kantensprung(HAND_ROH, HAND_PAARE), stellen)
    zeilen = R16.handnetz_filterzeilen(radius_mm / 1000.0)
    geglaettet = [sum(g * HAND_ROH[j] for j, g in zeilen[i]) for i in range(13)]
    return komma(R16.kantensprung(geglaettet, HAND_PAARE), stellen)
WichtigVorhersage-Punkt

Bevor du weiterliest: Abbildung 17.1 zeigt dasselbe Spongiosafeld zweimal, aus derselben Umbaurechnung — oben ein scharfes Muster aus vollen und leeren Elementen, unten ein glattes Dichtefeld. Beide sind „fertig gerechnet“. Welches ist der echte Knochen — das mit der schärferen Struktur oder das glattere? Und ändert sich deine Antwort, wenn du ein feineres Netz nimmst? Lege dich fest.

Code
werte = {w["radius_mm"]: w for w in PAKET["regler_filter"]["werte"]}
oben_w, unten_w = werte[0], werte[8]
fig, (a1, a2) = plt.subplots(2, 1, figsize=(6.4, 6.0),
                             gridspec_kw={"height_ratios": [1, 1.30]})
dichte_bild(a1, NETZE["grob"], oben_w["bild"], "scharfe Struktur", orient=False)
tp = dichte_bild(a2, NETZE["grob"], unten_w["bild"], "glattes Feld")
gemeinsame_colorbar(fig, tp, [a1, a2])
plt.show()
Abbildung 17.1: Dasselbe grobe Buchnetz, zweimal bis zum stationären Umbau gerechnet, gemeinsame magma-Skala, Bildreihe untereinander. Oben das Ergebnis einer rein lokalen Regel (jedes Element hört nur auf sich selbst), unten das Ergebnis mit einem nichtlokalen Filter. Beide Läufe sind stationär. Welches ist der echte Knochen? Die neutrale Frage steht hier bewusst ohne Auflösung — das Kapitel entwickelt die Antwort.

Deutung (erst nach deiner Vorhersage). Das schärfere Bild ist das Artefakt, das glattere der belastbare Knochen — obwohl das scharfe „detailreicher“ aussieht. Warum das so ist, warum die scharfe Struktur mit dem Netz mitwandert und das glatte Feld nicht, und woran man das beziffert, entwickeln die nächsten Abschnitte Schritt für Schritt. Halte deine Vorhersage fest und prüfe sie am Ende.

17.2 Die offene Tür aus Kapitel 16: aus der Regel wird ein Kreis

Kapitel 6 hatte einen Einweg-Pfeil: Die Dichte bestimmt den E-Modul, fertig (Kapitel 6). Kapitel 16 hängte die Antwortregel an, aber nur für eine Einzelstelle unter fester Last. Jetzt schließt sich der Kreis über das ganze Netz: Aus der Dichte folgt der E-Modul (Morgan), daraus über eine FEM-Rechnung auf dem Buchnetz das Spannungs- und Energiefeld, daraus der Reiz \(S = U/\rho\), daraus über den Mechanostaten eine neue Dichte — und von vorn. Abbildung 17.2 stellt den einen Pfeil neben die geschlossene Fünf-Pfeil-Schleife.

Code
fig, (a1, a2) = plt.subplots(1, 2, figsize=(7.4, 3.6),
                             gridspec_kw={"width_ratios": [1, 1.55]})
# Einweg-Pfeil (Kapitel 6).
a1.set_title("Kapitel 6: ein Pfeil", fontsize=9)
a1.text(0.5, 0.72, "Dichte ρ", ha="center", va="center", fontsize=10,
        bbox=dict(boxstyle="round", fc="#e8e0d0", ec="#7a6a4a"))
a1.text(0.5, 0.28, "E-Modul", ha="center", va="center", fontsize=10,
        bbox=dict(boxstyle="round", fc="#d0dae8", ec="#4a6a8a"))
a1.add_patch(FancyArrowPatch((0.5, 0.66), (0.5, 0.34),
             arrowstyle="-|>", mutation_scale=16, color="#333", lw=1.6))
a1.set_xlim(0, 1); a1.set_ylim(0, 1); a1.axis("off")
# Geschlossene Schleife.
a2.set_title("Kapitel 17: der Kreis schließt sich", fontsize=9)
knoten = {"Dichte ρ": (0.5, 0.90, "#e8e0d0", "#7a6a4a"),
          "E-Modul": (0.90, 0.62, "#d0dae8", "#4a6a8a"),
          "FEM-Feld": (0.72, 0.20, "#d0dae8", "#4a6a8a"),
          "Reiz S = U/ρ": (0.28, 0.20, "#d8e2c8", "#5a7a3a"),
          "Antwort g(a)": (0.10, 0.62, "#d8e2c8", "#5a7a3a")}
for name, (x, y, fc, ec) in knoten.items():
    a2.text(x, y, name, ha="center", va="center", fontsize=8.5,
            bbox=dict(boxstyle="round", fc=fc, ec=ec))
folge = ["Dichte ρ", "E-Modul", "FEM-Feld", "Reiz S = U/ρ", "Antwort g(a)",
         "Dichte ρ"]
for i in range(len(folge) - 1):
    x0, y0 = knoten[folge[i]][:2]
    x1, y1 = knoten[folge[i + 1]][:2]
    rot = (i == len(folge) - 2)   # der schließende Pfeil zurück auf ρ
    a2.add_patch(FancyArrowPatch((x0, y0), (x1, y1), arrowstyle="-|>",
                 mutation_scale=15, color="#c0392b" if rot else "#333",
                 lw=2.0 if rot else 1.4, connectionstyle="arc3,rad=0.18",
                 shrinkA=18, shrinkB=18))
a2.text(0.33, 0.68, "neue Dichte", color="#c0392b", fontsize=8, ha="center")
a2.set_xlim(0, 1); a2.set_ylim(0.05, 1.0); a2.axis("off")
plt.tight_layout(); plt.show()
Abbildung 17.2: Links der Einweg-Pfeil aus Kapitel 6: die Dichte bestimmt den E-Modul, sonst nichts. Rechts die geschlossene Rückkopplungsschleife dieses Kapitels: Dichte → E-Modul (Morgan) → FEM-Rechnung (Buchnetz, Talus-Lager, fünf Lastfälle) → Reiz S = U/ρ → Mechanostat-Antwort g(a) → neue Dichte, und wieder von vorn. Der entscheidende neue Pfeil ist der letzte, der zurück auf die Dichte zeigt: Er macht aus der Regel eine Iteration. Weil die FEM-Rechnung global ist, sind die Elemente über die Lastumlagerung gekoppelt.

Deutung. Der Unterschied ist ein einziger Pfeil — der rote, der von der neuen Dichte zurück auf die alte zeigt. Er verwandelt eine Berechnung in eine Iteration: dieselbe Rechnung, wieder und wieder, jedes Mal mit der Dichte des vorigen Durchgangs. Weil die mittlere, global gerechnete FEM-Stufe in der Mitte sitzt, ist die Schleife räumlich gekoppelt: Ändert ein Element seine Dichte, ändert sich das Gleichgewicht überall ein wenig mit. Das ist der Sprung über Kapitel 16 hinaus.

Ein Blick auf die Landkarte lohnt hier. Jede einzelne FEM-Rechnung in der Schleife ist linear: dieselbe Matrix, derselbe Löser wie in Kapitel 5, doppelte Last gibt doppelte Verschiebung. Nichtlinear ist erst die Schleife um sie herum: Der E-Modul folgt der Dichte mit einer Potenz, die Antwort hat eine tote Mitte, und die Dichte hat Grenzen. Teil II bis V haben die lineare Welt gebaut; Teil VI legt eine nichtlineare Rückkopplung um sie. Genau das meint die Zeile der Studiums-Tabelle im Vorwort, die von „nichtlinearer Rückkopplung“ spricht.

HinweisWelcher Knochen in dieser Schleife steckt

Bis Kapitel 15 war die Ferse ein einziger, homogen gedachter, kortikaler Knochen: überall 17 GPa, unveränderlich. Hier ist sie das nicht mehr, und darin liegt der ganze Unterschied — der Knochen dieses Kapitels ist homogen spongiös und veränderlich. Jedes Element trägt seine eigene Dichte zwischen 0,2 und 1,0 g/cm³, sein E-Modul folgt ihr über das Morgan-Gesetz, und die Schleife baut sie um. Eine feste kortikale Schale gibt es nicht und eine eigene Enthese-Region auch nicht. Gerechnet wird auf den gröberen Buchnetzen, mit festem Talus-Lager und den fünf Lastfällen des Kräftefahrplans (Tabelle 2.2). Kapitel 16 hat sein Reizfeld bereits auf genau diesem Startzustand gerechnet — gleichmäßige 0,50 g/cm³ —, die beiden Kapitel setzen also an derselben Stelle an. Welche Rechnungen dieses Buch sonst noch führt und worin sie sich unterscheiden, stellt Tabelle 13.3 nebeneinander. Der Sollwert \(S_0\) dieses Buchnetzlaufs (rund 8,9 J/kg) ist ein Modellwert dieser gröberen Rechnung, nicht der Reiz der numerisch geprüften Vergleichsrechnung. Die belastbaren Umbau-Kennzahlen dieses Kapitels stammen aus ihr und werden hier zitiert, nicht neu erzeugt.

Ein Wort noch zu dem Maß, mit dem dieses Kapitel gleich immer wieder misst. Wenn zwei Dichtefelder verglichen werden — dasselbe Modell auf zwei Netzstufen, derselbe Lauf mit zwei Schrittweiten —, dann heißt ihr Abstand die Feldabweichung: die Wurzel aus der Summe der quadrierten Unterschiede, geteilt durch die Wurzel aus der Summe der Quadrate des Vergleichsfeldes,

\[ \text{Feldabweichung} = \sqrt{\frac{\sum_e (\rho_e - \rho^{\text{ref}}_e)^2} {\sum_e (\rho^{\text{ref}}_e)^2}} . \]

Sie ist eine Zahl ohne Einheit und wird in Prozent angegeben: Ein Prozent heißt, die beiden Felder unterscheiden sich im Mittel um ein Hundertstel der Dichte, die im Vergleichsfeld steckt. Vergleicht man zwei verschieden feine Netze, kommen zwei Vorkehrungen dazu: Jeder Summand wird mit seiner Elementfläche gewichtet (sonst zählte das Netz mit den vielen kleinen Elementen mehr), und das gröbere Feld wird an den Schwerpunkten des feineren abgelesen, damit überhaupt Ort für Ort verglichen wird. Wo im Folgenden von „Streuung“ die Rede ist, ist dieselbe Größe gemeint, gemessen über eine ganze Reihe von Läufen.

17.3 Der absichtlich schlechte erste Versuch: das Schachbrett

Der naheliegendste Weg, die Schleife zu schließen, ist der rein lokale: Jedes Element schaut nur auf seinen eigenen Reiz und baut auf oder ab. Kein Blick zum Nachbarn. Das entspricht einem Filterradius \(R = 0\). Es klingt harmlos — und erzeugt ein Desaster mit System. Bevor wir es laufen lassen, eine Vorhersage.

WichtigVorhersage-Punkt

Bevor du die Animation startest: Zwei Läufe starten aus derselben uniformen Dichte — links ohne Filter (\(R = 0\)), rechts mit dem Reizfilter (\(R = 8\) mm). Was passiert links, wenn jedes Element nur auf seinen eigenen Reiz hört, und was rechts, wenn die Nachbarschaft mithört? Lege dich fest, dann spiele ab.

Deutung der Animation. Das ist Ursache und Gegenmittel in einem Bild: Links (\(R = 0\)), wo jedes Element nur auf sich selbst hört, wächst das Schachbrett — ein lokales positives Feedback koppelt voll und leer auf die kleinste Netzskala. Rechts (\(R = 8\) mm), wo der Reizfilter die Nachbarschaft mitmitteln lässt, bleibt das Feld glatt. Dieselbe Startdichte, derselbe Iterationsschritt — nur die Nachbarschaft macht den Unterschied. Der Filter verhindert das Artefakt. (Ob er ein bereits entstandenes Schachbrett auch wieder glätten kann, ist eine eigene Frage — sie kommt gleich, und die Antwort überrascht.) Ein Blick auf die stationären Kantensprung-Zahlen ordnet den Eindruck:

Code
werte = {w["radius_mm"]: w for w in PAKET["regler_filter"]["werte"]}
roh, glatt = werte[0], werte[8]
fig, (a1, a2) = plt.subplots(2, 1, figsize=(6.4, 6.0),
                             gridspec_kw={"height_ratios": [1, 1.30]})
dichte_bild(a1, NETZE["grob"], roh["bild"],
            "R = 0 (kein Filter) · Kantensprung %s" % fest(roh["kantensprung"], 3),
            orient=False)
tp = dichte_bild(a2, NETZE["grob"], glatt["bild"],
                 "R = 8 mm (Filter) · Kantensprung %s" % fest(glatt["kantensprung"], 3))
gemeinsame_colorbar(fig, tp, [a1, a2])
plt.show()

print("Grobes Buchnetz, stationärer Umbau, Kantensprung-Indikator:")
print("  R = 0 (kein Filter): %s   (Schachbrett)" % komma(roh["kantensprung"], 3))
print("  R = 8 mm (Filter)  : %s   (glatt)" % komma(glatt["kantensprung"], 3))
print("  Verhältnis         : %s-fach kleiner mit Filter"
      % komma(roh["kantensprung"] / glatt["kantensprung"], 1))
Abbildung 17.3: Dasselbe grobe Buchnetz, zweimal bis zur Stationarität gerechnet, gemeinsame magma-Skala. Oben rein lokal (R = 0): ein scharfes Schachbrett aus vollen und leeren Elementen. Unten mit dem nichtlokalen Filter (R = 8 mm): ein glattes Dichtefeld. Der Kantensprung steht als Zahl über jedem Panel. Sagittalschnitt, hinten (Ferse) links, vorn (Zehen) rechts, oben ↑; Maßstab 10 mm.
Grobes Buchnetz, stationärer Umbau, Kantensprung-Indikator:
  R = 0 (kein Filter): 0,695   (Schachbrett)
  R = 8 mm (Filter)  : 0,065   (glatt)
  Verhältnis         : 10,6-fach kleiner mit Filter

Deutung. Das ist die Antwort auf die Vorhersagefrage, und sie ist unbequem: Ohne Filter entsteht ein Schachbrett aus vollen und leeren Elementen. Der Kantensprung-Indikator — der mittlere Dichteunterschied zwischen benachbarten Elementen, auf die mittlere Dichte bezogen — springt von 0,065 (glatt) auf 0,695 (Schachbrett), rund 11-mal so groß. Das Muster sieht detailreicher aus als das glatte Feld und ist doch das Falsche. Der Grund ist ein lokales positives Feedback: Ein Element, das ein wenig steifer wird, zieht Last an sich, sein Reiz steigt, es baut weiter auf — und entzieht damit den unmittelbaren Nachbarn Last, die daraufhin abbauen. Weil der Reiz elementweise ausgewertet wird, koppelt sich dieses Auf-und-Ab auf die kleinste Skala, die das Netz hat: ein Element voll, das nächste leer. Das ist ein Netzartefakt, genau die Warnung aus Kapitel 13 — stabil aussehen und trotzdem netzabhängig falsch sein.

WarnungSchärfer heißt nicht echter

Verbreitete Vermutung: „Wo die Dichtekarte feine, scharfe Strukturen zeigt, hat die Simulation echte Trabekel gefunden.“

Warum sie naheliegt: Das Schachbrett hat mehr Kontrast und mehr Detail als das glatte Feld — und echte Spongiosa besteht ja aus feinen Bälkchen. Mehr Struktur, mehr Wahrheit, so scheint es.

Was stattdessen gilt: Ein rein lokales positives Feedback macht ein Element immer dichter und seinen Nachbarn immer leerer — das Schachbrett ist ein Netzartefakt, das mit dem Netz mitwandert, keine Trabekel. Die belastbare Struktur ist nur so fein wie die Reichweite des nichtlokalen Filters; alles darunter ist Scheinauflösung. Ein feineres Netz liefert dort nicht mehr Wahrheit, nur ein anderes Artefakt (ungefiltert 40,6 % Feldabweichung in der numerisch geprüften Vergleichsrechnung). Das Buch modelliert Knochen ohnehin als Dichtekontinuum — als stetiges Dichtefeld, nicht als einzelne Bälkchen mit echten Löchern; ein Schachbrett ist auch deshalb keine Trabekelstruktur.

Der Kantensprung-Indikator ist eine einfache Rechnung, und sie steht hier sichtbar, damit die Zahl im Bild kein Orakel bleibt. Der Indikator geht jede innere Kante des Netzes durch — jede Kante, an der zwei Dreiecke aneinanderstoßen —, misst den Betrag des Dichteunterschieds und mittelt ihn, bezogen auf die mittlere Dichte. Ein glattes Feld hat kleine Sprünge, ein Schachbrett große. Der folgende Block schreibt diese Rechnung als Funktion aus und legt zwei winzige Handbeispiele daneben — vier Elemente in einer Reihe, einmal fast gleich, einmal abwechselnd leer und voll.

Der Kantensprung-Indikator, in sechs Zeilen
# Der Kantensprung-Indikator, an zwei Handbeispielen nachgerechnet. Er misst, wie
# stark benachbarte Elemente sich in der Dichte unterscheiden — klein bei glatt,
# groß beim Schachbrett. „paare" sind die benachbarten Elementpaare (i, j).
def kantensprung(dichten, paare):
    mittel = sum(dichten) / len(dichten)
    summe = 0.0
    for (i, j) in paare:                    # jede innere Kante einmal
        summe = summe + abs(dichten[i] - dichten[j])
    return (summe / len(paare)) / mittel     # mittlerer Sprung, auf ρ̄ bezogen

nachbarn = [(0, 1), (1, 2), (2, 3)]          # vier Elemente in einer Reihe
glatt = [0.50, 0.52, 0.51, 0.50]             # fast gleich -> kleiner Sprung
schachbrett = [0.20, 1.00, 0.20, 1.00]       # abwechselnd leer/voll -> großer Sprung

Tabelle 17.1 hält das Ergebnis der beiden Handbeispiele fest.

Tabelle 17.1: Der Kantensprung-Indikator an zwei Handbeispielen aus vier Elementen in einer Reihe — derselbe Indikator, der über die inneren Kanten des Buchnetzes gemittelt die Werte der Bilder oben liefert.
Handbeispiel Dichten der vier Elemente (g/cm³) Kantensprung
glattes Feld 0,50, 0,52, 0,51, 0,50 0,026
Schachbrett 0,20, 1,00, 0,20, 1,00 1,333

Deutung unter der Tabelle. Das glatte Handfeld hat einen winzigen Kantensprung — die Nachbarn unterscheiden sich kaum. Das Schachbrett-Handfeld springt auf ein Vielfaches davon: Jede Kante trägt den vollen Unterschied zwischen leer (0,20) und voll (1,00). Genau diese Rechnung, über die tausend inneren Kanten des Buchnetzes gemittelt, liefert die beiden Zahlen der Bilder oben. Der Indikator macht das Schachbrett zu einer Zahl, die man messen und vergleichen kann — und die man gleich als Regler-Anzeige wiedersieht.

17.4 Die Nachbarschaft hört mit: der nichtlokale Filter

Das Gegenmittel ist, jedem Element das Zuhören zu verbieten, das nur auf sich selbst hört. Statt seinen eigenen Reiz zu nehmen, nimmt jedes Element \(i\) ein gewichtetes Mittel der Reize seiner Nachbarn \(j\) im festen Umkreis \(R\). Das Gewicht fällt linear mit dem Abstand \(d_{ij}\) der Elementschwerpunkte (ein Zeltdach), wird mit der Nachbarfläche \(A_j\) multipliziert und die ganze Zeile auf Summe eins normiert:

\[ W_{ij} = \frac{A_j \,\max(0,\; 1 - d_{ij}/R)}{\displaystyle\sum_k A_k \,\max(0,\; 1 - d_{ik}/R)}, \qquad S^{\text{glatt}}_i = \sum_j W_{ij}\, S_j. \]

Drei Dinge stecken darin: das Zeltdach \(\max(0, 1 - d/R)\) (nahe Nachbarn zählen mehr, jenseits \(R\) null), die Fläche \(A_j\) (ein großes Element trägt mehr Gewicht als ein winziges — sonst hinge die Glättung von der Elementgröße ab) und die Zeilennormierung (die Summe der Gewichte ist genau eins, ein konstantes Reizfeld bleibt unverändert). Der Randfall ist damit sauber geregelt: Am Rand des Netzes hat ein Element weniger Nachbarn, aber die Zeile wird trotzdem auf eins normiert — es mittelt nur über die vorhandenen. Abbildung 17.4 zeigt die echten normierten Gewichte \(W_{ij}\) eines Zielelements mit \(R = 8\) mm.

Code
netz = NETZE["grob"]
kn = np.asarray(netz["knoten"]); tr = np.asarray(netz["dreiecke"])
schwer = kn[tr].mean(axis=1)
_x = kn[tr]
flaeche_el = 0.5 * np.abs((_x[:, 1, 0] - _x[:, 0, 0]) * (_x[:, 2, 1] - _x[:, 0, 1])
                          - (_x[:, 2, 0] - _x[:, 0, 0]) * (_x[:, 1, 1] - _x[:, 0, 1]))
R = 0.008
# ein Zielelement etwa in der Mitte der Ferse wählen.
ziel = int(np.argmin(np.sum((schwer - np.array([0.030, 0.020])) ** 2, axis=1)))
# die ECHTEN normierten Gewichte aus dem geteilten Kern (eine Filterzeile).
W = R16.filtermatrix_zeltdach(schwer, flaeche_el, R)
zeile = np.asarray(W[ziel].todense()).ravel()
gewicht = np.where(zeile > 0, zeile, np.nan)
fig, ax = plt.subplots(figsize=(6.4, 4.6))
trg = mtri.Triangulation(kn[:, 0] * 1000, kn[:, 1] * 1000, tr)
# Der GANZE Knochen zuerst in Hellgrau — ohne ihn schwebte der Filterfleck auf
# weißem Grund und der Ort im Fersenbein war nicht zu erkennen. Die Elemente
# jenseits von R behalten dieses Grau und sind damit sichtbar „Gewicht null".
ax.tripcolor(trg, facecolors=np.full(len(tr), 0.55), cmap="Greys",
             vmin=0.0, vmax=1.0)
ax.triplot(trg, color="white", lw=0.25, alpha=0.6)
tp = ax.tripcolor(trg, facecolors=gewicht, cmap="viridis")
cb = plt.colorbar(tp, ax=ax, fraction=0.046, pad=0.03)
cb.set_label("normiertes Filtergewicht W_ij (Σ = 1)", fontsize=7); cb.ax.tick_params(labelsize=7)
sz = schwer[ziel] * 1000
ax.add_patch(Circle((sz[0], sz[1]), R * 1000, fill=False, ec="#c0392b", lw=1.8, ls="--"))
ax.plot(sz[0], sz[1], "o", color="white", ms=6, mec="#111", mew=1.2)
ax.annotate("Zielelement", xy=(sz[0], sz[1]), xytext=(sz[0] - 15, sz[1] + 16),
            fontsize=7.5, color="#111", ha="right",
            arrowprops=dict(arrowstyle="->", color="#111", lw=1.0))
ax.text(sz[0] + R * 1000 + 1.5, sz[1] - R * 1000 - 1.0, "R = 8 mm", color="#c0392b",
        fontsize=8.5, ha="left", va="top", weight="bold",
        bbox=dict(boxstyle="round,pad=0.18", fc="white", ec="#c0392b", lw=0.6))
ax.text(2, -5.0, "hinten (Ferse)", fontsize=7, color="#666")
ax.text(64, -5.0, "vorn (Zehen)", fontsize=7, color="#666", ha="right")
ax.annotate("oben", xy=(-4, 22), xytext=(-4, 9), fontsize=7, color="#444",
            ha="center", va="top", arrowprops=dict(arrowstyle="->", color="#444", lw=1.0))
ax.plot([48, 58], [-9.5, -9.5], color="#333", lw=2.0, solid_capstyle="butt")
ax.annotate("10 mm", xy=(53, -9.5), xytext=(0, -5), textcoords="offset points",
            ha="center", va="top", fontsize=7, annotation_clip=False)
ax.text(-6, 46.5, "grau: außerhalb R, Gewicht null", fontsize=7.5, color="#444")
ax.set_xlim(-8, 68); ax.set_ylim(-15, 48); ax.set_aspect("equal"); ax.axis("off")
plt.tight_layout(); plt.show()

print("Filterzeile des Zielelements (R = 8 mm): %d Nachbarn, Gewichtssumme %s (= 1)"
      % (int((zeile > 0).sum()), komma(float(zeile.sum()), 4)))
Abbildung 17.4: Der nichtlokale Filter für ein Zielelement (weißer Punkt) im groben Buchnetz, Radius R = 8 mm. Der ganze Fersenschnitt liegt hellgrau darunter, damit der Ort des Zielelements im Knochen erkennbar bleibt. Eingefärbt sind die ECHTEN normierten Filtergewichte W_ij aus dem geteilten Kern (reglerkern.filtermatrix_zeltdach) — Fläche mal Zeltdach, zeilenweise auf Summe eins normiert (die Farbwerte summieren sich also über alle Nachbarn zu eins). Der rot gestrichelte Kreis ist der Radius R; jenseits davon bleibt das Element grau und zählt null. Sagittalschnitt, hinten (Ferse) links, vorn (Zehen) rechts, oben ↑; Maßstab 10 mm.
Filterzeile des Zielelements (R = 8 mm): 91 Nachbarn, Gewichtssumme 1 (= 1)

Deutung. Das ist die ganze Idee des Filters, mit den echten Gewichten. Das Zielelement bekommt nicht mehr seinen eigenen, elementscharfen Reiz, sondern das mit \(W_{ij}\) gewichtete Mittel der Reize seiner Umgebung; die Gewichte summieren sich zu eins (die Zelle druckt es nach). Weil zwei benachbarte Elemente fast dieselbe Umgebung mitteln, können sie nicht mehr gegeneinander springen — das Schachbrett kann sich nicht bilden. Entscheidend ist, dass \(R\) eine feste physische Länge ist (8 mm), keine Zahl von Elementkanten: Auf einem feineren Netz liegen im selben Umkreis \(R\) mehr Elemente, sodass der Filter dieselbe physische Skala glättet — das ist der Grund, warum erst er die Netzunabhängigkeit herstellt. Was das für das Ergebnis heißt, macht der Regler erlebbar.

WichtigVorhersage-Punkt

Bevor du den Regler bewegst: Setzt du \(R\) auf null — entsteht ein Schachbrett? Und wenn du \(R\) groß machst: wird das Feld glatt, und wird das Ergebnis auf einem feineren Netz dadurch besser oder nur anders? Lege dich fest, dann schiebe.

Deutung. Die Antwort auf die Vorhersagefrage: \(R = 0\) erzeugt das Schachbrett (Kantensprung 0,695); schon ein kleiner Radius senkt ihn kräftig (bei \(R = 2\) mm auf 0,141), und \(R = 8\) mm liefert ein glattes Feld (0,065). Der Kantensprung fällt monoton mit \(R\). Der Filter macht das Ergebnis also glatt — aber die zweite Hälfte der Vorhersagefrage ist die wichtigere: Ein feineres Netz macht das gefilterte Feld nicht „besser“ im Sinne von „schärfer“, sondern stabil. Das ist der Kern der Netzlehre weiter unten.

Und hier steckt eine Warnung, die man dem Bild allein nicht ansieht — deshalb führt der Regler neben \(R\) auch das Verhältnis \(R/h\) mit, wobei \(h\) die Elementgröße des Netzes ist (im groben Buchnetz 1,57 mm, die Wurzel der mittleren Dreiecksfläche). Ein zu kleiner Radius hilft nicht, auch wenn das Feld schon glatter aussieht: Bei \(R = 2\) mm ist \(R/h\) gleich 1,3, bei \(R = 4\) mm erst 2,5 — das Zeltdach erfasst dann so wenige Nachbarn, dass die Glättungsskala noch am Netz hängt und ein feineres Netz ein anderes Ergebnis liefert. Erst ab \(R = 6\) mm (\(R/h\) gleich 3,8) und bei den \(R = 8\) mm dieses Kapitels (5,1) ist die Schranke von 3,5 eingehalten, die die numerisch geprüfte Vergleichsrechnung auf allen ihren Netzstufen gewahrt hat. Der Regler sagt es an jeder Stellung ausdrücklich dazu: Ein glatt aussehendes Feld ist noch kein netzunabhängiges.

Woher kommen die 8 mm? Sie stehen nicht im Zahlenanhang — sie sind ein Modellparameter dieses Kapitels, und ihre Größenordnung ist von zwei Seiten eingeklemmt. Nach unten von der Elementgröße: Unter einem \(R/h\) von rund 3,5 mittelt der Filter über zu wenige Nachbarn, und die Glättungsskala hängt wieder am Netz — das ist die Schranke von eben. Nach oben von der Ferse selbst, die nur 66 mm lang ist: Ein Radius in der Größenordnung des Knochens verwischte genau die Struktur, die man sehen will. Acht Millimeter liegen dazwischen — rund ein Achtel der Knochenlänge und gut das Fünffache der Elementgröße des groben Buchnetzes. Mit dieser Wahl hat die Vergleichsrechnung gerechnet, und jede Zahl dieses Kapitels hängt an ihr; eine biologisch gemessene Reichweite ist sie nicht.

HinweisVerwandt, aber nicht dasselbe: Topologieoptimierung

Derselbe Zeltdach-Filter und dasselbe Schachbrett-Problem kennt man aus der Topologieoptimierung — dem Entwurf von Bauteilen, die bei minimalem Material möglichst steif sind. Dort ist die Schachbrettinstabilität ein klassisches Ärgernis, und der nichtlokale Filter ist das Standard-Gegenmittel. Unser Umbaumodell ist damit verwandt, aber nicht dasselbe: Es optimiert nichts, sondern folgt einer biologischen Regel (dem Mechanostaten). Die Technik ist geliehen, die Frage ist eine andere.

17.5 Vorbeugen oder heilen? Ein fertiges Schachbrett und der Filter

Der Filter verhindert das Schachbrett, wenn er von Anfang an mitrechnet — das zeigte die Vergleichsanimation. Aber was, wenn das Schachbrett schon da ist? Kann man den Filter nachträglich einschalten und das Muster wieder glattziehen? Die Frage ist nicht rhetorisch; ihre Antwort ist ein eigener Lehrmoment.

WichtigVorhersage-Punkt

Bevor du weiterliest: Ein Lauf hat bei \(R = 0\) ein fertiges, stationäres Schachbrett gebildet (Kantensprung 0,695). Jetzt schaltest du den Reizfilter (\(R = 8\) mm) ein und rechnest weiter. Wird das Schachbrett wieder glatt wie ein von Anfang an gefilterter Lauf (Kantensprung 0,065), oder bleibt es rau? Lege dich fest.

Code
heil = PAKET["heilung"]
panels = [("schachbrett", "1. Schachbrett (R = 0)"),
          ("weiter_freeze", "2. Reizfilter NACH dem Schachbrett"),
          ("weiter_frueh", "3. Reizfilter früh (Iter. 40)"),
          ("von_anfang", "4. von Anfang an gefiltert")]
fig, achsen = plt.subplots(4, 1, figsize=(6.4, 8.6),
                           gridspec_kw={"height_ratios": [1, 1, 1, 1.30]})
for j, (ax, (schl, titel)) in enumerate(zip(achsen, panels)):
    sprung = heil[schl + "_kantensprung"]; status = heil[schl + "_status"]
    zusatz = "" if status == "stationaer" else " [nicht stat.]"
    tp = dichte_bild(ax, NETZE["grob"], heil[schl + "_bild"],
                     "%s · KS %s%s" % (titel, fest(sprung, 3), zusatz),
                     orient=(j == 3))
gemeinsame_colorbar(fig, tp, list(achsen), hspace=0.16)
plt.show()

print("Kann der Filter ein fertiges Schachbrett heilen? Kantensprung der vier Läufe:")
print("  1. Schachbrett (R=0)            : %s (%s)"
      % (komma(heil["schachbrett_kantensprung"], 3), heil["schachbrett_status"]))
print("  2. Reizfilter NACH Schachbrett  : %s (%s)"
      % (komma(heil["weiter_freeze_kantensprung"], 3), heil["weiter_freeze_status"]))
print("  3. Reizfilter früh (Iter. 40)   : %s (%s)"
      % (komma(heil["weiter_frueh_kantensprung"], 3), heil["weiter_frueh_status"]))
print("  4. von Anfang an gefiltert      : %s (%s)"
      % (komma(heil["von_anfang_kantensprung"], 3), heil["von_anfang_status"]))
Abbildung 17.5: Vier echte Läufe auf dem groben Buchnetz, gemeinsame magma-Skala, von oben nach unten: (1) das bei R = 0 eingelaufene Schachbrett; (2) Reizfilter erst NACH dem Schachbrett eingeschaltet, echter Weiterlauf ab Zustand 1; (3) Reizfilter schon bei Iteration 40 eingeschaltet, also früh, aber nach beginnendem Schachbrett; (4) von Anfang an gefiltert. Über jedem Panel steht sein Kantensprung (KS). Sagittalschnitt, hinten (Ferse) links, vorn (Zehen) rechts, oben ↑; Maßstab 10 mm.
Kann der Filter ein fertiges Schachbrett heilen? Kantensprung der vier Läufe:
  1. Schachbrett (R=0)            : 0,695 (stationaer)
  2. Reizfilter NACH Schachbrett  : 0,581 (stationaer)
  3. Reizfilter früh (Iter. 40)   : 0,398 (stationaer)
  4. von Anfang an gefiltert      : 0,065 (stationaer)

Deutung. Die Antwort überrascht und ist der eigentliche Lehrmoment: Nein — der Reizfilter heilt ein fertiges Schachbrett nicht. Schaltet man ihn erst nach dem eingelaufenen Muster ein, sinkt der Kantensprung kaum (von 0,695 auf 0,581; der Lauf läuft zwar in einen stationären Zustand, bleibt aber ein rauer); schaltet man ihn früh ein, wird er ebenfalls stationär, bleibt aber rau (0,398) — weit weg von den 0,065 eines von Anfang an gefilterten Laufs.

Der Grund sind zwei Bremsen, die Kapitel 16 vorbereitet hat. Die erste ist die Sättigung: Ein Element, das an die Dichtegrenze 1,0 g/cm³ gelaufen ist, kann nicht weiter aufbauen, eines an der Grenze 0,2 g/cm³ nicht weiter abbauen — gleich, was der geglättete Reiz von ihm verlangt. Die zweite ist das Totzonen-Band: Elemente dazwischen, deren geglätteter Reiz in der toten Mitte liegt, bekommen die Antwort null und bleiben, wo sie sind. Beides zusammen friert das Muster ein; der stationäre Zustand erinnert sich an den Start. Deshalb ist der Filter ein Vorbeugen, kein Heilen: Man muss von Anfang an richtig rechnen.

Und weil hier drei ähnlich aussehende Größen dicht beieinanderliegen, bleiben sie im ganzen Kapitel getrennt: Der Algorithmus glättet den Reiz (\(W\cdot S\)), die Dichte \(\rho\) ist sein rohes Ergebnis, und \(W\cdot\rho\) ist eine nachträgliche Auswertung dieser Dichte auf der Filterskala. Eine fertige Dichte nachträglich zu glätten sieht glatt aus, ist aber ein anderer Vorgang als das, was oben gerechnet wurde — es ersetzt den Weiterlauf nicht.

17.6 Die Kernschleife von Hand

Die ganze Schleife ist klein genug, um sie auf dem winzigen Handnetz in reinem Python durchlaufen zu lassen — dreizehn Knoten, dreizehn Dreiecke, dieselben Nummern wie in Kapitel 10. Auch Lager und Last sind dieselben wie dort, wo das Netz zum ersten Mal vollständig gerechnet wurde (Kapitel 11): festgehalten wird es oben an der Talus-Gelenkfläche, an den Knoten 4, 5 und 6; die Bodenreaktion von 2,5 · KG sitzt unten auf den drei Bodenfacetten (0, 7), (0, 9) und (9, 10), auf jede Facette nach ihrer Länge und von dort je zur Hälfte auf ihre beiden Endknoten. Last- und Lagerknoten sind damit getrennt — keine Kraft geht beim Festhalten verloren. Neu gegenüber Kapitel 11 ist allein, dass die Dichte jetzt nicht mehr feststeht, sondern Iteration für Iteration umgebaut wird.

Der sichtbare Lehrcode dieses Kapitels rechnet dabei dieselbe Physik wie der schnelle Löser hinter den Reglern; das Buch prüft bei jedem Bau, dass beide auf diesem Handnetz Ziffer für Ziffer dasselbe Reizfeld liefern. Die Funktion handnetz_reiz_python(dichten) führt den Kern jeder Iteration aus — E-Modul je Element (Morgan), FEM-Rechnung, daraus der Reiz \(S = U/\rho\) —, alles mit ausgeschriebenen Schleifen. Die folgende Zelle setzt darauf die vollständige Rückkopplung: den Reizfilter \(W\cdot S\), die Mechanostat-Antwort \(g(a)\), das begrenzte Dichte-Update und den Abbruch, wenn sich nichts mehr ändert.

Statt am Ende nur „stationär nach so und so vielen Iterationen“ zu drucken, hält die Schleife ihren eigenen Verlauf fest — das flächengewichtete Dichtemittel und die RMS-Änderung je Iteration — und trägt beides auf: die mittlere Dichte auf der linken Achse, die RMS-Änderung logarithmisch auf der rechten. So wird der Einlauf zur Kurve, an der man den stationären Punkt abliest, statt ihn zu behaupten.

Die Kernschleife der Rückkopplung, Zeile für Zeile
# Die Kernschleife der Rückkopplung, von Hand auf dem winzigen Handnetz. Jede
# Iteration: aus den Dichten das Reizfeld (handnetz_reiz_python: E → FEM → S),
# dann der nichtlokale Filter (W·S), die Antwortkurve g(a) mit Totzone, das
# begrenzte Update Δρ und die Abbruch-Prüfung über die RMS-Änderung. Der Verlauf
# (mittlere Dichte und RMS je Iteration) wird mitgeschrieben und aufgetragen.
import rueckkopplung as werk

zeilen = werk.handnetz_filterzeilen()          # Filter-Nachbarn je Element (W)
flaeche = werk.dreiecksflaechen_handnetz()      # Elementflächen (für Mittel und RMS)
dichten = [0.50] * 13                            # uniformer Start

# Sollwert S0: flächengewichtetes Reizmittel des Startzustands, danach fest.
reiz = werk.handnetz_reiz_python(dichten)
s0 = sum(r * a for r, a in zip(reiz, flaeche)) / sum(flaeche)

iterationen, mittel_verlauf, rms_verlauf = [], [], []
folge = 0
for iteration in range(1, 401):
    reiz = werk.handnetz_reiz_python(dichten)              # E → FEM → S
    reiz_glatt = []                                        # W · S (Reizfilter)
    for i in range(13):
        reiz_glatt.append(sum(gewicht * reiz[j] for j, gewicht in zeilen[i]))
    neu = []
    rms = 0.0
    for e in range(13):
        a = reiz_glatt[e] / s0 - 1.0                       # Reizabweichung
        if a < -0.08:                                      # g(a): Abbau
            g = a + 0.08
        elif a > 0.08:                                     # g(a): Aufbau
            g = a - 0.08
        else:                                              # Totzone
            g = 0.0
        d = max(-0.04, min(0.04, 0.08 * g))                # Δρ = clip(η·g)
        r = max(0.20, min(1.00, dichten[e] + d))           # ρ in [0,2; 1,0]
        rms += flaeche[e] * (r - dichten[e]) ** 2
        neu.append(r)
    rms = (rms / sum(flaeche)) ** 0.5
    dichten = neu
    iterationen.append(iteration)                          # Verlauf mitschreiben
    mittel_verlauf.append(sum(r * a for r, a in zip(dichten, flaeche)) / sum(flaeche))
    rms_verlauf.append(max(rms, 1e-12))
    folge = folge + 1 if rms < 1e-5 else 0                 # Abbruch: RMS klein
    if folge >= 3:
        break

# Zeichnung in die Hilfsfunktion ausgelagert; sie erzeugt die Figur, die Quarto
# als Abbildung dieser Zelle einbindet (mittlere Dichte links, RMS rechts/log).
fig = werk.konvergenzbild(iterationen, mittel_verlauf, rms_verlauf)
Abbildung 17.6: Konvergenz der Kernschleife auf dem winzigen Handnetz (uniformer Start ρ = 0,50 g/cm³, Reizfilter): linke Achse (bordeaux) das flächengewichtete Dichtemittel je Iteration, rechte Achse (blau, logarithmisch) die RMS-Änderung Δρ. Die mittlere Dichte läuft auf ihren stationären Wert ein; die RMS-Änderung fällt um mehrere Größenordnungen, bis der Abbruch (RMS dreimal unter 10⁻⁵) greift. Die gestrichelte Linie markiert die Abbruch-Iteration.

Welchen Zustand die Schleife am Ende erreicht, hält Tabelle 17.2 elementweise fest — die dreizehn Enddichten des Handnetzes, wie die letzte Iteration sie hinterlässt.

Tabelle 17.2: Endzustand der Handnetz-Schleife nach 125 Iterationen: die Dichte jedes der dreizehn Dreiecke des Handnetzes. Sollwert dieses Laufs S₀ = 1,34 J/kg (flächengewichtetes Reizmittel des Startzustands).
Element Enddichte ρ (g/cm³)
0 0,596
1 0,652
2 0,507
3 0,200
4 0,491
5 1,000
6 0,782
7 0,200
8 0,200
9 0,200
10 0,200
11 0,200
12 0,200

Deutung unter der Tabelle. Kurve und Tabelle machen den Motorraum sichtbar. Die Enddichten zerfallen in zwei Gruppen: 7 der dreizehn Dreiecke sind auf die untere Dichtegrenze 0,2 g/cm³ gelaufen und dort liegen geblieben, die übrigen tragen zwischen 0,49 und 1,00 g/cm³ — der Lastpfad zeichnet sich als dichteres Band ab, daneben bleibt kaum Material. Das eine Dreieck, das an die obere Grenze gelaufen und dort gesättigt ist, hängt am hintersten der drei Lagerknoten — also genau an der Kante der Einspannung, an der Kapitel 13 die Spannungsspitze gezeigt hat und der man am wenigsten trauen darf. Und die Rechnung dahinter: Jede Iteration rechnet erst das Reizfeld (E-Modul, FEM, \(S = U/\rho\) — in handnetz_reiz_python, reines Python), dann glättet der Reizfilter jeden Reiz mit dem seiner Nachbarn (\(W\cdot S\), die ausgeschriebene innere Schleife), dann übersetzt \(g(a)\) die Reizabweichung in Auf-, Ab- oder keinen Umbau, und das begrenzte \(\Delta\rho\) verschiebt die Dichte. Man liest den Einlauf jetzt ab, statt ihn zu glauben: Die mittlere Dichte fällt vom uniformen Start auf ihren stationären Wert von rund 0,38 g/cm³ — nicht streng monoton, sie fällt erst schnell, steigt kurz wieder ein wenig an und sinkt dann endgültig ab —, während die RMS-Änderung um mehrere Größenordnungen kleiner wird; nach 125 Iterationen ist sie dreimal in Folge unter \(10^{-5}\) und der Lauf hält an. Das ist derselbe Ablauf wie im großen Buchnetz-Modell — nur so klein, dass man jede Zeile lesen kann. Und das Buch prüft bei jedem Bau, dass es wirklich derselbe ist: handnetz_reiz_python und der schnelle Löser hinter den Reglern liefern auf diesem Netz dasselbe Reizfeld, bis auf zwölf Nachkommastellen.

17.6.1 Den Filter selbst drehen: die Weite R

Die eine Stellschraube, die über glatt oder rau entscheidet, ist die Filterweite \(R\) — der Radius, über den jeder Reiz mit dem seiner Nachbarn gemittelt wird. In der Schleife oben steckt sie fest in handnetz_filterzeilen(). Der folgende Block holt sie heraus und legt sie in deine Hand: Er nimmt ein künstlich raues Testfeld auf dem Handnetz — abwechselnd voll (1,0) und leer (0,2), das Schachbrett im Kleinen — und wendet den Glättungsoperator \(W\) allein mit deiner Weite \(R\) direkt darauf an. Das isoliert bewusst nur die Glättung: Im vollen Umbau oben glättet \(W\) den Reiz \(W\cdot S\), und die Dichte entwickelt sich daraus Schritt für Schritt; hier gibt es keine Dichteentwicklung und keinen Heilungsversuch, nur den einen Filterschritt auf einem festen Muster. Der Kantensprung (mittlerer Sprung über die inneren Elementkanten, normiert; groß beim Schachbrett, null beim glatten Feld) misst, was danach übrig bleibt. Das Bild stellt beide Felder nebeneinander: je Element ein graues Balkenpaar für das rohe Schachbrett und ein farbiges für das gefilterte Feld. Je stärker die farbigen Balken sich einander angleichen, desto besser hat der Filter geglättet.

Die Balken tragen die Nummer ihres Dreiecks, und damit die Zahl auf der Achse ein Ort im Knochen wird, zeigt Abbildung 17.7 zuerst, welches Dreieck wo liegt. Zwei Lagen sind gleich wichtig: Am dichtesten stehen die unteren mittleren Dreiecke 2 und 3 beieinander — ihre Schwerpunkte trennen nur 5,8 mm; am weitesten abseits liegen die vorderen Dreiecke 7, 8 und 9 im schlanken Vorderfortsatz. Genau diese Streuung entscheidet gleich darüber, ab welcher Weite \(R\) der Filter überhaupt greift und ab welcher er auch die entfernten Dreiecke erreicht.

Code
_hkn = np.asarray(R16.HANDNETZ_KNOTEN) * 1000.0
_htr = np.asarray(R16.HANDNETZ_DREIECKE)
fig, ax = plt.subplots(figsize=(6.4, 4.0))
ax.triplot(mtri.Triangulation(_hkn[:, 0], _hkn[:, 1], _htr), color="#7a6a4a", lw=1.1)
for _e, _d in enumerate(_htr):
    _s = _hkn[_d].mean(axis=0)
    ax.text(_s[0], _s[1], str(_e), ha="center", va="center", fontsize=9,
            weight="bold", color="#7a2b8a",
            bbox=dict(boxstyle="circle,pad=0.16", fc="white", ec="#7a2b8a", lw=0.8))
for _k, (_x, _y) in enumerate(_hkn):
    ax.plot(_x, _y, "o", color="#555", ms=3.0)
    ax.text(_x + 0.8, _y + 0.9, "K%d" % _k, fontsize=6.5, color="#666")
ax.text(0, 40, "violett: Nummer des Dreiecks (Achse des Blocks) · grau: Knoten",
        fontsize=7.5, color="#444")
ax.text(0, -4.5, "hinten (Ferse)", fontsize=7, color="#666")
ax.text(68, -4.5, "vorn (Zehen)", fontsize=7, color="#666", ha="right")
ax.annotate("oben", xy=(-5, 22), xytext=(-5, 9), fontsize=7, color="#444",
            ha="center", va="top", arrowprops=dict(arrowstyle="->", color="#444", lw=1.0))
ax.plot([50, 60], [-9.0, -9.0], color="#333", lw=2.0, solid_capstyle="butt")
ax.annotate("10 mm", xy=(55, -9.0), xytext=(0, -5), textcoords="offset points",
            ha="center", va="top", fontsize=7, annotation_clip=False)
ax.set_xlim(-9, 72); ax.set_ylim(-15, 43); ax.set_aspect("equal"); ax.axis("off")
plt.tight_layout(); plt.show()
Abbildung 17.7: Das winzige Handnetz — dreizehn Knoten, dreizehn Dreiecke, dieselben Nummern wie in Kapitel 10 — mit den Nummern der Dreiecke, wie sie auf der Achse des folgenden Blocks stehen. Die Knotennummern sind klein und grau daneben gesetzt. Ohne diese Zuordnung wäre „Element 7“ auf der Balkenachse eine Zahl ohne Ort. Sagittalschnitt, hinten (Ferse) links, vorn (Zehen) rechts, oben ↑; Maßstab 10 mm.
WichtigVorhersage-Punkt

Bevor du den Block ausführst: Du kannst die Weite R_mm frei setzen. Bei \(R \to 0\) sieht der Filter nur das eigene Element — das Schachbrett bleibt stehen. Was passiert, wenn du \(R\) vergrößerst: Sinkt der Kantensprung stetig gegen null, oder gibt es eine Schwelle, unter der sich gar nichts tut und über der es plötzlich glatt wird? Lege dich fest, dann drehe an R_mm.

Deutung. Ja — es gibt eine Schwelle, aber eine geometrische. Solange \(R\) kleiner ist als der Abstand zum nächsten Nachbarelement (auf diesem Handnetz 5,8 mm zwischen den unteren mittleren Dreiecken 2 und 3, siehe Abbildung 17.7), erreicht das Zeltdach kein Nachbarelement: Der Filter mittelt jeden Reiz nur mit sich selbst, und das Schachbrett bleibt mit Kantensprung 0,815 unverändert stehen — setze R_mm auf 4 oder 5, und die farbigen Balken decken sich genau mit den grauen. Sobald \(R\) diese Grenze überschreitet, greifen die Nachbarn, zuerst die eng stehenden plantaren Dreiecke: bei 6 mm fällt der Kantensprung auf 0,81, bei 8 mm auf 0,746, bei 15 mm (der voreingestellte Wert, dieselbe Weite wie die Kernschleife oben) auf 0,244, bei 30 mm auf 0,054. Weil dieses grobe Netz weiter streut als ein Streifennetz, braucht es hier größere Weiten, bis auch die abgelegenen vorderen und oberen Dreiecke erfasst sind — die 15 mm sind darum eine handnetzspezifische numerisch-didaktische Wahl, nicht die physikalische Filterskala der Buchnetze, die bei 8 mm bleibt. Oberhalb der Schwelle sinkt der Kantensprung an diesen Stützstellen weiter, je weiter \(R\) reicht — kein zweiter Sprung; eine strenge Monotonie ist das nicht (bei feiner Abtastung kann er zwischen zwei Weiten geringfügig schwanken, weil Flächengewichtung und neu erfasste Nachbarn zusammenspielen). Genau diese Weite steuert im großen Modell, ob ein Schachbrett überhaupt erst entsteht: Der Filter beugt vor, indem er benachbarte Reize schon während des Umbaus verrührt. (Er heilt ein fertiges Schachbrett trotzdem nicht — das zeigt Abbildung 17.5 an der vollen Rückkopplung, wo die Dichtegrenzen die Totzone füttern; hier siehst du nur die Glättung selbst.)

17.7 Konvergenz erkennen — und Scheinstationarität entlarven

Die Schleife läuft, aber wann ist sie fertig? Die Antwort scheint einfach: wenn sich die Dichte nicht mehr ändert. Doch „nicht mehr ändern“ hat eine Falle, die aus Kapitel 16 vertraut ist. Zuerst der gute Fall: Mit Filter läuft das Feld ruhig in einen stationären Zustand ein. Bevor wir zusehen, eine Vorhersage.

WichtigVorhersage-Punkt

Bevor du die Animation startest: Wenn das Feld mit Filter Iteration um Iteration umgebaut wird — läuft die mittlere Dichte auf einen festen Wert zu und bleibt dort, oder pendelt sie am Ende hin und her? Lege dich fest, dann spiele ab.

Deutung. Das Feld läuft ruhig ein: Aus der uniformen Startdichte verdichtet sich das Material dort, wo die Last durchläuft, und wird dünner, wo wenig ankommt — bis sich fast nichts mehr ändert. Der Lauf wird nach 474 Iterationen stationär, und zwar echt: Der Periode-2-Indikator ist null — kein Element pendelt. Damit zur Falle. „Fertig“ heißt nicht einfach „die maximale Änderung ist klein“. Erinnere Kapitel 16: Ohne Totzone kann eine Stelle mit Periode 2 zwischen zwei Werten springen — die Änderung je Schritt bleibt dann groß, ohne dass irgendetwas konvergiert. Im Feld überlebt dieser Mechanismus nur als schwaches Flackern am Totzonenrand. Deshalb trennt dieses Kapitel Abbruch und Etikett:

\[ \underbrace{\text{Abbruch}}_{\text{wie in der Vergleichsrechnung}}:\ \text{RMS-Änderung} < \varepsilon \ (\text{dreimal in Folge}), \qquad \underbrace{\text{Etikett }„\text{stationär}"}_{\text{zusätzlich}}:\ \text{Periode-2-Anteil} \approx 0. \]

Der Abbruch der Iteration erfolgt wie in der numerisch geprüften Vergleichsrechnung, wenn die flächengewichtete RMS-Änderung des ganzen Feldes dreimal in Folge unter \(10^{-5}\) g/cm³ liegt. Das Etikett „stationär“ verlangt zusätzlich, dass der Periode-2-Anteil — der Anteil der Elemente, deren Änderung von einer Iteration zur nächsten das Vorzeichen wechselt — nahe null ist; sonst heißt der Zustand scheinstationär (Periode-2-Oszillation). Und wer das Iterationsbudget ausschöpft, ohne die RMS-Schwelle zu erreichen, wird als „Budget ausgeschöpft, noch nicht stationär“ gekennzeichnet und nie als Endzustand ausgegeben — Stationarität wird nicht behauptet, sie wird belegt. Die Schrittweite \(\eta\) steuert dabei vor allem das Rechentempo — mit einer Einschränkung, die der nächste Regler beziffert.

WichtigVorhersage-Punkt

Bevor du den Regler bewegst: Eine größere Schrittweite \(\eta\) — ändert sich der Endzustand des Feldes, oder nur, wie schnell er erreicht wird? Lege dich fest, dann schiebe.

Deutung. Die Antwort: \(\eta\) beeinflusst im geprüften Bereich vor allem das Rechentempo — eine größere Schrittweite bringt das Feld mit weniger Iterationen zur Ruhe (198 statt 474). Der Endzustand ist aber nicht identisch: Die Feldabweichung gegenüber \(\eta = 0{,}08\) beträgt auf dem groben Buchnetz 1,99 % (\(\eta = 0{,}12\)) und 3,02 % (\(\eta = 0{,}16\)). In der Vergleichsrechnung streut das ganze Dichtefeld über dieselben drei Schrittweiten um 5,26 %, und die Enthese-Zone am Sporn-Ort etwas stärker als der Rest: Der eng begrenzte Reiz am Abhang reagiert empfindlicher auf den Rechenweg. Das Buch nimmt das als Modellgrenze ernst, und Kapitel 18 kommt darauf zurück, wenn es um den Ort des Sporns geht.

Der Grund für die Wegabhängigkeit steckt in den drei Bremsen, die schon das Schachbrett nicht heilen ließen: Totzone, das begrenzte \(\Delta\rho\) und die Dichtegrenzen. Wo sie greifen, kann der Weg mitbestimmen, in welchem Zustand des Bandes die Schleife stehen bleibt — die Start- und Wegabhängigkeit aus Kapitel 16. Die Schrittweite bleibt dabei ein Rechenparameter, kein biologischer: Sie sagt nichts über die Geschwindigkeit des Knochens. Eine Iteration ist kein Tag.

17.8 Die Netzlehre: was netzunabhängig ist — und was Scheinauflösung

Jetzt die härteste Lehre des Kapitels, und sie steckt in einer Zahl der numerisch geprüften Vergleichsrechnung. Wir haben gesehen: Der Filter macht das Feld glatt. Aber macht ein feineres Netz das Ergebnis genauer? Für das ungefilterte Elementfeld lautet die Antwort nein — und das ist kein Makel, sondern der Kern der Sache.

WichtigVorhersage-Punkt

Bevor du den Regler bewegst: Machst du das Netz feiner — bleibt das gefilterte Feld stabil, während das ungefilterte sich stark ändert? Oder werden beide mit dem feineren Netz „genauer“? Lege dich fest, dann schiebe.

Deutung. Die Antwort auf die Vorhersagefrage trennt die beiden Läufe qualitativ. Der ungefilterte Lauf bleibt auf jedem Buchnetz ein Schachbrett (Kantensprung grob 0,695, mittel 0,658, fein 0,556) und wird mit dem feineren Netz nur anders. Dass diese Zahl mit dem feineren Netz etwas sinkt, ist eine Falle: Sie misst nur, wie stark Nachbarelemente auseinanderliegen, und feinere Elemente liegen naturgemäß dichter beieinander — sie misst nicht, ob dasselbe Muster an derselben Stelle bleibt. Der Kantensprung belegt die Rauheit; dass das Muster räumlich mitwandert, zeigt erst die Feldabweichung der Vergleichsrechnung, nicht diese Buchnetz-Zahl. Der mit Reizfilter gerechnete Lauf bleibt auf jedem Netz qualitativ glatt; auf der 8-mm-Skala ausgewertet (W·ρ — die Größe, um die es eigentlich geht) liegt er bei 0,045 → 0,028 → 0,016, also überall im glatten Bereich. Der Regler zeigt beide Reihen nebeneinander an. Drei Trennungen sind wichtig, damit hier nichts verschwimmt:

  • Reizfilter (Algorithmus) gegen W·ρ (Auswertung). Der Umbau filtert je Iteration den Reiz (\(W\cdot S\)); die gezeigte rohe Dichte \(\rho\) ist sein Ergebnis. Die belastbare Netzunabhängigkeit misst die Vergleichsrechnung dagegen am W·ρ-ausgewerteten Feld — der Dichte, auf der 8-mm-Skala gelesen. Nur darauf beziehen sich die Zahlen.
  • Buchnetz gegen Vergleichsrechnung. Die Bilder hier sind die gröberen Buchnetze (qualitativ); die Feldabweichungen ungefiltert 40,6 % und gefiltert 3,25 % stammen aus der Vergleichsrechnung auf der bildbasierten Kontur.
  • Spongiöser Umbau gegen kortikalen Modellknochen. Die Kapitel 10 bis 15 rechnen dieselbe Ferse als einen festen, homogen kortikalen Knochen; hier wird jedes Element umgebaut. Wer Zahlen zwischen den Teilen vergleicht, sieht in Tabelle 13.3 nach, welche Rechnung sie geliefert hat.

Daraus folgt die verbindliche Lesart des Buches: Belastbar ist allein das auf der Filterskala ausgewertete Dichtefeld. Jedes Dichtebild feiner als die Filterskala (8 mm) ist Scheinauflösung — es zeigt Detail, aber kein Wissen. So löst dieses Kapitel die Zeile der Studiums-Tabelle im Vorwort ein: „nochmal rechnen, bis sich nichts mehr ändert“ ist die Fixpunktiteration, und ihre Konvergenz gilt für das gefilterte Feld, nicht für jedes einzelne Element. Und der Zustand, in dem die Schleife stehen bleibt, ist der Fixpunkt, den Kapitel 16 an der Einzelstelle vorgeführt hat — biologisch gesprochen der Knochen, der zu seiner Belastung passt und deshalb nichts mehr zu tun findet; hier zum ersten Mal für das ganze Feld auf einmal.

Was hier „Iteration“ heißt, hat Kapitel 16 schon geklärt: keine biologische Zeitspanne, sondern ein Rechenschritt — das Modell sagt wo neuer Knochen entsteht, nicht wann. Eine Grenze kommt in diesem Kapitel dazu: Der Rand des Knochens steht fest. Umgebaut wird nur im Inneren, im Dichtekontinuum; die Kontur selbst bewegt kein Modell dieses Buches.

Übungen

Ü 17.1 (Verstehen). Ein Kommilitone zeigt dir eine Dichtekarte mit einem scharfen Schachbrettmuster und sagt: „Sieh nur, die Simulation hat die einzelnen Trabekel gefunden.“ Begründe an zwei Punkten, warum die scharfe Struktur ein Artefakt und keine Trabekel sind.

Erstens die Herkunft: Das Muster entsteht aus einem rein lokalen positiven Feedback (jedes Element hört nur auf seinen eigenen Reiz), das voll und leer auf die kleinste Netzskala koppelt — ein Element voll, das nächste leer. Das ist eine Eigenschaft des Netzes und der Update-Regel, nicht des Knochens. Zweitens die Netzabhängigkeit: Rechnet man dasselbe Modell auf einem feineren Netz, ändert sich das Schachbrett vollständig (ungefiltert 40,6 % Feldabweichung) — echte Struktur müsste sich beim Verfeinern stabilisieren, nicht neu würfeln. Zusatz: Das Buch modelliert Knochen als Dichtekontinuum, nicht als einzelne Bälkchen mit Löchern; ein Schachbrett ist schon deshalb keine Trabekelstruktur. Belastbar ist nur das auf der Filterskala (8 mm) geglättete Feld.

Ü 17.2 (Verändern). Öffne den Filterradius-Regler und stelle \(R = 0\). Lies den Kantensprung ab. Sage voraus, um welchen Faktor er fällt, wenn du auf \(R = 8\) mm stellst, und prüfe es.

Bei \(R = 0\) zeigt der Regler das volle Schachbrett mit einem Kantensprung von 0,695. Bei \(R = 8\) mm fällt er auf 0,065 — ein Faktor von rund 11. Der nichtlokale Filter mittelt jedes Element mit seinen Nachbarn im festen 8-mm-Umkreis; zwei benachbarte Elemente mitteln fast dieselbe Umgebung und können nicht mehr gegeneinander springen. Wichtig: Ein sehr kleiner Radius (der Regler zeigt es bei \(R = 1\) mm, Kantensprung noch 0,684) hilft nicht — der Filter braucht eine feste, hinreichend große physische Reichweite, sonst bleibt das Ergebnis netzabhängig. Achte dabei auf das mitlaufende \(R/h\): Es kommt erst ab \(R = 6\) mm über die Schranke 3,5, obwohl der Kantensprung schon bei \(R = 2\) mm klein aussieht.

Ü 17.3 (Übertragen). Erkläre, warum ein sehr kleiner Filterradius das Ergebnis wieder netzabhängig macht — und warum das selbst Lehrstoff ist und keine Panne.

Der Filter stellt die Netzunabhängigkeit nur her, wenn seine Reichweite \(R\) groß gegen die Elementgröße \(h\) ist — wenn also das Verhältnis \(R/h\) hinreichend groß ist und im Umkreis \(R\) viele Elemente (Stützstellen) liegen. Ist \(R\) so klein, dass \(R/h\) nahe eins liegt, erfasst das Zeltdach nur eine Handvoll Nachbarn; die Regularisierungsskala liegt dann zu nahe an der Netzskala, sodass die Glättung selbst vom Netz abhängt und das Schachbrett zurückkehrt (im Regler bei \(R = 1\) mm sichtbar, \(R/h\) gleich 0,6, kaum über einem Schwerpunktabstand). Der Regler zeigt zugleich, warum das Auge hier nicht reicht: Bei \(R = 2\) mm ist der Kantensprung mit 0,141 schon klein, \(R/h\) aber erst 1,3 — glatt und netzunabhängig sind zweierlei. Die Vergleichsrechnung hält deshalb auf allen Netzstufen mindestens 3,5 ein. Das ist Lehrstoff, weil es die Rolle des Filters offenlegt: Er ist kein kosmetischer Weichzeichner, sondern er definiert die physikalisch belastbare Skala des Modells. Ein Ergebnis unterhalb dieser Skala hat keine Bedeutung — es ist Scheinauflösung. Deshalb ist der Filterradius ein Modellparameter mit physischer Bedeutung (8 mm), keine freie Stellschraube.

Roter Faden

Wo kam das schon vor, wo kommt es wieder? Zurück: Der Löser aus Kapitel 5 steckt in jeder Iteration; die dichteabhängige Steifigkeit (Morgan-Gesetz) aus Kapitel 6 liefert den E-Modul; die Formänderungsenergie hinter dem Reiz \(S = U/\rho\) und der Mechanostat mit seiner Totzone stammen aus Kapitel 16; und das Misstrauen gegen ein plausibles buntes Bild, das netzabhängig falsch ist, ist die Fortsetzung von Kapitel 13 — dort die Lagerkantensingularität, hier das Schachbrett. Vor: Kapitel 18 setzt die jetzt geschlossene Schleife auf die reale Frage an — erzeugt Zug oder Druck den Sporn? —, und die Scheinauflösungs-Lehre schützt dort die Deutung der Dichtefelder: Man liest die geglättete Struktur, nicht das einzelne Element.

Was dieses Kapitel NICHT tut

Es zeigt keine echten Trabekel und keine Löcher: Knochen ist hier ein Dichtekontinuum, ein stetiges Dichtefeld zwischen 0,2 und 1,0 g/cm³; ein Schachbrett ist keine Trabekelstruktur, sondern ein Artefakt. Es kennt keine Zellbiologie (Osteoblasten, Osteoklasten), keinen Stoffwechsel, keine Durchblutung — nur die Rückkopplung aus Reiz und Dichte. Es hat keine biologische Umbauzeit: Eine Iteration ist kein Tag. Es ist keine Topologieoptimierung — die Technik des Filters ist von dort geliehen, aber das Modell optimiert nichts, es folgt einer biologischen Regel. Und es lässt keinen Rand wandern: Es baut nur im Inneren um; der wandernde Rand (die Ossifikationsfront) käme erst mit einem Modell, das die Kontur selbst bewegt — das leistet dieses Buch nicht. Und es macht keine klinische Aussage: Die Buchnetz-Zahlen dieses Kapitels (Sollwert \(S_0 \approx 8\,9\) J/kg, die Kantensprünge, der stationäre Dichteschnitt) sind Modellwerte einer bewusst gröberen Rechnung; die belastbaren Feldabweichungen stammen aus der numerisch geprüften Vergleichsrechnung und werden hier zitiert, nicht neu erzeugt.