Last active
April 17, 2022 02:05
-
-
Save Kenny2github/a4e1b15b3a5ac78fe76fa017728b6427 to your computer and use it in GitHub Desktop.
Compute the equivalent impedance between two nodes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from __future__ import annotations | |
from itertools import combinations | |
class Node: | |
components: set[Component] | |
num: int = 1 | |
def __init__(self, components: set = None) -> None: | |
self.num = type(self).num | |
type(self).num += 1 | |
if components: | |
self.components = components.copy() | |
else: | |
self.components = set() | |
def __repr__(self) -> str: | |
return f'<Node #{self.num}>' | |
def connect(self, component: Component) -> None: | |
self.components.add(component) | |
def disconnect(self, component: Component) -> None: | |
self.components.discard(component) | |
class Component: | |
value: complex | |
left: Node | |
right: Node | |
num: int = 1 | |
@property | |
def nodes(self) -> set[Node]: | |
return {self.left, self.right} | |
def __init__(self, value: complex, left: Node, right: Node) -> None: | |
self.value = value | |
self.left = left | |
self.right = right | |
left.connect(self) | |
right.connect(self) | |
self.num = type(self).num | |
type(self).num += 1 | |
def __repr__(self) -> str: | |
return f'Component #{self.num}(value={self.value!r}, left={self.left!r}, right={self.right!r})' | |
def _combine(self, components: set[Component], other: Component, | |
value: complex, left: Node, right: Node) -> Component: | |
new = Component(value, left, right) | |
left.disconnect(self) | |
left.disconnect(other) | |
right.disconnect(self) | |
right.disconnect(other) | |
components.discard(self) | |
components.discard(other) | |
components.add(new) | |
# old components and common node get garbage collected | |
def combine_series(self, components: set[Component], | |
other: Component) -> Component: | |
"""Combine this component with the other in series.""" | |
left, right = self.nodes ^ other.nodes | |
self._combine(components, other, self.value + other.value, left, right) | |
def combine_parallel(self, components: set[Component], | |
other: Component) -> Component: | |
"""Combine this component with the other in parallel.""" | |
new_value = self.value * other.value / (self.value + other.value) | |
self._combine(components, other, new_value, self.left, self.right) | |
def wye_delta(nodes: list[Node], components: set[Component], | |
A: Node, B: Node) -> None: | |
"""Perform any Y-Δ transform on the network.""" | |
for common_node in nodes: | |
if common_node is A or common_node is B: | |
continue # can't delete output node | |
if len(common_node.components) != 3: | |
continue | |
R1, R2, R3 = common_node.components | |
N1, N2, N3 = ((R.nodes ^ {common_node}).pop() | |
for R in (R1, R2, R3)) | |
Rp = sum(a.value * b.value for a, b in combinations( | |
(R1, R2, R3), 2)) | |
Ra = Component(Rp / R1.value, N2, N3) | |
Rb = Component(Rp / R2.value, N1, N3) | |
Rc = Component(Rp / R3.value, N1, N2) | |
for R, N in ((R1, N1), (R2, N2), (R3, N3)): | |
N.disconnect(R) | |
components.discard(R) | |
components.update({Ra, Rb, Rc}) | |
nodes.remove(common_node) | |
return | |
def equivalent_impedance(nodes: list[Node], components: set[Component], | |
A: Node, B: Node) -> complex: | |
"""Find the equivalent impedance of a network between nodes A and B. | |
A and B must be present in the nodes list. | |
""" | |
nodes = nodes.copy() | |
components = components.copy() | |
while len(components) > 1: | |
#print(nodes, components, sep='\n', end='\n\n') | |
for R1, R2 in combinations(components, 2): | |
common = R1.nodes & R2.nodes | |
if len(common) == 1: # in series? | |
common_node = common.pop() | |
if common_node is A or common_node is B: | |
continue # not in series if crosses output node | |
if common_node.components != {R1, R2}: | |
continue # not connected to exactly these two components | |
R1.combine_series(components, R2) | |
nodes.remove(common_node) # common node vanishes | |
break | |
elif len(common) == 2: # in parallel | |
R1.combine_parallel(components, R2) | |
break | |
else: # everything is neither in series nor in parallel | |
wye_delta(nodes, components, A, B) | |
return components.pop().value | |
def main() -> None: | |
node_count = int(input('How many nodes? ')) | |
component_count = int(input('How many components? ')) | |
nodes = [Node() for _ in range(node_count)] | |
components = set() | |
for i in range(1, component_count + 1): | |
value = complex(input(f'Enter value of component {i} (a±bj, in Ω): ')) | |
left, right = map(int, input( | |
f'Enter the two node numbers that component {i} is connected to (e.g. 1 2): ').split()) | |
components.add(Component(value, nodes[left-1], nodes[right-1])) | |
A, B = [nodes[int(i) - 1] for i in input( | |
f'Enter the two nodes to find equivalent impedance between (e.g. 1 4): ').split()] | |
print() | |
print('\nEquivalent impedance is', equivalent_impedance( | |
nodes, components, A, B)) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment