Aus Linux-Magazin 05/2021

Python-Paket berechnet Widerstände und Füllmengen

Ob er Wasser in die Badewanne einlässt oder Strom durch Widerstände schickt – mithilfe des Python-Pakets Sympy jongliert Mike Schilli mit mathematischen Formeln.

An ein traumatisches Ereignis als Grundschüler erinnere ich mich noch genau: Eine Wochenendzeitung hatte eine Knobelaufgabe für Kinder gestellt, deren Auflösung sie in der nächsten Ausgabe eine Woche später versprach. Es handelte sich um eine Badewanne mit zwei Wasserhähnen, von denen der eine die Wanne in 10, der andere in 15 Minuten füllte. Die Frage lautete, nach welcher Zeit die Wanne voll sei, wenn man beide Hähne ganz aufdreht.

Als kleiner Junge war ich mir absolut sicher, 10 plus 15 ergibt 25, also 25 Minuten. Mein Vater lachte und gab zu bedenken, das könne nicht sein, denn zwei Hähne füllten die Badewanne schneller als einer allein. Am nächsten Wochenende triumphierte ich zunächst, denn schwarz auf weiß gedruckt kam in der folgenden Ausgabe tatsächlich die Bestätigung, 25 Minuten sei die richtige Lösung.

Wieder eine Woche später allerdings folgte die Ernüchterung: Nach erbosten Leserkommentaren musste die Zeitung eingestehen, dass ihr ein Irrtum unterlaufen war, denn es dauert nicht 25, sondern nur 6 Minuten, bis beide Hähne die Wanne füllen. Ich fiel aus allen Wolken und beschloss, ein berühmter Kolumnenschreiber für mathematische Rätsel zu werden.

Abbildung 1: Zwei parallel geschaltete Widerst&auml;nde <span class="ui-element">R1</span> und <span class="ui-element">R2</span> &hellip;

Abbildung 1: Zwei parallel geschaltete Widerstände R1 und R2

Abbildung 2: &hellip; und ihr Ersatzwiderstand <span class="ui-element">R</span>.

Abbildung 2: … und ihr Ersatzwiderstand R.

Wasserhähne und Widerstände

Erst viel später, im Studium während einer Vorlesung für Schaltungstechnik, kam mir ein ähnliches Problem unter, das ebenfalls eine gute Erklärung der Lösung des Badewannenproblems bot: die parallele Anordnung von Widerständen in einem Schaltkreis. Der Elektrotechniker misst den Widerstand eines Leiters in Ohm, und je höher der Wert, desto stärker bremst er den Stromfluss.

Wie berechnet sich nun der Ersatzwiderstand einer Parallelschaltung mit zwei Widerständen R1 und R2 wie in Abbildung 1? Die Lösung führt über die Ströme I1 und I2, die durch die einzelnen Widerstände fließen. Sie summieren sich nach der Wiedervereinigung der beiden Kabel zum Gesamtstrom I. Die anliegende Spannung ist überall gleich, nämlich U, und nach dem Ohmschen Gesetz gilt U = R*I. Ergo summieren sich die Einzelströme I1 und I2 zu U/R1 + U/R2.

Ersetzt man nun die beiden parallel angeordneten Widerstände R1 und R2 durch einen Ersatzwiderstand R, ergibt sich für den Gesamtstrom ebenfalls I = U/R, also gilt: U/R = U/R1 + U/R2. Die Spannung U kürzt sich heraus, sodass sich 1/R = 1/R1 + 1/R2 und nach Umformung (Abbildung 2) folgendes Resultat ergibt:

R = (R1*R2)/(R1+R2)

Heute nehmen Programme dem Ingenieur die Rechenarbeit ab. Listing 1 zeigt die Entwicklung der Formel mit sympy, einem Paket für symbolische Algebra in Python. Damit lassen sich Symbole definieren, die das Paket später bei der Auswertung von Formeln intakt lässt, statt gleich Variablen durch Werte zu ersetzen und das numerische Ergebnis einer Formel zu ermitteln. Mit der Funktion »simplify()« beherrscht Sympy auch die Regeln der Algebra und kann Ausdrücke vereinfachen – ganz so, wie das ein mathematisch talentierter Mensch täte.

Listing 1

parallel.py

#!/usr/bin/env python3
from sympy import simplify, symbols, pprint
r, r1, r2, u, i1, i2 = symbols("r r1 r2 u i1 i2")
i1 = u / r1
i2 = u / r2
r = u / (i1 + i2)
pprint(simplify(r))

So definiert Zeile 3 in Listing 1 einen ganzen Schwung von Symbolen. Für die beiden Widerstände der Schaltung und deren Ersatzwiderstand gibt es »r1«, »r2« und »r«. Für die anliegende Spannung steht »u«, für die beiden Teilströme »i1« und »i2«. Die Formeln in den Zeilen 4 bis 6 wenden das Ohmsche Gesetz an und summieren die Teilströme zum Gesamtstrom.

Der Wert für den Ersatzwiderstand »r« kommt beim Aufruf des Skripts über die Funktion »pprint()« (pretty print) zutage (Abbildung 3). Sympy hat offensichtlich erkannt, dass sich die Spannung »u« aus dem Bruch herauskürzen lässt und das Ergebnis nicht mehr von ihr abhängt.

Abbildung 3: Ausgabe via &raquo;pprint()&laquo;.

Abbildung 3: Ausgabe via »pprint()«.

TIPP

Wer Python 3 nutzt, installiert Pakete wie sympy und matplotlib einfach mit dem Kommando »pip3 install Paket« – am besten in einer Virtualenv-Umgebung [1], um anderen Python-Skripten auf demselben Host nicht eventuell den Teppich wegzuziehen.

Vom Widerstand zur Badewanne

Die Formel funktioniert auch für das eingangs erwähnte Badewannenproblem: Im Zähler steht das Produkt der Füllzeiten pro Wasserhahn (10*15), im Nenner deren Summe (10+15). Das macht nach Adam Riese 150/25, also 6 Minuten, genau wie in der Musterlösung des Sonntagsblatts.

Man kann sich die Lösung übrigens auch folgendermaßen überlegen: Nach einer Minute hat der erste Wasserhahn die Wanne zu einem Zehntel gefüllt, der zweite zu einem Fünfzehntel. Beide zusammen schaffen in einer Minute 1/10 + 1/15 = 3/30 + 2/30 = 5/30 = 1/6 der Wanne, also kann man nach 6 Minuten ins Bad steigen.

Grenzwertig

Was passiert, wenn beide Widerstände in der Parallelschaltung gegen 0 Ohm gehen, also den Stromfluss überhaupt nicht bremsen, oder im Fall einer Badewanne mit Niagara-artigen Füllhähnen, die die Wanne im Nullkommanichts füllen? Intuitiv ist klar, dass solche Spitzenarmaturen die Wanne in Parallelschaltung praktisch ebenso schnell füllen wie einzeln betrieben. Wer aber in der Formel (R1*R2)/(R1+R2) die Werte für R1 und R2 auf null setzt und das Konstrukt einem Python-Programm zum Ausrechnen übergibt, der erlebt sein blaues Wunder: Computer weigern sich standhaft, Divisionen durch null auszuführen, da diese mathematisch undefiniert sind.

Im vorliegenden Fall steht aber sowohl im Zähler als auch im Nenner ein gegen null tendierender Wert, was manchmal interessante (weil endliche) Ergebnisse liefert. Wohl gemerkt, eine echte Null als Nenner entzieht sich der mathematischen Definition, aber den Grenzwert für R1 und R2 gegen null kann man sehr wohl ausrechnen.

Sympy hält dazu die Funktion »limit()« parat, die eine symbolische Formel, ein Symbol (etwa »r1«) sowie einen Grenzwert (hier »0«) entgegennimmt. Listing 2 setzt vorher noch »r1 = r2«, sodass beide Variablen in der »limit()«-Funktion gegen null tendieren. Für den Grenzwert des Ersatzwiderstands liefert das Skript beim Aufruf erwartungsgemäß eine Null (Listing 3).

Listing 2

limit.py

#!/usr/bin/env python3
from sympy import limit, symbols
r1, r2, r = symbols("r1 r2 r")
r = (r1 * r2) / (r1 + r2)
r1 = r2
# r1/2->0
print(limit(r, r1, 0))
# 1/x with x->0
x = symbols("x")
print(limit(1/x, x, 0))

Listing 3

Grenzwert

$ ./limit.py
0
oo

Der zweite Testfall ab der vorletzten Zeile von Listing 2 verdeutlicht, was mit der Formel »1/x« passiert, wenn »x« gegen null strebt. Die Ausgabe zeigt, dass das Ergebnis der Formel in diesem Fall gegen unendlich strebt, was die ASCII-Ausgabe »oo« illustrieren soll.

Schön und in Farbe

Um zu demonstrieren, wie die beiden Wasserhähne beim Füllen der Wanne zusammenspielen, stellen die drei Balkengrafiken in Abbildung 4 den jeweiligen Füllstand im Minutentakt dar. Im linken Graph dreht der Bademeister nur den langsamen Hahn auf, der die Wanne in 15 Minuten füllt, im mittleren ist der 10-Minuten-Schnellhahn aktiv, im rechten beide Hähne zusammen. Der besseren Druckbarkeit zuliebe stellen wir hier die Graphen nebeneinander dar; das Programm aus Listing 4 gibt sie jedoch untereinander aus.

Abbildung 4: Mit Pyplot erzeugte Balkengrafiken zur Wannenf&uuml;llung. Links der Verlauf mit dem 10-Minuten-Hahn, in der Mitte der mit dem 15-Minuten-Hahn. Ganz rechts bef&uuml;llen beide die Wanne.

Abbildung 4: Mit Pyplot erzeugte Balkengrafiken zur Wannenfüllung. Links der Verlauf mit dem 10-Minuten-Hahn, in der Mitte der mit dem 15-Minuten-Hahn. Ganz rechts befüllen beide die Wanne.

Listing 4

bars.py

#!/usr/bin/env python3
from matplotlib import pyplot as plt
from fractions import Fraction
def fill_tub(per_min,xmax):
  lx=list(range(1,xmax+1))
  ly=[0] * xmax
  sum=0
  for i in range(xmax):
    sum+=per_min
    if sum > 1:
      break
    ly[i]=sum
  return lx,ly
xmax=15
plt.style.use('ggplot')
fig,ax = plt.subplots(nrows=3,ncols=1,figsize=(5,10))
fig.suptitle("Filling a bath tub")
fig.subplots_adjust(hspace=.5)
lx,ly = fill_tub(Fraction(1,15),xmax)
ax[0].bar(lx,ly,color='tab:red')
ax[0].set_xlabel("Minutes")
ax[0].set_ylabel("Fill Level")
ax[0].set_title("15min faucet")
lx,ly = fill_tub(Fraction(1,10),xmax)
ax[1].bar(lx,ly,color='tab:orange')
ax[1].set_xlabel("Minutes")
ax[1].set_ylabel("Fill Level")
ax[1].set_title("10min faucet")
lx,ly = fill_tub(Fraction(1,10)+Fraction(1/15),xmax)
ax[2].bar(lx, ly, color='tab:green')
ax[2].set_xlabel("Minutes")
ax[2].set_ylabel("Fill Level")
ax[2].set_title("Both 15min and 10min faucet")
plt.savefig("bars.png")

Dabei nimmt die Funktion »fill_tub()« ab Zeile 5 die Wannenfüllrate pro Minute (»per_min«) sowie die Anzahl der dargestellten X-Werte entgegen, also den Minutenticker. Obwohl die dargestellten Szenarien jeweils unterschiedliche Mengen von Minutenwerten bedienen, bestehen die Funktionen der »matplotlib« darauf, dass X- und Y-Werte als gleich große Arrays vorliegen, sonst hagelt es schwer verständliche Fehlermeldungen aus den Untiefen der Library. Die Funktion »fill_tub()« gibt zwei Arrays »lx« und »ly« zurück, die Werte für die X- und Y-Achsen in der Balkengrafik enthalten. Beide Arrays haben dieselbe Länge, und unbesetzte Y-Werte bei bereits voller Wanne werden zur Initialisierung in Zeile 7 einfach auf null gesetzt.

Drei verschiedene Graphen untereinander in eine Bilddatei zu malen, ist für »matplotlib« ein Kinderspiel: Die Funktion »subplots()« erzeugt in Zeile 20 einfach ein Diagrammraster mit drei Reihen sowie einer Spalte und gibt in »ax« ein Array von drei Graph-Objekten zurück. Eigentlich sollte »matplotlib« dann alles geschmackvoll zu einem Gesamtgebinde arrangieren, doch im vorliegenden Fall sah die Darstellung ohne manuelle Intervention etwas gequetscht aus. Deshalb schafft Zeile 22 mit »hspace« noch zusätzlichen Spielraum zwischen den Einzelgraphen. Die letzte Zeile schließlich schreibt die Bilddaten in die Bilddatei »bars.png«.

Exaktes Bruchrechnen

Damit das Programm aus einem Bruch wie 1/15 nicht sofort eine Fließkommazahl macht, zieht Listing 4 das Paket fractions hinzu, das echte Bruchrechnung beherrscht. So kann Zeile 25 mit »Fraction(1, 15)« tatsächlich 1/15 an »fill_tub()« übergeben und nicht etwa »0.0666666666666667«. Letzteres würde beim Aufsummieren später böse Rundungsfehler einschleusen und die Wanne eventuell sogar zum Überlaufen bringen. Das Paket fractions hingegen überlädt Operatoren wie »+« oder »>«. Daher zählt »sum += per_min« in Zeile 10 zur Wassermenge in der Wanne auch tatsächlich 1/15 hinzu, und »sum« in der Abfrage in Zeile 11 steht später bei voller Wanne exakt auf 1 und nicht etwa auf einem schrägen Float-Wert. (uba/jlu)

TIPP

Quelle: Rheinwerk Verlag

Quelle: Rheinwerk Verlag

Interessieren Sie sich für wissenschaftliche Anwendungen und möchten diese mit Python angehen, empfiehlt sich der Erwerb des exzellenten Buchs “Der Python-Kurs für Ingenieure und Naturwissenschaftler” [2]. Autor Veit Steinkamp reißt typische Anwendungsfälle für Python-Programme in den Naturwissenschaften an, indem er von Beispiel zu Beispiel springt. Dabei präsentiert er die Themen so breit gefächert und tiefgründig, dass selbst gestandene Ingenieure noch das ein oder andere längst vergessene Schmankerl aus Studienzeiten wiederentdecken.

Infos

  1. “Virtual Environments and Packages”: https://docs.python.org/3/tutorial/venv.html
  2. “Der Python-Kurs für Ingenieure und Naturwissenschaftler”: https://www.rheinwerk-verlag.de/der-python-kurs-fuer-ingenieure-und-naturwissenschaftler/
  3. Listings zu diesem Artikel: http://www.linux-magazin.de/static/listings/magazin/2021/04/snapshot/
DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 4 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben