diff --git a/circuit_solver.py b/circuit_solver.py index 6340ad6..5c77788 100644 --- a/circuit_solver.py +++ b/circuit_solver.py @@ -4,7 +4,7 @@ class CircuitSolver: def __init__(self, circuitCode): self.components = {} self.compileCircuit(circuitCode) - self.stepByStepReasoning = '' + self.stepByStepReasoning = [] def compileCircuitFromFile(fileName): with open(fileName, 'r') as circuitCode: @@ -38,7 +38,7 @@ def compileCircuit(self, circuitCode): subcomponentString = p if c[0] == 'r': #Create new resistor - r = Resistor(parameters[0], voltage = voltage, current = current, resistance = resistance) + r = Resistor(parameters[0], self.writeReasoning, voltage = voltage, current = current, resistance = resistance) self.components[r.name] = r else: #Create new Leg @@ -50,7 +50,7 @@ def compileCircuit(self, circuitCode): raise Exception(f'Must declare whether Leg "{parameters[0].upper()}" is series or parallel.') subcomponentNames = subcomponentString.split(',')[:-1] subcomponents = [self.components[n.upper()] for n in subcomponentNames] - l = Leg(parameters[0], circuitType, subcomponents, voltage = voltage, current = current, resistance = resistance) + l = Leg(parameters[0], self.writeReasoning, circuitType, subcomponents, voltage = voltage, current = current, resistance = resistance) self.components[l.name] = l else: #Return Statement @@ -65,22 +65,48 @@ def solve(self): if not self.circuit.solve(): raise Exception('Either not enough information passed resulting in ambigious case or algorithm missing solving method') - def showCircuit(self, showLegs = True, showResistors = True): - print(self.__str__(showLegs, showResistors)) + def writeReasoning(self, reasoning): + self.stepByStepReasoning.append(reasoning) - def __str__(self, showLegs = True, showResistors = True): + def getStepByStepReasoning(self, showVoltageSteps = True, showCurrentSteps = True, showResistanceSteps = True): + out = [] + i = 1 + for r in self.stepByStepReasoning: + write = False + if "Ohm's Law" in r: + if 'its voltage' in r and showVoltageSteps: + write = True + elif 'its current' in r and showCurrentSteps: + write = True + elif 'its resistance' in r and showResistanceSteps: + write = True + elif 'Voltage' in r and showVoltageSteps: + write = True + elif 'Current' in r and showCurrentSteps: + write = True + elif 'Resistance' in r and showResistanceSteps: + write = True + if write: + out.append(f'{i}. {r}') + i += 1 + return '\n'.join(out) + + def showStepByStepReasoning(self, showVoltageSteps = True, showCurrentSteps = True, showResistanceSteps = True): + print(self.getStepByStepReasoning(showVoltageSteps=showVoltageSteps, showCurrentSteps=showCurrentSteps, showResistanceSteps=showResistanceSteps)) + + def __str__(self, showLegs = True, showResistors = True, showVoltage = True, showCurrent = True, showResistance = True): def sortKeys(name): return 0 if name[0].upper() == 'L' else 1, sum([ord(i) for i in name[1:]]) componentNames = sorted(list(self.components.keys()), key = sortKeys) - out = str(self.circuit) + out = self.circuit.__str__(showVoltage=showVoltage, showCurrent=showCurrent, showResistance=showResistance) for n in componentNames: if not ((not showLegs and n[0] == 'L') or (not showResistors and n[0] == 'R') or n == self.circuit.name): - out += f'\n\n{self.components[n]}' + out += f'\n\n{self.components[n].__str__(showVoltage=showVoltage, showCurrent=showCurrent, showResistance=showResistance)}' pass return out - def __repr__(self, showLegs = True, showResistors = True): - return self.__str__(showLegs, showResistors) + def __repr__(self, showLegs = True, showResistors = True, showVoltage = True, showCurrent = True, showResistance = True): + return self.__str__(showLegs=showLegs, showResistors=showResistors, showVoltage=showVoltage, showCurrent=showCurrent, showResistance=showResistance) - def writeReasoning(reasoning): - self.stepByStepReasoning += reasoning; + def showCircuit(self, showLegs = True, showResistors = True, showVoltage = True, showCurrent = True, showResistance = True): + print(self.__str__(showLegs=showLegs, showResistors=showResistors, showVoltage=showVoltage, showCurrent=showCurrent, showResistance=showResistance)) diff --git a/component.py b/component.py index 9dadb0b..41d2626 100644 --- a/component.py +++ b/component.py @@ -5,8 +5,9 @@ class CircuitType(enum.Enum): parallel = 2 class Resistor: - def __init__(self, name, voltage = None, current = None, resistance = None, allowBypass = False): + def __init__(self, name, writeReasoning, voltage = None, current = None, resistance = None, allowBypass = False): self.name = name.upper() + self.writeReasoning = writeReasoning if not(voltage or current or resistance) and not allowBypass: raise Exception(f"Voltage, current, or resistance needs to be known for Resistor '{self.name}'") self.voltage = voltage @@ -28,27 +29,37 @@ def ohmsLaw(self): if self.voltage and self.current: self.resistance = self.voltage / self.current self.resistanceReason = "Ohm's Law" + self.writeReasoning(f'{self.name} has a voltage of {round(self.voltage, 5)} Volts and a current of {round(self.current, 5)} Amps, so its resistance is {round(self.resistance, 5)} Ohms. (Ohm\'s Law)') elif self.voltage and self.resistance: self.current = self.voltage / self.resistance self.currentReason = "Ohm's Law" + self.writeReasoning(f'{self.name} has a voltage of {round(self.voltage, 5)} Volts, and a resistance of {round(self.resistance, 5)} Ohms, so its current is {round(self.current, 5)} Amps. (Ohm\'s Law)') else: self.voltage = self.current * self.resistance self.voltageReason = "Ohm's Law" + self.writeReasoning(f'{self.name} has a current of {round(self.current, 5)} Amps and a resistance of {round(self.resistance, 5)} Ohms, so its voltage is {round(self.voltage, 5)} Volts. (Ohm\'s Law)') return True return False - def __repr__(self, pretty = False): - return self.__str__(pretty = pretty) + def __repr__(self, pretty = False, showVoltage = True, showCurrent = True, showResistance = True): + return self.__str__(pretty = pretty, showVoltage = showVoltage, showCurrent = showCurrent, showResistance = showResistance) - def __str__(self, pretty = True): + def __str__(self, pretty = True, showVoltage = True, showCurrent = True, showResistance = True): if pretty: - return f'{self.name}:\nVoltage: {round(self.voltage, 5)} Volts ({self.voltageReason})\nCurrent: {round(self.current, 5)} Amps ({self.currentReason})\nResistance: {round(self.resistance, 5)} Ohms ({self.resistanceReason})' + props = [f'Voltage: {round(self.voltage, 5)} Volts ({self.voltageReason})', f'Current: {round(self.current, 5)} Amps ({self.currentReason})', f'Resistance: {round(self.resistance, 5)} Ohms ({self.resistanceReason})'] + props = [p for i, p in enumerate(props) if [showVoltage, showCurrent, showResistance][i]] + return f'{self.name}:\n' + '\n'.join(props) else: - return f'Resistor({self.name}, [{self.voltage}-{self.voltageReason}, {self.current}-{self.currentReason}, {self.resistance}-{self.resistanceReason}])' + props = [f'{self.voltage}-{self.voltageReason}', f'{self.current}-{self.currentReason}', f'{self.resistance}-{self.resistanceReason}'] + props = [p for i, p in enumerate(props) if [showVoltage, showCurrent, showResistance][i]] + if props: + return f'Resistor({self.name}, {props})' + else: + return f'Resistor({self.name})' class Leg(Resistor): - def __init__(self, name, circuitType, subcomponents, voltage = None, current = None, resistance = None): - super().__init__(name, voltage = voltage, current = current, resistance = resistance, allowBypass = True) + def __init__(self, name, writeReasoning, circuitType, subcomponents, voltage = None, current = None, resistance = None): + super().__init__(name, writeReasoning, voltage = voltage, current = current, resistance = resistance, allowBypass = True) self.circuitType = circuitType if len(subcomponents) == 0: raise Exception(f"Leg {self.name} must have at least 1 subresistor.") @@ -84,10 +95,12 @@ def equalityRules(self): if not self.current: self.current = equalityCurrent self.currentReason = 'Series Current Equality' + self.writeReasoning(f'{self.name} has a current of {round(self.current, 5)} Amps because all subcomponents have the same current. (Series Current Equality)') for s in self.subcomponents: if not s.current: s.current = equalityCurrent s.currentReason = 'Series Current Equality' + s.writeReasoning(f'{s.name} has a current of {round(s.current, 5)} Amps because all subcomponents have the same current. (Series Current Equality)') return True else: testSum = len([True for s in self.subcomponents if s.voltage]) + (1 if self.voltage else 0) @@ -96,10 +109,12 @@ def equalityRules(self): if not self.voltage: self.voltage = equalityVoltage self.voltageReason = 'Parallel Voltage Equality' + self.writeReasoning(f'{self.name} has a voltage of {round(self.voltage, 5)} Volts because all subcomponents have the same voltage. (Parallel Voltage Equality)') for s in self.subcomponents: if not s.voltage: s.voltage = equalityVoltage s.voltageReason = 'Parallel Voltage Equality' + s.writeReasoning(f'{s.name} has a voltage of {round(s.voltage, 5)} Volts because all subcomponents have the same voltage. (Parallel Voltage Equality)') return True return False @@ -110,6 +125,7 @@ def sumRules(self): if not self.voltage: #Voltage for leg not provided. Add all subvoltages. self.voltage = sum([s.voltage for s in self.subcomponents]) self.voltageReason = 'Series Voltage Sum' + self.writeReasoning(f'{self.name} has a voltage of {round(self.voltage, 5)} Volts because it is equal to the sum of the voltages of all subcomponents {", ".join([s.name+" ("+str(round(s.voltage, 5))+" Volts)" for s in self.subcomponents])}. (Series Voltage Sum)') else: subcomponentVoltageSum = 0 targetSubcomponent = None @@ -120,12 +136,14 @@ def sumRules(self): targetSubcomponent = s targetSubcomponent.voltage = self.voltage - subcomponentVoltageSum targetSubcomponent.voltageReason = 'Series Voltage Sum' + self.writeReasoning(f'{targetSubcomponent.name} has a voltage of {round(targetSubcomponent.voltage, 5)} Volts because it is equal to the difference between the voltage of {self.name} ({round(self.voltage, 5)} Volts) and the sum of the voltages of all other subcomponents ({", ".join([s.name for s in self.subcomponents if s != targetSubcomponent])}) ({round(subcomponentVoltageSum, 5)} Volts). (Series Voltage Sum)') return True testResistanceSum = len([True for s in self.subcomponents if s.resistance]) + (1 if self.resistance else 0) if testResistanceSum == len(self.subcomponents): if not self.resistance: #Resistance for leg not provided. Add all subresistances. self.resistance = sum([s.resistance for s in self.subcomponents]) self.resistanceReason = 'Series Resistance Sum' + self.writeReasoning(f'{self.name} has a resistance of {round(self.resistance, 5)} Ohms because it is equal to the sum of the resistances of all subcomponents {", ".join([s.name+" ("+str(round(s.resistance, 5))+" Ohms)" for s in self.subcomponents])}. (Series Resistance Sum)') else: subcomponentResistanceSum = 0 targetSubcomponent = None @@ -136,6 +154,7 @@ def sumRules(self): targetSubcomponent = s targetSubcomponent.resistance = self.resistance - subcomponentResistanceSum targetSubcomponent.resistanceReason = 'Series Resistance Sum' + self.writeReasoning(f'{targetSubcomponent.name} has a resistance of {round(targetSubcomponent.resistance, 5)} Ohms because it is equal to the difference between the resistance of {self.name} ({round(self.resistance, 5)} Ohms) and the sum of the resistances of all other subcomponents ({", ".join([s.name for s in self.subcomponents if s != targetSubcomponent])}) ({round(subcomponentResistanceSum, 5)} Ohms). (Series Resistance Sum)') return True else: testCurrentSum = len([True for s in self.subcomponents if s.current]) + (1 if self.current else 0) @@ -143,6 +162,7 @@ def sumRules(self): if not self.current: #Current for leg not provided. Add all subcurrents. self.current = sum([s.current for s in self.subcomponents]) self.currentReason = 'Parallel Current Sum' + self.writeReasoning(f'{self.name} has a current of {round(self.current, 5)} Amps because it is equal to the sum of the currents of all subcomponents {", ".join([s.name+" ("+str(round(s.current, 5))+" Amps)" for s in self.subcomponents])}. (Parallel Current Sum)') else: subcomponentCurrentSum = 0 targetSubcomponent = None @@ -153,12 +173,14 @@ def sumRules(self): targetSubcomponent = s targetSubcomponent.current = self.current - subcomponentCurrentSum targetSubcomponent.currentReason = 'Parallel Current Sum' + self.writeReasoning(f'{targetSubcomponent.name} has a current of {round(targetSubcomponent.current, 5)} Amps because it is equal to the difference between the current of {self.name} ({round(self.current, 5)} Amps) and the sum of the currents of all other subcomponents ({", ".join([s.name for s in self.subcomponents if s != targetSubcomponent])}) ({round(subcomponentCurrentSum, 5)} Amps). (Parallel Current Sum)') return True testResistanceSum = len([True for s in self.subcomponents if s.resistance]) + (1 if self.resistance else 0) if testResistanceSum == len(self.subcomponents): if not self.resistance: #Resistance for leg not provided. Add all subresistances using 1/r formula. self.resistance = 1/sum([(1/s.resistance) for s in self.subcomponents]) self.resistanceReason = 'Parallel Resistance Sum' + self.writeReasoning(f'{self.name} has a resistance of {round(self.resistance, 5)} Ohms because it is equal to the reciprocal of the sum of the reciprocals of the resistances of all subcomponents {", ".join([s.name+" ("+str(round(s.resistance, 5))+" Ohms)" for s in self.subcomponents])}. (Parallel Resistance Sum)') else: subcomponentResistanceSum = 0 targetSubcomponent = None @@ -169,14 +191,19 @@ def sumRules(self): targetSubcomponent = s targetSubcomponent.resistance = 1/((1/self.resistance) - subcomponentResistanceSum) targetSubcomponent.resistanceReason = 'Parallel Resistance Sum' + self.writeReasoning(f'{targetSubcomponent.name} has a resistance of {round(targetSubcomponent.resistance, 5)} Ohms because it is equal to the reciprocal of the difference between the reciprocal of the resistance of {self.name} ({round(self.resistance, 5)} Ohms) and the sum of the reciprocals of the resistances of all other subcomponents ({", ".join([s.name for s in self.subcomponents if s != targetSubcomponent])}) ({round(subcomponentResistanceSum, 5)} Ohms). (Parallel Resistance Sum)') return True return False - def __repr__(self, pretty = False): - return self.__str__(pretty=pretty) - - def __str__(self, pretty = True): + def __str__(self, pretty = True, showVoltage = True, showCurrent = True, showResistance = True): if pretty: - return f'{self.name}:\nVoltage: {round(self.voltage, 5)} Volts ({self.voltageReason})\nCurrent: {round(self.current, 5)} Amps ({self.currentReason})\nResistance: {round(self.resistance, 5)} Ohms ({self.resistanceReason})' + props = [f'Voltage: {round(self.voltage, 5)} Volts ({self.voltageReason})', f'Current: {round(self.current, 5)} Amps ({self.currentReason})', f'Resistance: {round(self.resistance, 5)} Ohms ({self.resistanceReason})'] + props = [p for i, p in enumerate(props) if [showVoltage, showCurrent, showResistance][i]] + return f'{self.name}:\n' + '\n'.join(props) else: - return f'Leg({self.name}, {self.circuitType}, {self.subcomponents}, [{self.voltage}-{self.voltageReason}, {self.current}-{self.currentReason}, {self.resistance}-{self.resistanceReason}])' \ No newline at end of file + props = [f'{self.voltage}-{self.voltageReason}', f'{self.current}-{self.currentReason}', f'{self.resistance}-{self.resistanceReason}'] + props = [p for i, p in enumerate(props) if [showVoltage, showCurrent, showResistance][i]] + if props: + return f'Leg({self.name}, {self.circuitType}, {[s.name for s in self.subcomponents]}, {props})' + else: + return f'Leg({self.name}, {self.circuitType}, {[s.name for s in self.subcomponents]})' \ No newline at end of file diff --git a/main.py b/main.py index e51156d..701f96c 100644 --- a/main.py +++ b/main.py @@ -2,4 +2,6 @@ circuitSolver = CircuitSolver.compileCircuitFromFile('circuit.crc') circuitSolver.solve() -circuitSolver.showCircuit() \ No newline at end of file +circuitSolver.showStepByStepReasoning(showVoltageSteps=False, showCurrentSteps=False, showResistanceSteps=True) +print('\n\n') +circuitSolver.showCircuit(showVoltage=False, showCurrent=False, showResistance=True, showLegs=True, showResistors=False) \ No newline at end of file