import enum class LcrConnException(Exception): """The exception thrown by LCR Connector""" pass class DeviceKind(enum.IntEnum): """The kind of device""" RESISTOR = enum.auto() """Resistor device""" CAPACITOR = enum.auto() """Capacitor device""" INDUCTOR = enum.auto() """Inductor device""" class JointKind(enum.IntEnum): """The joint type between 2 devices""" SERIES = enum.auto() """Series connection""" PARALLEL = enum.auto() """Parallel connection""" def flip(self) -> "JointKind": """ Flip the joint kind Flip the joint kind from series to parallel or vice versa :return: The flipped joint kind """ match self: case JointKind.SERIES: return JointKind.PARALLEL case JointKind.PARALLEL: return JointKind.SERIES class SubCircuit: """The part of circuit composed of two devices and the joint kind""" __device_value: float """The value of the device""" __joint_kind: JointKind """The joint kind between this device and the next device""" def __init__(self, device_value: float, joint_kind: JointKind): self.__device_value = device_value self.__joint_kind = joint_kind def compute(self, value: float, device_kind: DeviceKind) -> float: """ Compute the joint value :param value: The value computed from previous devices :param device_kind: The kind of the device :return: The joint value computed """ if self.__device_value <= 0 or value <= 0: raise LcrConnException("Device value must be greater than 0") # We perform series connect for: series resistor, series inductor and parallel capacitor. # We perform parallel connect for: parallel resistor, parallel inductor and series capacitor. joint_kind = self.__joint_kind if device_kind == DeviceKind.CAPACITOR: joint_kind = joint_kind.flip() match joint_kind: case JointKind.SERIES: return self.__device_value + value case JointKind.PARALLEL: return (self.__device_value * value) / (self.__device_value + value) def get_device_value(self) -> float: """ Get the device value :return: The device value """ return self.__device_value def get_joint_kind(self) -> JointKind: """ Get the joint kind :return: The joint kind """ return self.__joint_kind class CircuitDeviceCount(enum.IntEnum): """The number of devices in the circuit""" ONE = enum.auto() """One device""" TWO = enum.auto() """Two devices""" THREE = enum.auto() """Three devices""" def to_device_count(self) -> int: match self: case CircuitDeviceCount.ONE: return 1 case CircuitDeviceCount.TWO: return 2 case CircuitDeviceCount.THREE: return 3 class Circuit: """The circuit composed of multiple joints""" __first_device_value: float """The value of the first device""" __second_device_subckt: SubCircuit | None """The second device and its joint property""" __third_device_subckt: SubCircuit | None """The third device and its joint property""" def __init__( self, first_device_value: float, second_device_subckt: SubCircuit | None, third_device_subckt: SubCircuit | None, ): """ Initialize the circuit :param first_device_value: The value of the first device :param second_device_subckt: The second device and its joint property :param third_device_subckt: The third device and its joint property """ if second_device_subckt is None and third_device_subckt is not None: raise LcrConnException("Third device cannot exist without second device") self.__first_device_value = first_device_value self.__second_device_subckt = second_device_subckt self.__third_device_subckt = third_device_subckt @staticmethod def from_one_device(device1_value: float) -> "Circuit": return Circuit(device1_value, None, None) @staticmethod def from_two_devices( device1_value: float, device2_value: float, device2_joint: JointKind ) -> "Circuit": return Circuit(device1_value, SubCircuit(device2_value, device2_joint), None) @staticmethod def from_three_devices( device1_value: float, device2_value: float, device2_joint: JointKind, device3_value: float, device3_joint: JointKind, ) -> "Circuit": return Circuit( device1_value, SubCircuit(device2_value, device2_joint), SubCircuit(device3_value, device3_joint), ) def get_device_count(self) -> CircuitDeviceCount: if self.__third_device_subckt is not None: return CircuitDeviceCount.THREE elif self.__second_device_subckt is not None: return CircuitDeviceCount.TWO else: return CircuitDeviceCount.ONE def get_first_device_value(self) -> float: """ Get the value of the first device :return: The value of the first device """ return self.__first_device_value def get_second_device_joint(self) -> JointKind: """ Get the joint kind of the second device :return: The joint kind of the second device :raises LcrConnException: If there is no second device """ if self.__second_device_subckt is not None: return self.__second_device_subckt.get_joint_kind() else: raise LcrConnException("No second device") def get_second_device_value(self) -> float: """ Get the value of the second device :return: The value of the second device :raises LcrConnException: If there is no second device """ if self.__second_device_subckt is not None: return self.__second_device_subckt.get_device_value() else: raise LcrConnException("No second device") def get_third_device_joint(self) -> JointKind: """ Get the joint kind of the third device :return: The joint kind of the third device :raises LcrConnException: If there is no third device """ if self.__third_device_subckt is not None: return self.__third_device_subckt.get_joint_kind() else: raise LcrConnException("No third device") def get_third_device_value(self) -> float: """ Get the value of the third device :return: The value of the third device :raises LcrConnException: If there is no third device """ if self.__third_device_subckt is not None: return self.__third_device_subckt.get_device_value() else: raise LcrConnException("No third device") def compute(self, device_kind: DeviceKind) -> float: """ Compute the circuit value :param device_kind: The kind of the device :return: The circuit value """ if self.__first_device_value <= 0: raise LcrConnException("Device value must be greater than 0") value = self.__first_device_value if self.__second_device_subckt is None: return value value = self.__second_device_subckt.compute(value, device_kind) if self.__third_device_subckt is None: return value value = self.__third_device_subckt.compute(value, device_kind) return value