Dieser Artikel ist der 23. Tag von Competitive Programming (2) Adventskalender 2019. Ich habe einen Gleichstromlöser geschrieben, der berechnet, wie viele Ströme und kombinierte Widerstände durch jeden Widerstand in einem Stromkreis fließen, in dem mehrere elektrische Widerstände in Reihe oder parallel geschaltet sind. (Obwohl es in der aktuellen Wettbewerbsprogrammierung selten gefragt wird, ist es in Ordnung, da es sich um eine Grafik und eine Berechnung handelt.)
Angenommen, Sie möchten den kombinierten Widerstand der folgenden Widerstandsschaltung kennen, die durch Kombinieren von Widerständen mit jedem Widerstand von 1,0 Ω hergestellt wird. Obwohl es einfach aussieht, ist es sehr schwierig, es tatsächlich zu berechnen. In diesem Artikel wird das Programm, das dies berechnet, als Gleichstromlöser bezeichnet. Als Voraussetzung
Durch Lösen der nach dem Kirchhofschen Gesetz von Spannung und Strom formulierten Gleichungen kann das Potential jedes Knotens und der Strom jedes Widerstands erhalten werden. Daher ist es notwendig, die Variablen entsprechend zu definieren.
Formulieren Sie das Teil in rot als Variable. Da es schließlich auf eine lineare Gleichung reduziert wird, wird das minimale unbekannte Element zu einer Variablen gemacht. In Physikklassen und Prüfungen an Gymnasien wird die aktuelle Berechnung eines Systems mit vielen unbekannten Variablen selten abgefragt. Wenn jedoch viele Variablen vorhanden sind, muss eine Implementierung erstellt werden, in der festgelegt wird, wie viele linear unabhängige Gleichungen formuliert werden sollen. Ich werde. Indem wir den Anschluss von der Stromversorgung als Variable betrachten, können wir die notwendigen und ausreichenden Gleichungen wie folgt formulieren.
Sie erhalten die gleiche Anzahl von Ausdrücken wie die Anzahl der definierten Variablen. Der Punkt ist, dass die Formel, die ohne Anschließen der Stromversorgung erhalten wird, nicht linear unabhängig wird und dass die linear unabhängige Formel erhalten werden kann, unabhängig davon, wie viele Knoten, die die Stromversorgung verbinden, erhöht sind (es sei denn, die Stromversorgungen sind kurzgeschlossen). .. (Kann induktiv nachgewiesen werden (sollte))
Zum Beispiel bei einem so einfachen Serienwiderstand
Insgesamt 7 können wie folgt formuliert werden.
Da es einen linearen Löser gibt, der leicht verwendet werden kann, werde ich ihn in Python schreiben. Beim Schreiben eines Lösers gilt die folgende Richtlinie
Die Quelle ist GitHub. Es ist fast so
DCsolver.py
# coding: utf-8
import sys
import time
import numpy as np
from collections import deque
class DCsolver:
def __init__(self, nodenum):
#Löser
self.linear_solver = np.linalg.solve
#Die Anzahl der Knoten ist festgelegt. Aktualisieren Sie die Anzahl der Widerstände und die Anzahl der Vorspannungsanschlüsse
self.n_node = nodenum
self.n_res = 0
self.n_bias_probe = 0
#Widerstand: Startpunkt, Endpunkt, Widerstandswert
self.res_from = []
self.res_to = []
self.res_value = []
#Stromversorgung: Zugriff nach Namen für die virtuelle Verbindung
self.bias_name = []
self.bias_level = []
self.bias_index = dict() # dict<string,int>
# Bias supplied (-1: not biased)
self.biased = [-1] * self.n_node
#Bestimmen Sie bei der Berechnung, auf welchen Knoten jede Vorspannung prüft
self.bias_from = None
self.bias_to = None
#Lineare Gleichung A.*X=Löse V.
self._A = None
self._V = None
self._X = None
# result
self.node_voltage = None
self.node_current = None
self.bias_total_current = None
self.bias_current_per_node = None
self.res_current = None
def add_resistance(self, node_from, node_to, res_value):
# node_von Knoten zu Knoten_Widerstandswert res bis to_Schließen Sie den Wertwiderstand an
#Definieren Sie die Richtung des Stroms in dieser Richtung
assert res_value > 0 , "inhibit res_value <= 0"
self.res_from.append(node_from)
self.res_to.append(node_to)
self.res_value.append(res_value)
self.n_res += 1
def define_bias(self, bias_name, bias_level=0.0):
if bias_name in self.bias_index:
idx = self.bias_index[bias_name]
self.bias_level[idx] = bias_level
else :
idx = len(self.bias_name)
self.bias_index[bias_name] = idx
self.bias_name.append(bias_name)
self.bias_level.append(bias_level)
def supply_bias_to_node(self, bias_name, node_to):
#Verhindern Sie, dass mehrere Verzerrungen auf einen Knoten zugreifen, um die neuesten Einstellungen widerzuspiegeln
assert bias_name in self.bias_index, \
"{0} is not defined, please define before supply".format(bias_name)
idx = self.bias_index[bias_name]
#Warnen, wenn bereits eine Vorspannung vorhanden ist.
if self.biased[node_to] != -1:
print("bias on node:{0} is changed: {1} --> {2} ".format(
node_to, self.bias_name[self.bias_index[self.biased[node_to]]], bias_name
))
self.biased[node_to] = idx
else :
self.biased[node_to] = idx
self.n_bias_probe += 1
def _create_matrix(self):
# (A, V)Definieren
nv = self.n_node
nr = self.n_res
nb = self.n_bias_probe
#Listen Sie die endgültigen Einstellungen für die Vorspannungsbedingungen auf und indizieren Sie sie
self.bias_from = []
self.bias_to = []
for i in range(nv):
if self.biased[i] != -1:
self.bias_from.append(self.biased[i])
self.bias_to.append(i)
assert nb == len(self.bias_from)
#Matrixgröße=Anzahl der Knoten+Anzahl der Widerstände+Anzahl der Vorspannungspfade
#Unbekannte Variable
# [0...nv-1]:Knoten ich Potenzial
# [nv...nv+nr-1] :Widerstandsstrom
# [nv+nr...n-1] :Vorspannungspfadstrom
n = nv + nr + nb
mat = np.zeros((n,n))
vec = np.zeros(n)
# Kirchhoff's Aktuelles Gesetz (Die Summe aus Outgo und Einkommen jedes Knotens ist 0)
#Linie i([0...nv-1])Gleichung ist die Summe der Ströme für Knoten i
#Der durch den Widerstand j fließende Strom kommt von[j]Geh raus zu[j]Als Einkommen gezählt
for j in range(nr):
mat[self.res_from[j]][nv + j] = 1
mat[self.res_to[j]][nv + j] = -1
# Kirchhoff's Spannungsgesetz (Der Spannungsabfall jedes Widerstands ist eine Potentialdifferenz)
# nv+Zeile j([nv...nv+nr-1])Gleichung ist die Summe der Ströme für den Widerstand j
for j in range(nr):
mat[nv + j][self.res_from[j]] = 1
mat[nv + j][self.res_to[j]] = -1
mat[nv + j][nv + j] = -self.res_value[j]
#Bias-Definitionsformel
# bias_from[i]Potenzial ist Voreingenommenheit_level['bias']Ist fest auf. (Zum KVL-Teil hinzugefügt)
# bias_to[i]Strom aus der Stromversorgung fließt in(Zum KCL-Teil hinzugefügt)
for j in range(len(self.bias_from)):
mat[nv + nr + j][self.bias_to[j]] = 1
vec[nv + nr + j] = self.bias_level[self.bias_from[j]]
mat[self.bias_to[j]][nv + nr + j] = -1
#Suchen Sie nach Knoten, die nicht mit Bias verbunden sind (schwebende Knoten sind nicht zulässig).
self.check_connention()
return mat, vec
def check_connention(self):
E = [[] for i in range(self.n_node)]
for i in range(self.n_res):
E[self.res_from[i]].append(self.res_to[i])
E[self.res_to[i]].append(self.res_from[i])
q = deque()
vis = [False] * self.n_node
for node in self.bias_to:
q.append(node)
while(len(q) > 0):
now = q.popleft()
vis[now] = True
for nxt in E[now]:
if not vis[nxt]:
q.append(nxt)
floating_node = []
for node in range(len(vis)):
if not vis[node]:
floating_node.append(node)
if len(floating_node) != 0:
print("Some floating node(s) exist:\nnode(s):\n{0}\n--> Aborted".format(floating_node))
sys.exit(0)
def solve(self):
A, V = self._create_matrix()
X = self.linear_solver(A, V)
X = np.array(X)
self._A = A
self._V = V
self._X = X
self.node_voltage = X[0:self.n_node]
self.res_current = X[self.n_node: self.n_node + self.n_res]
self.bias_current_per_node = dict()
self.bias_total_current = dict()
for bname in self.bias_name:
self.bias_current_per_node[bname] = []
self.bias_total_current[bname] = 0.0
for i in range(self.n_bias_probe):
bname = self.bias_name[self.bias_from[i]]
self.bias_current_per_node[bname].append( (self.bias_to[i], X[self.n_node + self.n_res + i]) )
self.bias_total_current[bname] += X[self.n_node + self.n_res + i]
#Der Knotenstrom berechnet nur den Ausgang ((Σ)|outgo|+Σ|income|)/Berechnen Sie mit 2)
self.node_current = np.zeros(self.n_node)
for i in range(self.n_res):
self.node_current[self.res_from[i]] += np.abs(self.res_current[i])
self.node_current[self.res_to[i]] += np.abs(self.res_current[i])
for bname in self.bias_current_per_node:
for node, cur in self.bias_current_per_node[bname]:
self.node_current[node] += np.abs(cur)
self.node_current /= 2.0
def print_result_summary(self, showCoef = False):
if showCoef:
print('A',self._A)
print('V',self._V)
print('X',self._X)
print('node_voltage\n:{0}\n'.format(self.node_voltage))
print('res_current\n:{0}\n'.format(self.res_current))
print('node_cur\n:{0}\n'.format(self.node_current))
print('bias_cur\n:{0}\n'.format(self.bias_total_current))
print('bias_cur_per_node\n:{0}\n'.format(self.bias_current_per_node))
Der Punkt ist, dass die supply_bias_to_node
überprüft wird, damit nicht verschiedene Stromversorgungen an denselben Knoten angeschlossen werden, und die check_connention
zum Zeitpunkt der Erzeugung der Matrix eingefügt wird, so dass keine nicht versorgten Knoten (Matrix) vorhanden sind. Der lineare Löser gibt nur einen Fehler aus, wenn er entartet ist.
Da der Zeitberechnungsbetrag der Vorverarbeitung $ O (Anzahl der Knoten + Anzahl der Widerstände + Anzahl der Stromversorgungsanschlüsse) $ beträgt, sollte die Berechnung selbst durch den Berechnungsbetrag des linearen Lösers nahezu ratenbestimmend sein.
Ich werde es tatsächlich benutzen. Zunächst ein einfaches Beispiel.
Dies ist ein Beispiel für die Reihenschaltung von zwei Widerständen mit drei Knoten. Der kombinierte Widerstand ist die Umkehrung des Stroms von Vdd, aber der Vdd-Strom beträgt 0,0333 ... A, was sicherlich 30 Ω ist.
Versuchen Sie als nächstes, den zweiten Widerstand parallel zu schalten. Von Knoten 1 bis Knoten 2 sind zwei Widerstände definiert. Der kombinierte Widerstand beträgt 20 Ω, aber der Vdd-Strom beträgt sicherlich 0,05 A.
Ein etwas kompliziertes Beispiel
Ich weiß nicht, wie viele Rundum-Elemente intuitiv vorhanden sind, aber es scheint, dass der Vdd-Strom 4 Ω bei 0,25 A beträgt. Da V1, V3 und V4 das gleiche Potential haben, werden sie effektiv zurückgezogen (1/20 + 1/10 + 1 / (5 + 5)) ^ -1.
Einige Verteilungen von Potentialen und Strömen, wenn den Knoten des Netzes unter Verwendung eines Lösers ein geeignetes Potential zugeführt wird. Es ist ein Netz, das aus 50 x 50 Knoten besteht und durch 1 Ω zwischen benachbarten Knoten verbunden ist.
sample1
sample2
sample3
Obwohl es sich um eine Berechnungszeit handelt, scheint es, dass sie sich selbst bei gleicher Schaltungsstruktur stark unterscheidet, je nachdem, wie die Stromversorgung angeschlossen ist und wie viele. Probe1 und Probe3 konnten in weniger als 1 Sekunde berechnet werden, Probe2 dauerte jedoch fast 200 Sekunden. Die Anzahl der Variablen beträgt (50 × 50) + 2 * (50 * 49) + Die Anzahl der Stromanschlüsse beträgt 7400 oder mehr, daher denke ich, dass es immer noch schnell ist. (Ich habe die spärliche Matrixbibliothek von scipy verwendet) Da es nach der direkten Methode berechnet wird, scheint es etwas schneller zu sein, wenn es nach der indirekten Methode (Iterationsmethode) berechnet wird (diesmal nicht verifiziert). Ich habe den Code usw. in [hier] eingefügt (https://github.com/kuuso/hobby/tree/master/dc_solver).
Viel Spaß beim Leben in Stromkreisen in der Zukunft!
Recommended Posts