Torus Netz Plot mit Matplotlib
English version below.
Ein Torus ist ein geometrischer Rotationskörper, der einem Donut ähnelt. Dieses Objekt entsteht durch das „Rollen“ einer Fläche um eine Achse und dem anschließenden „Verkleben“ der freien Enden. Eine beispielhafte Anwendung von Tori (plural von Torus) in der Technik findet sich in der Form des erzeugten Magnetfeldes eines Tokamak Fusionsreaktors. Das Plasma, welches in dieser Torusform eingeschlossen ist, kann somit auf über 100 Millionen °C erhitzt werden, ohne dass die Reaktorwände schaden nehmen. In dieser Anleitung soll die Geometrie von Tori anhand eines über den wulstartigen Körper gelegten Netzes verdeutlicht werden. Diese Visualisierung geschieht mittels der Bibliothek Matplotlib in der Programmiersprache Python 3.
Dieser Plot auf sämtlichen Produkten bei redbubble | ||
---|---|---|
![]() |
![]() |
![]() |
Implementierung in Python 3 |
![]() |
Zunächst werden alle erforderlichen Bibliotheken importiert
import numpy as np import matplotlib.pyplot as plt import matplotlib as mpl from mpl_toolkits.mplot3d import Axes3D
Damit die Plots direkt im Jupyter Notebook ausgegeben werden können, muss Matplotlib über einen so genannten Built-in Magic Command aktiviet werden:
%matplotlib inline
Anschließend erstellen wir einen Container fig
, in welchem die Plots deponiert werden. In unserem Fall ist es nur ein Plot. Als Parameter wird das Seitenverhältnis 1:1 mit figsize=plt.figaspect(1)
und desweiteren eine Auflösung von 700 dpi (dpi=700
) übergeben.
fig = plt.figure(figsize=plt.figaspect(1), dpi=700)
Torus Koordinaten |
![]() |
Ein Torus ist hat die Form eines Reifens bzw. Donuts. Dafür ist es zweckmäßig ein eigenes Koordinatensystem zu definieren (siehe untere Abbildung), ähnlich wie ein Sphärenkoordinatensystem für Kugeln. Dabei liegt der Koordinatenursprung in der Mitte des Torus. Der Radius R
reicht von dort aus bis in die Mitte der „Wulst“, welche ihrerseits durch den Radius r
definiert ist. Mit dem Winkel φ
(Phi) wird die Ausprägung des Radius R
gegeben (bei φ=2π
wird die Wulst um einen vollen Kreisring abgebildet). Der Winkel ϑ
(Theta) gibt an, um welches Ausmaß die Wulst mit dem Radius r
gebildet ist.

φ
(Phi) und ϑ
(Theta) sind in Bogenmaß (rad
) gegeben. Ein Winkel von 2π
entspricht dabei 360°
. Die Umrechnung im Allgemeinen lautet:
Bogenmaß → Gradmaß
Gradmaß → Bogenmaß
Nun werden die Koordinatenpunkte generiert, über welche folgend das „Wireframe“ gelegt wird. Diese ergeben sich aus den Winkeln φ
(Phi) und ϑ
(Theta) und werden jeweils um einen Vollkreis gespannt. Dies erreichen wir mit der Funktion linspace()
der Bibliothek numpy. Als Parameter übergeben wird die Startkoordinate 0
; gefolgt von der Endkoordinate 2π
(verwendet wird Pi der numpy-Bibliothek), Mit endpoint=True
stellen wir sicher, dass sich jeweils ein Koordinatenpunkt bei 0
und bei 2π
befindet. Schließlich wird in dieser Funktion die Anzahl der Unterteilungen mit num=75
übergeben.
phi = np.linspace(0, 2 * np.pi, endpoint=True, num=75) theta = np.linspace(0, 2 * np.pi, endpoint=True, num=75)
Bis jetzt existieren jedoch nur zwei Kreise. Einmal der für φ
(in der Graik pink dargestellt) und ϑ
(in der Graik rot dargestellt). Somit werden alle weiteren Koordinaten benötigt, die entstehen, wenn der rote Kreis entlang von φ
um den pinken „fährt“.

meshgrid
.
phi, theta = np.meshgrid(phi, theta)
Für die Radien R
und r
erstellen wir nun jeweils eine Variable und weisen numerische Werte hinzu:
R=1.5 r=1
Da Matplotlib keine Toruskoordinaten beherrscht, müssen diese in kartesische (standard) Koordinaten umgerechnet werden:
x = np.cos(theta) * (R + r * np.cos(phi)) y = np.sin(theta) * (R + r * np.cos(phi)) z = r * np.sin(phi)
Zurück zu Matplotlib: Für die Darstellung von Text (hier nur der Titel des Plots) ändern wir diese vom Standard zu der Schriftart der Auszeichnungssprache LaTeX, welche auch auf dieser Website für die Darstellung von Gleichungen genutzt wird.
plt.rc('text', usetex=True) plt.rc('font', family='serif')
Erstellung des Plots |
![]() |
Plots werden als subplot
dem Container (figure) zugeordnet. Dabei erhalten die Subplots eine Nummer in Form eines Zahlentupels. Der Tupel besteht aus den Elementen (zeile, spalte, plot)
. Dabei ergibt sich das letzte Element, also die Nummer des Plots, aus, wie in einem Dokument gelesenen Reihenfolge.

Dem Container (figrue) wird nun ein Plot an der Stelle 1,1,1 hinzugefügt.
ax = fig.add_subplot(1, 1, 1, projection='3d') # 1 zeile, 1 spalte, plot 1 (gelesen wie in Dokument)
Das visuelle Netz mit seinen Parametern wird dem Plot
zugewiesen. Damit das Resultat zufrieden stellen ist, müssen einige Parameter bei Variation der Auflösung dpi
nachjustiert werden. Weitere Informationen zu den Parametern finden sich in der Matplotlib Dokumentation.
ax.plot_wireframe(x, y, z, rstride=2, cstride=2, linewidth=0.2, antialiased=True, color=(0,0,0), alpha=1)
Plot ausgeben |
![]() |
Im folgenden wird ein quadratischer Ausschnitt der Größe lim
generiert. Die Kantenlängen dieses Quadrats entsprechen des betragsmäßig höchsten Maximums bzw. Minimums aus allen Koordinatenrichtungen x
und y
.
# Skalierung der gezeigten Funktion lim = (max(abs(max(np.max(x), np.max(y), np.max(z))), abs(min(np.min(x), np.min(y), np.min(z))))) # Axenlimits setzen ax.set_xlim(-lim, lim) ax.set_ylim(-lim, lim) ax.set_zlim(-lim, lim)
Zu guter Letzt wird der Plot abgespeichert und – falls im Jupyter Notebook ausgeführt, direkt angezeigt.
# Koordinatenachsen ausblenden plt.axis('off') # Winkel der Ansicht festlegen ax.view_init(20, 35) # Plot unter "wired_torus.png" abspeichern plt.savefig("wired_torus_black.png", bbox_inches='tight') # Ausgabe im Jupyter Notebook plt.show()
English Version |
![]() |
#!/usr/bin/env python print("This will maybe take a while, depending on the figure dpi you have chosen and your system performance") # import necessary libarys import numpy as np import matplotlib.pyplot as plt import matplotlib as mpl from mpl_toolkits.mplot3d import Axes3D # activate the output into the jupyter notebook %matplotlib inline # container "fig" for the plot(s, it's actually one^^) # for more info: https://matplotlib.org/api/_as_gen/matplotlib.pyplot.figure.html fig = plt.figure(figsize=plt.figaspect(1), dpi=700) # toroidal coordinate sytem with angle phi to the x-axis and theta to the z-axis # see also: http://inspirehep.net/record/1324991/files/torus_compel.png phi = np.linspace(0, 2 * np.pi, endpoint=True, num=75) # "num" phi proportions of the coordinate system theta = np.linspace(0, 2 * np.pi, endpoint=True, num=75) # "num" theta proportions of the coordinate system phi, theta = np.meshgrid(phi, theta) # phi and theta matrices with elements for every point wich will be rendered R=1.5 # radius from the coordinate origin r=1 # radius from the "pipe" # coordinate transformation from toroidal to cartesian (the standart one) # for more info: https://en.wikipedia.org/wiki/Torus#Geometry x = np.cos(theta) * (R + r * np.cos(phi)) y = np.sin(theta) * (R + r * np.cos(phi)) z = r * np.sin(phi) # activate LaTeX(-Font) plt.rc('text', usetex=True) plt.rc('font', family='serif') # Add a plot to the container (figure) ax = fig.add_subplot(1, 1, 1, projection='3d') # 1 row, 1 colomn, plot 1 (read like a document) ax.plot_wireframe(x, y, z, rstride=2, cstride=2, linewidth=0.2, antialiased=True, color=(0,0,0), alpha=1) ax.set_title('![]()
') # scaling of the shown function lim = (max(abs(max(np.max(x), np.max(y), np.max(z))), abs(min(np.min(x), np.min(y), np.min(z))))) # Set the axis limits ax.set_xlim(-lim, lim) ax.set_ylim(-lim, lim) ax.set_zlim(-lim, lim) # deactivae coordinate axis rendering plt.axis('off') # change the viewing angle ax.view_init(20, 35) # save the plot to the file "wired_torus.png" plt.savefig("wired_torus_black.png", bbox_inches='tight') # output plt.show()
Bildquellen |
![]() |
[1] | . |
[2] | Krishnavedala. (14. April 2014). Torus. Von Wikipedia, the free encyclopedia: https://en.wikipedia.org/wiki/Torus#/media/File:Torus_cycles.svg abgerufen |
[3] | Hunter, J., Dale, D., Firing, E., & Droettboom, M. (28. Ferbrar 2019). Matplotlib Documentation 3.0.3. Von Matplotlib: https://matplotlib.org/Matplotlib.pdf abgerufen |