Cet article est le 23e jour du Programmation compétitive (2) Calendrier de l'Avent 2019. J'ai écrit un solveur de courant continu qui calcule le nombre de courants et de résistances combinées qui traverseront chaque résistance dans un circuit dans lequel plusieurs résistances électriques sont connectées en série ou en parallèle. (Bien que cela soit rarement demandé dans la programmation actuelle du concours, c'est correct car il s'agit d'un graphique et d'un calcul)
Par exemple, supposons que vous souhaitiez connaître la résistance combinée du circuit de résistance suivant réalisé en combinant des résistances avec chaque résistance de 1,0Ω. Bien que cela semble simple, il est très difficile de le calculer, donc dans cet article, le programme qui calcule cela sera appelé le solveur de courant CC. Comme prémisse
--Chaque valeur de résistance est une valeur connue
En résolvant les équations simultanées formulées selon la loi de Kirchhof de la tension et du courant, le potentiel de chaque nœud et le courant de chaque résistance peuvent être obtenus. Par conséquent, il est nécessaire de définir les variables de manière appropriée.
Formulez la pièce en rouge comme une variable. Puisqu'il est finalement réduit à une équation linéaire, l'élément inconnu minimum devient une variable. Dans les cours et les examens de physique des lycées, le calcul actuel pour les systèmes avec de nombreuses variables inconnues est rarement demandé, mais s'il y a beaucoup de variables, il est nécessaire de concevoir une méthode de mise en œuvre en fonction du nombre d'équations linéairement indépendantes à formuler. Je vais. En considérant la connexion de l'alimentation comme une variable, nous pouvons formuler les équations nécessaires et suffisantes comme suit.
Vous obtiendrez le même nombre d'expressions que le nombre de variables définies. Le fait est que la formule obtenue sans connecter l'alimentation ne devient pas linéairement indépendante, et que la formule linéairement indépendante peut être obtenue quel que soit le nombre de nœuds qui connectent l'alimentation électrique sont augmentés (sauf si les alimentations sont court-circuitées). .. (Peut être prouvé par induction (devrait))
Par exemple, dans le cas d'une résistance série aussi simple
Un total de 7 peut être formulé comme suit.
Puisqu'il existe un solveur linéaire qui peut être utilisé facilement, je l'écrirai en Python. En écrivant un solveur, la politique suivante
La source est GitHub. C'est presque comme ça
DCsolver.py
# coding: utf-8
import sys
import time
import numpy as np
from collections import deque
class DCsolver:
def __init__(self, nodenum):
#Solveur
self.linear_solver = np.linalg.solve
#Le nombre de nœuds est fixe. Mettre à jour le nombre de résistances et le nombre de connexions de polarisation
self.n_node = nodenum
self.n_res = 0
self.n_bias_probe = 0
#Résistance: point de départ, point final, valeur de résistance
self.res_from = []
self.res_to = []
self.res_value = []
#Alimentation: accès par nom pour connexion virtuelle
self.bias_name = []
self.bias_level = []
self.bias_index = dict() # dict<string,int>
# Bias supplied (-1: not biased)
self.biased = [-1] * self.n_node
#Déterminez lors du calcul à quel nœud chaque biais sonde
self.bias_from = None
self.bias_to = None
#Équation linéaire A*X=Résoudre 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_de nœud en nœud_valeur de résistance res à à_Connectez la résistance de valeur
#Définir la direction du courant dans cette direction
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):
#Empêcher plusieurs biais d'accéder à un nœud pour refléter les derniers paramètres
assert bias_name in self.bias_index, \
"{0} is not defined, please define before supply".format(bias_name)
idx = self.bias_index[bias_name]
#Avertir si le biais est déjà fourni.
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)Définir
nv = self.n_node
nr = self.n_res
nb = self.n_bias_probe
#Répertoriez et indexez les paramètres de condition de biais finaux
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)
#Taille de la matrice=Nombre de nœuds+Nombre de résistances+Nombre de chemins d'alimentation de biais
#Variable inconnue
# [0...nv-1]:Potentiel du nœud i
# [nv...nv+nr-1] :Courant de résistance
# [nv+nr...n-1] :Courant de chemin d'alimentation de polarisation
n = nv + nr + nb
mat = np.zeros((n,n))
vec = np.zeros(n)
# Kirchhoff's Loi actuelle (La somme des dépenses et des revenus de chaque nœud est 0)
#ligne i([0...nv-1])L'équation est la somme des courants pour le nœud i
#Le courant traversant la résistance j provient de[j]Aller à[j]Compté comme revenu
for j in range(nr):
mat[self.res_from[j]][nv + j] = 1
mat[self.res_to[j]][nv + j] = -1
# Kirchhoff's Loi de tension (la chute de tension de chaque résistance est une différence de potentiel)
# nv+ligne j([nv...nv+nr-1])L'équation est la somme des courants pour la résistance 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]
#Formule de définition du biais
# bias_from[i]Le potentiel est un biais_level['bias']Est fixé à. (Ajouté à la partie KVL)
# bias_to[i]Le courant de l'alimentation électrique pénètre dans(Ajouté à la partie KCL)
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
#Recherchez les nœuds qui ne sont pas connectés à Bias (les nœuds flottants ne sont pas autorisés)
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]
#Le courant de nœud calcule uniquement la sortie ((Σ)|outgo|+Σ|income|)/Calculer avec 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))
Le fait est que le supply_bias_to_node
est vérifié afin que différentes alimentations ne soient pas connectées au même nœud, et que check_connention
est inséré au moment de générer la matrice afin qu'il n'y ait pas de nœuds non alimentés (matrice). Le solveur linéaire lève juste une erreur lorsqu'il est dégénéré)
Étant donné que la quantité de temps de calcul du prétraitement est $ O (nombre de nœuds + nombre de résistances + nombre de connexions d'alimentation) $, le calcul lui-même devrait être presque déterminant par la quantité de calcul du solveur linéaire.
Je vais vraiment l'utiliser. Tout d'abord, un exemple simple.
Voici un exemple de connexion de deux résistances en série à trois nœuds. La résistance combinée est l'inverse du courant de sortie de Vdd, mais le courant Vdd est de 0,0333 ... A, ce qui est certainement 30Ω.
Ensuite, essayez de connecter la deuxième résistance en parallèle. Deux résistances sont définies du nœud 1 au nœud 2. La résistance combinée est de 20Ω, mais le courant Vdd est sûrement de 0,05A.
Un exemple un peu compliqué
Je ne sais pas combien d'éléments enveloppants il y a intuitivement, mais il semble que le courant Vdd est de 4Ω à 0,25A. Puisque V1, V3 et V4 ont le même potentiel, ils sont effectivement rétractés (1/20 + 1/10 + 1 / (5 + 5)) ^ -1.
Une partie de la distribution du potentiel et de la distribution du courant lorsqu'un potentiel approprié est fourni aux nœuds du maillage à l'aide d'un solveur. C'est un maillage composé de 50 x 50 nœuds, et est connecté par 1Ω entre des nœuds adjacents.
sample1
sample2
sample3
Bien qu'il s'agisse d'un temps de calcul, il semble que même avec la même structure de circuit, il diffère considérablement selon la façon de connecter l'alimentation et le nombre, et sample1 et sample3 pourraient être calculés en moins d'une seconde, mais sample2 a pris près de 200 secondes. Le nombre de variables est (50 × 50) + 2 * (50 * 49) + le nombre de connexions électriques est de 7400 ou plus, donc j'ai l'impression que c'est toujours rapide. (J'ai utilisé la bibliothèque de matrices éparses de scipy) Puisqu'il est calculé par la méthode directe, il semble qu'il sera un peu plus rapide s'il est calculé par la méthode indirecte (méthode répétitive) (non vérifiée cette fois). J'ai mis le code etc. dans ici.
Ayez une vie de circuit électrique amusante dans le futur!
Recommended Posts