1  Felder sehen

Schau auf eine Wetterkarte: An jedem Ort des Landes steht eine Temperatur — und an jedem Ort weht ein Wind mit Richtung und Stärke. Beides sind Felder: Die Temperaturkarte ordnet jedem Ort eine Zahl zu, die Windkarte jedem Ort einen Pfeil. Mehr ist ein Feld nicht.

Das Verblüffende: Das elektrische Feld, mit dem dieses ganze Buch arbeitet, ist genau dasselbe — ein Pfeil an jedem Ort des Raums. Wer eine Windkarte lesen kann, hat das schwerste Konzept der Elektrodynamik schon verstanden. In diesem Kapitel machen wir Felder zu dem, was sie für uns als Programmierer sind: NumPy-Arrays, die man zeichnen kann.

Lernziele

Nach diesem Kapitel kannst du …

  1. … erklären, was ein Skalarfeld und ein Vektorfeld ist — und beide als NumPy-Arrays anlegen,
  2. … Felder sichtbar machen (imshow/contour für Zahlen, quiver und streamplot für Pfeile) und begründen, welche Darstellung wann passt,
  3. … das Skalarprodukt geometrisch deuten („wie viel zeigt \(\vec{a}\) in Richtung \(\vec{b}\)?“) und numerisch nachprüfen,
  4. … das Kreuzprodukt geometrisch deuten (senkrechter Pfeil, Rechte-Hand-Regel, Parallelogrammfläche) und mit np.cross berechnen,
  5. … das Feldbild einer Punktladung und eines Dipols vorhersagen und selbst berechnen.

1.1 Ein Feld ist eine Zahl an jedem Ort

Ein Skalarfeld ordnet jedem Punkt \((x, y)\) eine Zahl zu — etwa eine Temperatur \(T(x, y)\). Für den Computer heißt das: Wir legen ein Gitter aus Punkten über die Fläche und speichern an jedem Gitterpunkt einen Wert. Genau das leistet np.meshgrid plus ein 2D-Array:

import numpy as np
import matplotlib.pyplot as plt

# Gitter: 200 × 200 Punkte auf dem Quadrat [-2, 2] × [-2, 2].
# np.linspace(a, b, n): n gleichmäßig verteilte Werte von a bis b.
# np.meshgrid: spannt aus zwei 1D-Achsen das 2D-Gitter auf — x[i, j]
# und y[i, j] sind die Koordinaten des Gitterpunkts in Zeile i, Spalte j
x, y = np.meshgrid(np.linspace(-2, 2, 200), np.linspace(-2, 2, 200))

# Ein erfundenes Temperaturfeld: warme Insel links, kalte Senke rechts.
# np.exp wirkt — wie alle NumPy-Funktionen — elementweise auf das ganze
# Array: eine Zeile Code, 40 000 Rechnungen
T = 20 + 6*np.exp(-((x+1)**2 + y**2)) - 4*np.exp(-((x-1)**2 + (y-0.5)**2))

fig, ax = plt.subplots(figsize=(5.6, 4.4))
bild = ax.imshow(T, extent=(-2, 2, -2, 2), origin="lower", cmap="coolwarm")
linien = ax.contour(x, y, T, colors="k", linewidths=0.5)
ax.clabel(linien, fontsize=7, fmt="%.0f °C")
fig.colorbar(bild, label="Temperatur (°C)")
ax.set_xlabel("x"); ax.set_ylabel("y")
ax.set_title("Skalarfeld T(x, y)")
plt.tight_layout(); plt.show()
Abbildung 1.1: Ein Skalarfeld: an jedem Ort eine Zahl. Farbe = Temperatur, Linien = Orte gleicher Temperatur (wie Höhenlinien auf der Wanderkarte).

Das Array T ist das Feld — eine Tabelle mit einer Zahl pro Gitterpunkt. Zwei Darstellungen auf einen Blick: Die Farbe zeigt den Wert direkt, die Konturlinien verbinden Orte gleichen Werts. Auf solchen Linien wirst du im ganzen Buch herumlaufen — sie heißen später nur anders (Äquipotentiallinien, Wellenfronten).

1.2 Ein Feld kann auch ein Pfeil sein

Ein Vektorfeld ordnet jedem Punkt einen Pfeil zu: Richtung und Länge. Numerisch sind das einfach zwei Arrays — eines für die x-Komponente des Pfeils, eines für die y-Komponente.

WichtigVorhersage-Punkt

Bevor du weiterliest: Wir zeichnen gleich das Vektorfeld mit den Komponenten \(u = -y\) und \(v = x\), d. h. der Pfeil am Ort \((x, y)\) ist \((-y, x)\). Nimm ein Blatt Papier und trage die Pfeile an den vier Punkten \((1,0)\), \((0,1)\), \((-1,0)\), \((0,-1)\) ein. Was für ein Strömungsmuster entsteht? Lege dich fest.

x, y = np.meshgrid(np.linspace(-2, 2, 15), np.linspace(-2, 2, 15))
u = -y      # x-Komponente des Pfeils an jedem Gitterpunkt
v = x       # y-Komponente

fig, ax = plt.subplots(figsize=(4.6, 4.4))
# np.hypot(u, v): elementweise sqrt(u² + v²) — hier die Pfeillänge,
# die als fünftes Argument die Farbe der Pfeile steuert
ax.quiver(x, y, u, v, np.hypot(u, v), cmap="viridis")
ax.set_xlabel("x"); ax.set_ylabel("y")
ax.set_title("Vektorfeld (u, v) = (−y, x)")
ax.set_aspect("equal")
plt.tight_layout(); plt.show()
Abbildung 1.2: Ein Vektorfeld: an jedem Ort ein Pfeil. Das Feld (−y, x) ist ein Wirbel gegen den Uhrzeigersinn; außen sind die Pfeile länger.

Ein Wirbel: Am Punkt \((1, 0)\) zeigt der Pfeil nach oben \((0, 1)\), am Punkt \((0, 1)\) nach links \((-1, 0)\) — die Strömung kreist gegen den Uhrzeigersinn, außen schneller als innen. Wenn deine Skizze das zeigt: sehr gut. Wenn nicht: Geh die vier Punkte noch einmal durch — der häufigste Stolperstein ist, \((-y, x)\) am Ort einzusetzen, statt es als festen Pfeil zu lesen.

quiver („Köcher”) zeichnet die rohen Pfeile. Das ist die ehrlichste Darstellung eines Vektorfelds — und unser Arbeitspferd, bis die Pfeile so unterschiedlich lang werden, dass das Bild unleserlich wird. Dann kommt streamplot ins Spiel; das siehst du unten beim Dipol.

1.3 Werkzeug: das Skalarprodukt

Bevor wir echte elektrische Felder bauen, brauchen wir zwei Handgriffe für Pfeile. Der erste beantwortet die Frage: „Wie viel von \(\vec{a}\) zeigt in die Richtung von \(\vec{b}\)?“ Das ist das Skalarprodukt — zwei Pfeile rein, eine Zahl raus:

\[ \vec{a} \cdot \vec{b} \;=\; a_x b_x + a_y b_y \;=\; |\vec{a}|\,|\vec{b}|\,\cos\alpha . \]

Die erste Form ist die Rechenvorschrift (Komponenten multiplizieren, aufsummieren), die zweite die Bedeutung: Länge von \(\vec{a}\) mal Länge von \(\vec{b}\) mal „wie parallel sind sie” (\(\cos\alpha\), mit dem Winkel \(\alpha\) zwischen den Pfeilen). Beide Formen liefern dieselbe Zahl — das prüfen wir nach, statt es zu glauben:

a = np.array([3.0, 1.0])        # np.array: macht aus einer Liste einen Vektor
b = np.array([1.0, 2.0])

links  = a @ b                  # der @-Operator: das Skalarprodukt
laenge_a, laenge_b = np.linalg.norm(a), np.linalg.norm(b)   # norm: Länge |a|
alpha  = np.arccos(links / (laenge_a * laenge_b))   # arccos: Kosinus rückwärts
rechts = laenge_a * laenge_b * np.cos(alpha)        # geometrische Form

print(f"Komponentenform:  a·b = {links:.4f}")
# np.degrees: Bogenmaß → Grad (np.radians ginge den umgekehrten Weg)
print(f"geometrische Form:      {rechts:.4f}   (Winkel α = {np.degrees(alpha):.1f}°)")
Komponentenform:  a·b = 5.0000
geometrische Form:      5.0000   (Winkel α = 45.0°)

Drei Fälle solltest du im Schlaf erkennen: Pfeile parallel → Skalarprodukt maximal positiv. Pfeile senkrecht → exakt null (\(\cos 90° = 0\)). Pfeile entgegengesetzt → negativ. Dieses „null bei senkrecht” wird unser meistbenutzter Test: Ob ein Feld durch eine Fläche hindurch zeigt oder nur an ihr entlangstreicht, entscheidet in Kapitel 3 ein Skalarprodukt.

1.4 Werkzeug: das Kreuzprodukt

Der zweite Handgriff beantwortet eine andere Frage: „Welcher Pfeil steht senkrecht auf \(\vec{a}\) und \(\vec{b}\)?“ Das Kreuzprodukt \(\vec{a} \times \vec{b}\) liefert genau diesen Pfeil — zwei Pfeile rein, ein Pfeil raus. Seine Länge ist die Fläche des von \(\vec{a}\) und \(\vec{b}\) aufgespannten Parallelogramms, \(|\vec{a} \times \vec{b}| = |\vec{a}||\vec{b}|\sin\alpha\), und von den zwei möglichen senkrechten Richtungen gilt die Rechte-Hand-Regel: Zeigt der Daumen entlang \(\vec{a}\) und der Zeigefinger entlang \(\vec{b}\), dann zeigt der Mittelfinger entlang \(\vec{a} \times \vec{b}\).

a = np.array([2.0, 0.0, 0.0])     # entlang x
b = np.array([1.0, 1.0, 0.0])     # schräg in der xy-Ebene

c = np.cross(a, b)              # np.cross: Kreuzprodukt zweier 3er-Vektoren
print(f"a × b = {c}")
print(f"senkrecht auf a? a·c = {a @ c}    senkrecht auf b? b·c = {b @ c}")

flaeche_geometrisch = (np.linalg.norm(a) * np.linalg.norm(b)
                       * np.sin(np.arccos(a @ b / (np.linalg.norm(a) * np.linalg.norm(b)))))
print(f"|a × b| = {np.linalg.norm(c):.4f}   "
      f"Parallelogrammfläche = {flaeche_geometrisch:.4f}")
a × b = [0. 0. 2.]
senkrecht auf a? a·c = 0.0    senkrecht auf b? b·c = 0.0
|a × b| = 2.0000   Parallelogrammfläche = 2.0000

Beide Skalarprodukte sind null — \(\vec{c}\) steht tatsächlich senkrecht auf beiden —, und die Länge stimmt mit der Parallelogrammfläche überein. Beachte: Für das Kreuzprodukt mussten die Pfeile dreidimensional werden (dritte Komponente 0); das Ergebnis \((0, 0, 2)\) ragt aus der Papierebene heraus.

Warum dieses Werkzeug? Ein Vorgriff: Die Richtung, in die eine elektromagnetische Welle ihre Energie trägt, ist \(\vec{S} = \vec{E} \times \vec{H}\) — ein Kreuzprodukt aus elektrischem und magnetischem Feld. Wenn dir in Kapitel 18 jemand \(\vec{E}\) und \(\vec{H}\) zeigt, kannst du mit der rechten Hand ablesen, wohin das Licht fließt.

1.5 Das Feld einer Punktladung

Jetzt bauen wir unser erstes physikalisches Feld. Eine Punktladung \(q\) im Ursprung erzeugt das elektrische Feld

\[ \vec{E}(\vec{r}) \;=\; \underbrace{\frac{q}{4\pi\varepsilon_0}}_{\text{Stärke}} \; \frac{\hat{r}}{r^2} \qquad\text{mit } \hat{r} = \frac{\vec{r}}{r},\; r = |\vec{r}| . \]

In Worten: Der Feldpfeil zeigt vom Ort der Ladung weg (Richtung \(\hat{r}\)) und wird mit dem Quadrat des Abstands schwächer (\(1/r^2\)). Den konstanten Vorfaktor \(q/(4\pi\varepsilon_0)\) setzen wir beim Zeichnen auf 1 — er skaliert nur alle Pfeile gemeinsam (mehr dazu im Kleingedruckten).

WichtigVorhersage-Punkt

Bevor du weiterliest: Wie sieht das Pfeilbild einer positiven Punktladung aus — und woran erkennst du im Bild das \(1/r^2\)? Zwei Sätze, dann weiterlesen.

def punktladungsfeld(q, ort, x, y):
    """E = q · r̂ / r² einer Punktladung am gegebenen Ort (Vorfaktor = 1)."""
    dx, dy = x - ort[0], y - ort[1]
    # np.maximum: elementweises Maximum zweier Arrays/Werte — hebt r
    # überall auf mindestens 1e-9, damit nie durch 0 geteilt wird
    r = np.maximum(np.hypot(dx, dy), 1e-9)
    return q * dx / r**3, q * dy / r**3      # q·r/|r|³  =  q·r̂/r²

# 20 Punkte je Achse: so liegt kein Gitterpunkt exakt auf der Ladung
x, y = np.meshgrid(np.linspace(-2, 2, 20), np.linspace(-2, 2, 20))
ex, ey = punktladungsfeld(+1.0, (0, 0), x, y)
staerke = np.hypot(ex, ey)

fig, ax = plt.subplots(figsize=(5.2, 4.4))
# np.log10: Zehnerlogarithmus (elementweise) — staucht den riesigen
# Wertebereich der Feldstärke auf eine lesbare Farbskala
pfeile = ax.quiver(x, y, ex/staerke, ey/staerke, np.log10(staerke),
                   cmap="viridis", pivot="mid", scale=30)
ax.plot(0, 0, "ro", markersize=10)
fig.colorbar(pfeile, label="log₁₀ |E|  (willk. Einh.)")
ax.set_xlabel("x"); ax.set_ylabel("y")
ax.set_title("Punktladung: E zeigt radial nach außen")
ax.set_aspect("equal")
plt.tight_layout(); plt.show()
Abbildung 1.3: Das Feld einer positiven Punktladung: alle Pfeile zeigen radial nach außen. Die Pfeile sind auf Einheitslänge gebracht; die Stärke |E| steckt in der Farbe (logarithmisch — von innen nach außen fällt sie um Größenordnungen).

Radiale Pfeile — und das \(1/r^2\) steckt in der Farbe: In doppelter Entfernung ist das Feld viermal schwächer. Hier siehst du auch den ersten ehrlichen Visualisierungs-Trick: Die Pfeile sind alle gleich lang gezeichnet (ex/staerke), sonst würden die riesigen Pfeile nahe der Ladung das Bild erschlagen. Richtung und Stärke sind im Bild getrennt — Pfeil und Farbe.

1.6 Das Kapitel-Programm: der Dipol

Was passiert mit zwei Ladungen? Die Antwort ist das vielleicht wichtigste Prinzip dieses Buchs, und es ist erfreulich schlicht: Felder addieren sich. An jedem Ort ist das Gesamtfeld die Pfeilsumme der Einzelfelder (Superposition). Eine positive und eine negative Ladung nebeneinander — ein Dipol — sind damit zwei Funktionsaufrufe und ein Plus:

WichtigVorhersage-Punkt

Bevor du weiterliest: Skizziere das Linienbild: \(+q\) links, \(-q\) rechts. Wo starten die Linien, wo enden sie, wie laufen sie dazwischen? Und: In welche Richtung zeigt das Feld auf der Mittelsenkrechten (der senkrechten Linie genau zwischen beiden Ladungen)?

# von oben: punktladungsfeld()   (Abschnitt „Das Feld einer Punktladung")
x, y = np.meshgrid(np.linspace(-2, 2, 200), np.linspace(-2, 2, 200))

ex1, ey1 = punktladungsfeld(+1.0, (-0.5, 0.0), x, y)
ex2, ey2 = punktladungsfeld(-1.0, (+0.5, 0.0), x, y)
ex, ey = ex1 + ex2, ey1 + ey2          # Superposition: Felder addieren sich

fig, ax = plt.subplots(figsize=(5.6, 4.6))
strom = ax.streamplot(x, y, ex, ey, color=np.log10(np.hypot(ex, ey)),
                      cmap="viridis", density=1.4, linewidth=0.9)
ax.plot(-0.5, 0, "ro", markersize=10); ax.annotate("+q", (-0.55, 0.12), color="red")
ax.plot(+0.5, 0, "bo", markersize=10); ax.annotate("−q", (0.45, 0.12), color="blue")
fig.colorbar(strom.lines, label="log₁₀ |E|  (willk. Einh.)")
ax.set_xlabel("x"); ax.set_ylabel("y")
ax.set_title("Dipol: Superposition zweier Punktladungsfelder")
ax.set_aspect("equal")
plt.tight_layout(); plt.show()
Abbildung 1.4: Der Dipol: Feldlinien laufen von +q (rot) zu −q (blau). Farbe = log₁₀|E|. Auf der Mittelsenkrechten zeigt das Feld exakt in +x-Richtung — von der positiven zur negativen Seite.

streamplot zeichnet Feldlinien: Kurven, die an jedem Punkt der dortigen Pfeilrichtung folgen. Sie quellen aus \(+q\), münden in \(-q\), und auf der Mittelsenkrechten laufen sie waagerecht von links nach rechts — dort heben sich die y-Anteile beider Ladungen aus Symmetriegründen exakt auf. Das volle Kapitel-Programm programme/kap01/kap01_dipolfeld.py zeichnet beide Bilder und belegt diese Symmetrie numerisch: Es wertet das Feld auf der Mittelsenkrechten aus und zeigt, dass die y-Komponente auf Maschinengenauigkeit null ist. Führe es aus und lies den Beleg.

WarnungNaheliegende Vermutung

Vermutung: „Feldlinien existieren wirklich — zwischen zwei Linien ist kein Feld.”

Warum sie naheliegt: Jede Abbildung zeigt einzelne, sauber getrennte Linien mit leerem Raum dazwischen. Eisenfeilspäne am Magneten ordnen sich sogar sichtbar zu Ketten — das sieht nach realen Fäden aus.

Was stattdessen stimmt: Das Feld füllt den Raum lückenlos; an jedem Punkt steht ein Pfeil. Feldlinien sind ein Zeichenwerkzeug: streamplot wählt willkürliche Startpunkte und folgt von dort den Pfeilen — mit dem Parameter density kannst du beliebig viele Linien dazwischenlegen, ohne dass sich physikalisch irgendetwas ändert. (Die Feilspäne-Ketten entstehen, weil magnetisierte Späne einander anziehen — der Magnet allein hätte sie nie zu Linien sortiert.)

Beides kannst du in dieser HTML-Fassung sofort selbst prüfen: Die folgende Zelle ist ein Ladungs-Baukasten — editierbar und per Run-Knopf (oder Strg+Enter) direkt im Browser ausführbar, ohne Installation. Drei Aufträge: Mach aus dem Dipol zwei positive Ladungen (wohin laufen die Linien dann?), baue einen Quadrupol (vier Ladungen mit wechselndem Vorzeichen an den Ecken eines Quadrats — skizziere das Linienbild vorher!), und drehe dichte auf 3: mehr Linien, gleiche Physik — der lebende Beweis für den Kasten oben.

TippMerkkasten
  • Ein Skalarfeld ist eine Zahl an jedem Ort, ein Vektorfeld ein Pfeil an jedem Ort — numerisch: ein bzw. zwei (drei) Arrays auf einem Gitter.
  • Skalarprodukt \(\vec{a}\cdot\vec{b}\): eine Zahl — „wie viel von \(\vec{a}\) zeigt entlang \(\vec{b}\)?“; null bei senkrecht.
  • Kreuzprodukt \(\vec{a}\times\vec{b}\): ein Pfeil — senkrecht auf beiden (Rechte-Hand-Regel), Länge = Parallelogrammfläche.
  • Punktladung: \(\vec{E} \propto \hat{r}/r^2\), radial nach außen. Felder addieren sich (Superposition) — der Dipol ist nur eine Summe.
  • Feldlinien sind ein Zeichenwerkzeug; das Feld selbst füllt den Raum lückenlos.

Roter Faden

Alles Weitere im Buch steht auf diesem Kapitel: In Kapitel 2 fragen wir, wie sich Felder von Ort zu Ort ändern — aus Nachbarpfeil minus Pfeil werden Ableitungen, Quellen und Wirbel. In Kapitel 3 sagen die Maxwell-Gleichungen, welche Quellen und Wirbel die Felder \(\vec{E}\) und \(\vec{B}\) tatsächlich haben — das Skalarprodukt von heute misst dort den Fluss durch Flächen. Und das Kreuzprodukt trägt ab Kapitel 18 als \(\vec{S} = \vec{E}\times\vec{H}\) die Energie jeder Welle durch dieses Buch.

Übungen

Ü 1.1 (Verstehen). Das Skalarprodukt zweier Pfeile ist negativ. Was sagt das über den Winkel zwischen ihnen — und welche Bedeutung hat das Vorzeichen, wenn \(\vec{a}\) eine Kraft und \(\vec{b}\) ein Weg ist?

Negativ heißt \(\cos\alpha < 0\), also \(\alpha > 90°\) — die Pfeile zeigen „eher gegeneinander”. Bei Kraft mal Weg ist das Skalarprodukt die verrichtete Arbeit: negativ bedeutet, die Kraft bremst die Bewegung, statt sie anzutreiben (z. B. Reibung).

Ü 1.2 (Verstehen). Im Punktladungsbild sind alle Pfeile gleich lang gezeichnet. Woran liest man die Feldstärke ab — und warum wäre das Bild mit „ehrlichen” Pfeillängen unbrauchbar?

Die Stärke steckt in der Farbe (logarithmisch). Mit echten Längen wäre der Pfeil direkt neben der Ladung wegen \(1/r^2\) hunderte Male länger als am Rand — er würde das ganze Bild überdecken, während die äußeren Pfeile zu unsichtbaren Punkten schrumpfen.

Ü 1.3 (Verändern). Ersetze im Kapitel-Programm die negative Ladung durch eine zweite positive (\(-1.0 \to +1.0\)). Sage vorher: Wie ändert sich das Linienbild, und was passiert mit dem Feld genau in der Mitte zwischen den Ladungen? Dann ausführen und prüfen.

Die Linien laufen jetzt aus beiden Ladungen heraus und weichen einander aus; die Mittelsenkrechte wird zur „Wand”, die keine Linie kreuzt. Genau in der Mitte heben sich die beiden Felder vollständig auf: \(\vec{E} = 0\) — ein Gleichgewichtspunkt. Die geänderte Zeile und das Ergebnis:

# von oben: punktladungsfeld(), x, y   (Dipol-Abschnitt)
ex1, ey1 = punktladungsfeld(+1.0, (-0.5, 0.0), x, y)
ex2, ey2 = punktladungsfeld(+1.0, (+0.5, 0.0), x, y)   # war: -1.0
ex, ey = ex1 + ex2, ey1 + ey2

fig, ax = plt.subplots(figsize=(5.0, 4.2))
ax.streamplot(x, y, ex, ey, color=np.log10(np.hypot(ex, ey)),
              cmap="viridis", density=1.4, linewidth=0.9)
ax.plot([-0.5, 0.5], [0, 0], "ro", markersize=10)
ax.set_xlabel("x"); ax.set_ylabel("y")
ax.set_title("Zwei gleiche Ladungen: die Mitte wird feldfrei")
ax.set_aspect("equal"); plt.tight_layout(); plt.show()

e1 = punktladungsfeld(+1.0, (-0.5, 0.0), np.array(0.0), np.array(0.0))
e2 = punktladungsfeld(+1.0, (+0.5, 0.0), np.array(0.0), np.array(0.0))
print(f"E in der Mitte: ({e1[0]+e2[0]:.1f}, {e1[1]+e2[1]:.1f})")

E in der Mitte: (0.0, 0.0)

Der numerische Symmetrie-Beleg im Kapitel-Programm schlägt jetzt übrigens an, denn auf der Mittelsenkrechten verschwindet nicht mehr nur die y-Komponente, sondern auch die x-Komponente — genau das zeigt der Mittelpunkts-Check.

Ü 1.4 (Übertragen). Baue aus dem Kapitel-Programm einen Quadrupol: vier Ladungen \(+q, -q, +q, -q\) auf den Ecken eines Quadrats (abwechselnd). Zeichne das Linienbild. Wo ist das Feld null?

Vier Aufrufe von punktladungsfeld und drei Pluszeichen — mehr braucht es dank Superposition nicht:

# von oben: punktladungsfeld(), x, y   (Dipol-Abschnitt)
LADUNGEN = [(+1.0, (-0.5, -0.5)), (-1.0, (+0.5, -0.5)),
            (+1.0, (+0.5, +0.5)), (-1.0, (-0.5, +0.5))]

ex = sum(punktladungsfeld(q, ort, x, y)[0] for q, ort in LADUNGEN)
ey = sum(punktladungsfeld(q, ort, x, y)[1] for q, ort in LADUNGEN)

fig, ax = plt.subplots(figsize=(5.0, 4.2))
ax.streamplot(x, y, ex, ey, color=np.log10(np.hypot(ex, ey)),
              cmap="viridis", density=1.4, linewidth=0.9)
for q, (qx, qy) in LADUNGEN:
    ax.plot(qx, qy, "o", color="red" if q > 0 else "blue", markersize=10)
ax.set_xlabel("x"); ax.set_ylabel("y")
ax.set_title("Quadrupol: vier Blütenblätter, Zentrum feldfrei")
ax.set_aspect("equal"); plt.tight_layout(); plt.show()

mitte = [punktladungsfeld(q, ort, np.array(0.0), np.array(0.0))
         for q, ort in LADUNGEN]
print(f"E im Zentrum: ({sum(m[0] for m in mitte):.1f},"
      f" {sum(m[1] for m in mitte):.1f})")

E im Zentrum: (0.0, 0.0)

Im Zentrum heben sich aus Symmetriegründen alle vier Beiträge auf: \(\vec{E} = 0\). Zusatzbeobachtung für später: Weit weg fällt das Quadrupolfeld noch schneller ab als das Dipolfeld — Stichwort Multipolentwicklung.

Das Kleingedruckte

  • Willkürliche Einheiten: Der Vorfaktor \(q/(4\pi\varepsilon_0)\) skaliert nur alle Pfeile gemeinsam, deshalb durften wir ihn beim Zeichnen auf 1 setzen. Sobald Zahlenwerte zählen (Kräfte, Energien, ab Kapitel 3), rechnen wir in SI-Einheiten mit echtem \(\varepsilon_0\).
  • 2D ist ein Schnitt: Unsere Bilder zeigen die Ebene \(z = 0\) eines dreidimensionalen Feldes. Für die Punktladung mit \(1/r^2\) ist das der ehrliche Schnitt durchs Zentrum; ein „echtes 2D-Universum” (Linienladung) hätte \(1/r\).
  • Liniendichte mit Vorsicht: In 3D-Skizzen kodiert die Dichte der Feldlinien die Feldstärke. streamplot in der Ebene tut das nicht zuverlässig — darum tragen unsere Linien die Stärke als Farbe.